billion-context-dsh 0.2.3 → 0.2.4
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 +3 -3
- package/README.md +3 -3
- package/dist/index.js +60 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.2.
|
|
6
|
+
> This project (**v0.2.4**) 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 ⭐:
|
|
@@ -129,7 +129,7 @@ DSH derives every model request from its append-only session log (the *surface*)
|
|
|
129
129
|
| refs (`m00001` tags) | surface seqs, carried by the nudge's compressible-range table |
|
|
130
130
|
| nudge ("efficiency note — compress early and keep context lean") | injected at `agent/pre-step` by the kernel's pressure decision — efficiency note + context breakdown + compression rules, tone aligned with kernel/pi; never an order |
|
|
131
131
|
| `decompress` | read-only recovery of shadowed originals from the log |
|
|
132
|
-
| `search_context` | scores block summaries + originals rebuilt from the log |
|
|
132
|
+
| `search_context` | scores a unified doc set (block summaries + shadowed originals) rebuilt from the log via acp-kernel `searchBlocks` (hybrid: stemming + CJK bigrams + char n-gram fuzzy); hits link back to the owning block |
|
|
133
133
|
| `acp_status` | CONTEXT BREAKDOWN (tool/text/summaries shares of the visible total) + compressed-block ledger + nudge decision line; no context-window rows; scope/view/tool/sort/limit drilldown supported |
|
|
134
134
|
| block state | in-memory kernel state + **log-rebuilt ledger** (no sidecar files) |
|
|
135
135
|
| tiered distillation (T2/T3) | re-compressing a block's summary node distills that block (tier 2); distilling a tier-2 block yields tier 3. Tier + kernel block ids are persisted to the log, so kernel state rehydrates from the log after a restart and stays distillable |
|
|
@@ -148,7 +148,7 @@ A walkthrough of the ACP philosophy this project inherits — how active context
|
|
|
148
148
|
| --- | --- |
|
|
149
149
|
| `compress` | Replace a seq range with a dense summary you write (edges auto-balanced to tool-pair boundaries); re-compressing a block's summary node distills it (tier 2/3) |
|
|
150
150
|
| `decompress` | Restore a previously compressed block's original content (read-only); accepts the `bN` ref shown by acp_status or a compaction id |
|
|
151
|
-
| `search_context` | Search compressed block summaries and originals by keyword |
|
|
151
|
+
| `search_context` | Search compressed block summaries and originals by keyword (acp-kernel hybrid retrieval: stemming + CJK bigrams + fuzzy); hits link back to the owning block |
|
|
152
152
|
| `acp_status` | CONTEXT BREAKDOWN (tool/text/summaries shares of the visible total) + compressed-block ledger + nudge decision line; no context-window rows. Drilldown supported: `scope:"compressed"` per block, `scope:"uncompressed"` + `view:"messages"`/`"ranges"` per message/range, with `tool` filter, `sort` order and `limit` cap. Drilldown row refs are kernel ids (mN, size awareness only) — compress always uses the `Surface:` seqs |
|
|
153
153
|
| `/acp` | status / compress / decompress from the command bar |
|
|
154
154
|
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[中文](./README.md) | [English](./README.en.md)
|
|
4
4
|
|
|
5
5
|
> **⚠️ 测试版声明——请勿用于生产环境**
|
|
6
|
-
> 本项目(**v0.2.
|
|
6
|
+
> 本项目(**v0.2.4**)仍处于开发中的测试版。[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 本身也处于**公开测试版**阶段。**请勿将两者用于工程化 / 生产环境**——预期会有破坏性变更与粗糙之处。
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
9
|
<strong>衷心感谢以下项目——请给它们一个 ⭐:</strong>
|
|
@@ -130,7 +130,7 @@ DSH 的每个模型请求都派生自其 append-only 会话日志(*surface*)
|
|
|
130
130
|
| refs(`m00001` 标签) | surface seq,由 nudge 的可压缩范围表携带 |
|
|
131
131
|
| nudge("效率提示——尽早压缩保持精简") | 由内核的压力决策在 `agent/pre-step` 注入——效率通知 + 上下文分解 + 压缩规则,语气对齐 kernel/pi;绝非命令 |
|
|
132
132
|
| `decompress` | 从日志只读恢复被遮蔽的原文 |
|
|
133
|
-
| `search_context` |
|
|
133
|
+
| `search_context` | 从日志重建块摘要 + 被遮蔽原文的统一文档集,交 acp-kernel `searchBlocks`(hybrid:词干化 + CJK bigram + 字符 n-gram 模糊)打分;命中回链所属块 |
|
|
134
134
|
| `acp_status` | CONTEXT BREAKDOWN(tool/text/summaries 占可见总量)+ 压缩块账本 + nudge 决策行;不含上下文窗口;支持 scope/view/tool/sort/limit 钻取 |
|
|
135
135
|
| 块状态 | 内存内核状态 + **日志重建账本**(无旁车文件) |
|
|
136
136
|
| 分层蒸馏(T2/T3) | 再次压缩某块的摘要节点 = 蒸馏该块(tier 2),蒸馏 tier-2 块得 tier 3;tier 与内核块 id 持久化进日志,重启后内核状态从日志再水合、可继续蒸馏 |
|
|
@@ -149,7 +149,7 @@ DSH 的每个模型请求都派生自其 append-only 会话日志(*surface*)
|
|
|
149
149
|
| --- | --- |
|
|
150
150
|
| `compress` | 用你书写的紧凑摘要替换 seq 范围(边界自动平衡到 tool-call/result 配对点);对某块的摘要节点再次压缩 = 分层蒸馏(tier 2/3) |
|
|
151
151
|
| `decompress` | 恢复已压缩块的原始内容(只读);接受 acp_status 显示的 `bN` 或 compaction id |
|
|
152
|
-
| `search_context` |
|
|
152
|
+
| `search_context` | 按关键词搜索压缩块摘要与原文(acp-kernel hybrid 检索:词干化 + CJK bigram + 模糊);命中回链所属块 |
|
|
153
153
|
| `acp_status` | CONTEXT BREAKDOWN(tool/text/summaries 占可见总量)+ 压缩块账本 + nudge 决策行;不含上下文窗口。支持钻取:`scope:"compressed"` 逐块、`scope:"uncompressed"` + `view:"messages"`/`"ranges"` 逐消息/区间,`tool` 过滤、`sort` 排序、`limit` 截断。钻取行 ref 是内核 mN(仅供体量感知)——压缩始终用 `Surface:` 的 seq |
|
|
154
154
|
| `/acp` | 从命令栏执行 status / compress / decompress |
|
|
155
155
|
|
package/dist/index.js
CHANGED
|
@@ -781,7 +781,7 @@ var AcpStateStore = class {
|
|
|
781
781
|
|
|
782
782
|
// src/tools.ts
|
|
783
783
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
784
|
-
import { buildStatusReport, defaultCountTokens as defaultCountTokens3 } from "acp-kernel";
|
|
784
|
+
import { buildStatusReport, defaultCountTokens as defaultCountTokens3, searchBlocks } from "acp-kernel";
|
|
785
785
|
|
|
786
786
|
// src/config.ts
|
|
787
787
|
import { defaultConfig } from "acp-kernel";
|
|
@@ -1383,26 +1383,70 @@ var searchParameters = {
|
|
|
1383
1383
|
query: { type: "string", required: true, description: "Search terms to find inside compressed blocks." },
|
|
1384
1384
|
limit: { type: "integer", description: "Maximum results (default 5)." }
|
|
1385
1385
|
};
|
|
1386
|
+
function roleOfEvent(event) {
|
|
1387
|
+
switch (event.type) {
|
|
1388
|
+
case "user/message":
|
|
1389
|
+
return "user";
|
|
1390
|
+
case "assistant/message":
|
|
1391
|
+
return "assistant";
|
|
1392
|
+
case "tool/result":
|
|
1393
|
+
return "tool";
|
|
1394
|
+
default:
|
|
1395
|
+
return null;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
function buildSearchDocs(session) {
|
|
1399
|
+
const ledger = rebuildBlockLedger(session.events);
|
|
1400
|
+
const docs = [];
|
|
1401
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
1402
|
+
for (const block of ledger) {
|
|
1403
|
+
docs.push({
|
|
1404
|
+
kind: "block",
|
|
1405
|
+
ref: block.blockId,
|
|
1406
|
+
text: block.summary,
|
|
1407
|
+
title: block.summary.slice(0, 60) || block.blockId,
|
|
1408
|
+
blockId: block.blockId,
|
|
1409
|
+
tier: block.tier,
|
|
1410
|
+
tokens: defaultCountTokens3(block.summary)
|
|
1411
|
+
});
|
|
1412
|
+
for (const seq of expandShadowedSeqs(session, block.blockId)) {
|
|
1413
|
+
if (claimed.has(seq)) continue;
|
|
1414
|
+
claimed.add(seq);
|
|
1415
|
+
const event = session.events[seq];
|
|
1416
|
+
if (event === void 0) continue;
|
|
1417
|
+
const role = roleOfEvent(event);
|
|
1418
|
+
const text = extractEventText(event);
|
|
1419
|
+
if (role === null || text.length === 0) continue;
|
|
1420
|
+
docs.push({
|
|
1421
|
+
kind: "message",
|
|
1422
|
+
ref: `seq ${seq}`,
|
|
1423
|
+
text,
|
|
1424
|
+
title: `${role}: ${text.slice(0, 60)}`,
|
|
1425
|
+
role,
|
|
1426
|
+
blockId: block.blockId,
|
|
1427
|
+
tier: block.tier,
|
|
1428
|
+
tokens: defaultCountTokens3(text)
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
return docs;
|
|
1433
|
+
}
|
|
1386
1434
|
function handleSearch(_env, rawArgs, exec) {
|
|
1387
1435
|
const args = unwrapEnvelope(rawArgs);
|
|
1388
1436
|
const session = requireAgent(exec).session;
|
|
1389
|
-
|
|
1390
|
-
const
|
|
1391
|
-
const
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
const
|
|
1395
|
-
${
|
|
1396
|
-
|
|
1397
|
-
for (const term of terms) score += haystack.split(term).length - 1;
|
|
1398
|
-
if (score > 0) scored.push({ blockId: block.blockId, score, summary: block.summary });
|
|
1399
|
-
}
|
|
1400
|
-
scored.sort((a, b) => b.score - a.score);
|
|
1401
|
-
const top = scored.slice(0, args.limit ?? 5);
|
|
1402
|
-
if (top.length === 0) return { text: `search_context: no matches for "${args.query}"` };
|
|
1437
|
+
if (args.query.trim() === "") return { text: "search_context: empty query (no matches)" };
|
|
1438
|
+
const docs = buildSearchDocs(session);
|
|
1439
|
+
const results = searchBlocks(docs, args.query, { limit: args.limit ?? 5, previewLength: 160 });
|
|
1440
|
+
if (results.length === 0) return { text: `search_context: no matches for "${args.query}"` };
|
|
1441
|
+
const lines = results.map((r) => {
|
|
1442
|
+
const kind = r.kind === "block" ? `block ${r.ref}` : `message ${r.ref} (${r.role ?? "?"}, in block ${r.blockId ?? "?"})`;
|
|
1443
|
+
return ` - ${kind} (score ${r.score.toFixed(2)}): ${r.preview}`;
|
|
1444
|
+
});
|
|
1403
1445
|
return {
|
|
1404
1446
|
text: `Matches for "${args.query}":
|
|
1405
|
-
|
|
1447
|
+
${lines.join("\n")}
|
|
1448
|
+
|
|
1449
|
+
Decompress with: decompress({ blockId })`
|
|
1406
1450
|
};
|
|
1407
1451
|
}
|
|
1408
1452
|
var statusParameters = {
|
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/nudge.ts","../src/prompts.ts","../src/commands.ts","../src/window.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_ORDER } from './system-prompt.ts'\nimport { renderSystemPrompt, resolvePrompts, type AcpPrompts, type ResolvedPrompts } from './prompts.ts'\nimport { DEFAULT_CONTEXT_WINDOW, detectContextWindow, type AcpWindow } from './window.ts'\nimport { deferCompressPairHide, stripOrphanedSurfaceToolMessages } from './region.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 {\n DEFAULT_PROMPTS,\n DEFAULT_RESOLVED,\n renderSystemPrompt,\n renderTemplate,\n resolvePrompts,\n type AcpPrompts,\n type NudgePrompts,\n type PromptInput,\n type PromptOverride,\n type RangeTablePrompts,\n type ResolvedPrompts,\n type ToolPrompts,\n} from './prompts.ts'\nexport { makeTools, type ToolEnvironment } from './tools.ts'\nexport { acpCommand } from './commands.ts'\nexport { buildNudge, resolveTokenCount, 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 AlreadyCompressedRangeError,\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n findOpenTurn,\n assertNoActiveCompaction,\n blockRegistry,\n blockRefForSummarySeq,\n compactionIdsOfKernelBlocks,\n summarySeqOfKernelBlock,\n expandShadowedSeqs,\n hideCompressToolPair,\n stripOrphanedSurfaceToolMessages,\n type AcpBlockLedgerEntry,\n type CompactionTransactionInput,\n type ResolvedSurfaceRange,\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 /**\n * Custom token-count function for the kernel's internal estimation.\n * Defaults to the kernel's `defaultCountTokens` (CJK: 1 char = 1 token,\n * other: 4 chars = 1 token — aligns with billion-context-pi).\n * Can be overridden for provider-specific tokenization, e.g. DeepSeek's\n * official coefficient: 1 CJK char ≈ 0.6 tokens, 1 other char ≈ 0.3 tokens.\n * Only affects the kernel's internal estimation (compressible range sizing,\n * nudge text, growth branch pending); the `projectedTokens` reading from\n * `sessionProjections` (used for nudge pressure decisions and acp_status)\n * is provider-anchored and unaffected by this function.\n */\n readonly countTokens?: (text: string) => number\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 /** Per-stage prompt template overrides (nudge / range table / system prompt / tool descriptions). See docs/configurable-prompts-design.md. */\n readonly prompts?: AcpPrompts\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 /** Resolved prompt templates (validated at construction — fail-fast on template typos). */\n readonly prompts: ResolvedPrompts\n\n private readonly lastNudgeTurn = new Map<string, number>()\n /** Successful compress call ids awaiting their tool/result so the pair can be hidden. */\n private readonly compressCallIdsToHide = new Set<string>()\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 // Resolve + validate prompt templates BEFORE building env: a template typo\n // must fail engine construction, never silently leak into model context.\n this.prompts = resolvePrompts(config.prompts)\n const ports = this.config.countTokens !== undefined ? { countTokens: this.config.countTokens } : {}\n this.kernel = createCore(ports)\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 prompts: this.prompts,\n compressCallIdsToHide: this.compressCallIdsToHide,\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 // After a successful compress tool result is appended, hide its\n // call/result pair. The durable summary node was inserted mid-turn (before\n // the result), so leaving the pair visible would put a user message between\n // an assistant tool_calls block and its tool response — strict providers\n // reject that request with HTTP 400 (issue #18).\n ctx.on('session/event', (session, event) => {\n if (event.type !== 'tool/result') return\n const message = event.data.message\n const block = message.content[0]\n const callId = block?.toolCallId ?? message.source.callId\n if (typeof callId !== 'string' || !this.compressCallIdsToHide.has(callId)) return\n this.compressCallIdsToHide.delete(callId)\n // session.append is NOT reentrant: calling it synchronously inside this\n // session/event dispatch (the outer append still holds the reentry lock)\n // throws \"session append cannot reenter while another append is being\n // published\" on live, store-attached sessions, and the dispatcher\n // silently swallows the error — the hide would be a no-op. Defer it to a\n // microtask: microtasks drain after the append fully publishes and\n // before the agent loop resumes, so the pair is hidden before the next\n // request is built.\n deferCompressPairHide(session, callId, event.seq, (error) => {\n ctx.logger.warn(`billion-context-dsh: hide compress call/result pair failed: ${String(error)}`)\n })\n })\n ctx.on('agent/pre-step', async (payload, next) => {\n // A crash-interrupted tool leaves an orphan call/result on the surface:\n // it corrupts the pairing balance cache AND can 400 the next request\n // (strict providers reject tool messages without their call/response).\n // Clean them before EVERY step — not only when a nudge fires — so a\n // low-pressure session never hits the orphan 400 (issue #18). No call is\n // in flight at pre-step (the previous step's tools all landed), so the\n // default empty in-flight set is safe.\n stripOrphanedSurfaceToolMessages(payload.agent.session)\n if (!this.config.autoNudge) return 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 // The load-bearing ACP guidance lives in the system prompt ONCE; nudges\n // stay short and advisory (model-driven: the model decides). The\n // systemPrompt service may not be registered yet on cold start (cordis\n // starts unrelated composition rows concurrently), so apply the same\n // retry pattern as tools and commands: eager registration, then\n // re-attempt when the service appears via `internal/service`; guard so a\n // late callback never double-registers.\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: renderSystemPrompt(this.prompts),\n })\n } else {\n let done = false\n const registerSystemPrompt = (): void => {\n if (done) return\n const registry = ctx.get('systemPrompt')\n if (registry === undefined) return\n done = true\n registry.section({\n name: 'billion-context-dsh',\n order: ACP_SYSTEM_PROMPT_ORDER,\n text: renderSystemPrompt(this.prompts),\n })\n }\n ctx.on('internal/service', (name: unknown) => {\n if (name === 'systemPrompt') registerSystemPrompt()\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 { createAssistantMessage, createUserMessage, type ContentBlock } from '@deepseek-ai/dsh-llm'\nimport { defaultCountTokens } from 'acp-kernel'\nimport { extractEventText, extractText, toolCallIdOfResultEvent } 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 * A requested range whose EVERY live message was already shadowed by one or\n * more blocks. The compress tool catches this and reports the range as already\n * compressed (with the covering block ids) instead of folding block summary\n * nodes as plain messages or erroring out. Distillation stays an explicit act:\n * target a LIVE checkpoint seq directly to distill (tier 2/3).\n */\nexport class AlreadyCompressedRangeError extends Error {\n constructor(\n readonly start: number,\n readonly end: number,\n readonly coveringBlockIds: readonly string[],\n ) {\n super(\n `billion-context-dsh: seq ${start}..${end} already compressed — `\n + 'no live content remains in that span',\n )\n this.name = 'AlreadyCompressedRangeError'\n }\n}\n\ntype StaleRangeRecovery =\n | { kind: 'ok'; start: number; end: number }\n | { kind: 'already-compressed'; coveringBlockIds: string[] }\n | { kind: 'unresolvable'; failedEdge: number }\n\n/**\n * Rebuild a requested range whose edges are no longer on the current surface.\n * The dominant cause is staleness: the seqs came from an older nudge table or\n * a previous compress result, and an earlier compression SHADOWED them (they\n * stay in the append-only log, but are gone from the surface). The recovery:\n *\n * 1. An edge that does not exist in the log at all (invented, or from another\n * session) is unresolvable — there is no way to guess what it meant.\n * 2. The still-LIVE surface nodes inside the requested span, in VALUE order\n * (the surface can be locally non-monotonic after replacements, so value\n * order is the only coherent span). If there are none, the whole span was\n * already compressed → 'already-compressed' with the covering block ids.\n * 3. Otherwise the range snaps to the first..last live PLAIN node in the\n * span. Block checkpoint nodes are deliberately excluded: distilling a\n * block on a STALE reference would silently change block structure the\n * model never intended to touch — distillation requires targeting a live\n * checkpoint seq directly.\n */\nfunction recoverStaleRange(session: Session, start: number, end: number): StaleRangeRecovery {\n if (session.events[start] === undefined || session.events[end] === undefined) {\n const failedEdge = session.events[start] === undefined ? start : end\n return { kind: 'unresolvable', failedEdge }\n }\n const liveInside = session.surface.nodes\n .filter((seq) => seq >= start && seq <= end)\n .sort((a, b) => a - b)\n const plain = liveInside.filter((seq) => !isCheckpointNode(session.events[seq]!))\n if (plain.length === 0) {\n const coveringBlockIds = rebuildBlockLedger(session.events)\n .filter((entry) => entry.shadowedSeqs.some((seq) => seq >= start && seq <= end))\n .map((entry) => entry.blockId)\n return { kind: 'already-compressed', coveringBlockIds }\n }\n return { kind: 'ok', start: plain[0]!, end: plain[plain.length - 1]! }\n}\n\nexport interface ResolvedSurfaceRange {\n readonly start: number\n readonly end: number\n /**\n * True when the requested edges were not on the current surface and were\n * remapped to the still-live content of the requested span (an earlier\n * compression shadowed them). Callers surface this so the model sees what\n * was actually compressed instead of silently shadowing a different span.\n */\n readonly recovered?: boolean\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. Reversed\n * ranges throw. An edge that sits inside a tool-call/result pair — or on a\n * multi-tool-call assistant message that has no bare-seq ref — is first nudged\n * inward to the nearest clean cut; if that collapses the range (e.g. the model\n * asked for a SINGLE tool result, which can never be balanced alone), the\n * range EXPANDS outward to the enclosing clean pair instead — a lone tool\n * message is almost always a \"consumed output\" the model genuinely wants to\n * compress. The returned range is what a caller should actually shadow.\n *\n * Missing edges are NOT an immediate error: the seqs were probably shadowed by\n * an earlier compression (stale nudge table / old compress result). The span\n * is rebuilt from its still-live remainder via recoverStaleRange — a fully\n * shadowed span throws AlreadyCompressedRangeError, a genuinely unknown edge\n * throws the not-in-surface guidance error. The returned range is what a\n * caller should actually shadow.\n */\nexport function resolveSurfaceRange(\n session: Session,\n start: number,\n end: number,\n): ResolvedSurfaceRange {\n const nodes = session.surface.nodes\n if (start > end) {\n throw new Error(`billion-context-dsh: reversed range ${start}..${end}`)\n }\n let requestedStartIdx = nodes.indexOf(start)\n let requestedEndIdx = nodes.indexOf(end)\n let recovered = false\n if (requestedStartIdx < 0 || requestedEndIdx < 0) {\n const stale = recoverStaleRange(session, start, end)\n if (stale.kind === 'unresolvable') {\n throw new Error(\n `billion-context-dsh: seq ${start}..${end} not in the current surface — `\n + `edge seq ${stale.failedEdge} is not in this session's log. `\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 (stale.kind === 'already-compressed') {\n throw new AlreadyCompressedRangeError(start, end, stale.coveringBlockIds)\n }\n start = stale.start\n end = stale.end\n recovered = true\n requestedStartIdx = nodes.indexOf(start)\n requestedEndIdx = nodes.indexOf(end)\n if (requestedStartIdx < 0 || requestedEndIdx < 0) {\n // Unreachable in practice (recovery returns live nodes), but never let\n // a negative index reach the balancing passes.\n throw new Error(\n `billion-context-dsh: seq ${start}..${end} not in the current surface — `\n + 'consult acp_status for the current surface range',\n )\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 recovered\n ? { start: nodes[startIdx]!, end: nodes[endIdx]!, recovered: true }\n : { start: nodes[startIdx]!, end: nodes[endIdx]! }\n }\n // A recovered span NEVER expands across block checkpoints: the model's\n // requested edges were stale, so growing the span into block territory could\n // fold content it never intended to touch. If the live remainder cannot be\n // balanced by shrinking alone, give up with guidance instead.\n if (recovered) {\n throw new Error(\n `billion-context-dsh: no tool-pairing-balanced live remainder around seq ${start}..${end} — `\n + 'narrow the range or consult acp_status for the current surface',\n )\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/** Tool-call ids carried by one assistant surface message. */\nfunction toolCallIdsOfEvent(event: SessionEvent): string[] {\n if (event.type !== 'assistant/message') return []\n const content = (event.data as { message?: { content?: unknown } }).message?.content\n if (!Array.isArray(content)) return []\n const ids: string[] = []\n for (const block of content) {\n if (block === null || typeof block !== 'object') continue\n const b = block as { type?: unknown; id?: unknown }\n if (b.type === 'tool-call' && typeof b.id === 'string') ids.push(b.id)\n }\n return ids\n}\n\n/**\n * Provider/model to stamp on a synthetic empty assistant pruning node.\n */\nfunction assistantProviderModel(event: SessionEvent): { provider: string; model: string } {\n if (event.type === 'assistant/message') {\n const message = (event.data as { message?: { source?: { provider?: unknown; model?: unknown } } }).message\n return {\n provider: typeof message?.source?.provider === 'string' ? message.source.provider : 'billion-context-dsh',\n model: typeof message?.source?.model === 'string' ? message.source.model : 'surface-prune',\n }\n }\n return { provider: 'billion-context-dsh', model: 'surface-prune' }\n}\n\n/**\n * Durable model-free prune: append `compaction/prune` as the shadow price, then\n * replace the given surface seqs with either a user message carrying `text`\n * (used for compress call/result hiding, so the model still sees the tool\n * outcome) or an EMPTY assistant message (used for orphan cleanup, which DSH\n * derives to nothing). The originals remain in the append-only log.\n */\nfunction hideSurfaceSeqs(\n session: Session,\n seqs: readonly number[],\n provider: string,\n model: string,\n text?: string,\n): void {\n if (seqs.length === 0) return\n const start = seqs[0]!\n const end = seqs[seqs.length - 1]!\n let shadowedTokenCount = 0\n for (const seq of seqs) {\n const event = session.events[seq]\n if (event !== undefined) shadowedTokenCount += defaultCountTokens(extractEventText(event))\n }\n session.append('compaction/prune', {\n shadowedRange: { start, end },\n shadowedSeqs: [...seqs],\n shadowedTokenCount,\n })\n if (text !== undefined) {\n session.append('user/message', createUserMessage({\n content: [{ type: 'text', text }],\n source: { kind: 'plugin', plugin: 'billion-context-dsh' },\n }), {\n surfaceOp: { op: 'replace', start, end },\n sourceEventSeqs: [...seqs],\n })\n return\n }\n session.append('assistant/message', {\n turn: findOpenTurn(session.events) ?? 0,\n step: 0,\n message: createAssistantMessage({ content: [], source: { provider, model } }),\n }, {\n surfaceOp: { op: 'replace', start, end },\n sourceEventSeqs: [...seqs],\n })\n}\n\n/**\n * Hide one successful `compress` tool's call/result pair after its tool/result\n * has been logged. The durable compaction summary is inserted BEFORE the\n * current tool result (the compress tool runs mid-turn), so leaving the pair on\n * the surface would produce `assistant(tool_calls) → user(summary) →\n * tool(result)` — rejected by strict providers. Replacing both nodes with a\n * plain user message (the result text) removes the pair from the derived\n * surface without touching the compaction block.\n */\nexport function hideCompressToolPair(session: Session, callId: string, resultSeq?: number): boolean {\n let callSeq: number | null = null\n for (const event of session.events) {\n if (event.type !== 'assistant/message') continue\n if (toolCallIdsOfEvent(event).includes(callId)) {\n callSeq = event.seq\n break\n }\n }\n if (callSeq === null) return false\n // Only hide a node that carries EXACTLY the compress call. Hiding a\n // multi-call node replaces the whole assistant message, which would orphan\n // the sibling calls' results (their call ids vanish with the node).\n const callNodeIds = toolCallIdsOfEvent(session.events[callSeq]!)\n if (callNodeIds.length !== 1 || callNodeIds[0] !== callId) return false\n let resolvedResultSeq = resultSeq ?? null\n if (resolvedResultSeq === null) {\n for (const event of session.events) {\n if (event.type === 'tool/result' && toolCallIdOfResultEvent(event) === callId) {\n resolvedResultSeq = event.seq\n break\n }\n }\n }\n if (resolvedResultSeq === null) return false\n const nodes = session.surface.nodes\n const startIdx = nodes.indexOf(callSeq)\n const endIdx = nodes.indexOf(resolvedResultSeq)\n // Only hide an actually adjacent pair; never shadow unrelated messages that\n // happen to sit between a stale call and result.\n if (startIdx < 0 || endIdx < 0 || endIdx - startIdx !== 1) return false\n const { provider, model } = assistantProviderModel(session.events[callSeq]!)\n const resultEvent = session.events[resolvedResultSeq]\n const resultText = resultEvent === undefined ? '' : extractEventText(resultEvent)\n hideSurfaceSeqs(session, [callSeq, resolvedResultSeq], provider, model, resultText.trim().length > 0 ? resultText : undefined)\n return true\n}\n\n/**\n * Surface-level orphan cleanup: hide tool/result nodes with no matching call,\n * assistant tool-call nodes whose calls all lack results, and \"broken pairs\"\n * whose result is NOT adjacent to the call node on the surface (a\n * non-tool/result node — typically the compaction summary a buggy older\n * version inserted between a compress call and its result — sits between\n * them). A single orphan result corrupts the whole tool-pairing balance cache\n * (every range resolve throws), orphan calls fragment large ranges into tiny\n * uncompressed fragments, and a broken pair cannot serialize for strict\n * providers — the mechanisms behind issue #18's \"only ~28 tokens visible\".\n * Uses the same durable prune protocol as `hideSurfaceSeqs`, so the removed\n * nodes stay recoverable from the append-only log.\n */\nexport function stripOrphanedSurfaceToolMessages(\n session: Session,\n inFlightCallIds: ReadonlySet<string> = new Set(),\n): number {\n const nodes = session.surface.nodes\n const callIdsBySeq = new Map<number, string[]>()\n // callId -> surface position of the assistant node carrying it, for calls\n // whose result has not been decided yet.\n const open = new Map<string, { seq: number; index: number }>()\n const orphanResultSeqs: number[] = []\n // result seq -> call node seq, for pairs whose result landed but is not\n // adjacent to the call node on the surface.\n const brokenResults = new Map<number, number>()\n for (let index = 0; index < nodes.length; index += 1) {\n const seq = nodes[index]!\n const event = session.events[seq]\n if (event === undefined) continue\n if (event.type === 'assistant/message') {\n const ids = toolCallIdsOfEvent(event)\n if (ids.length === 0) continue\n callIdsBySeq.set(seq, ids)\n for (const id of ids) {\n if (!open.has(id)) open.set(id, { seq, index })\n }\n } else if (event.type === 'tool/result') {\n const id = toolCallIdOfResultEvent(event)\n if (id === null) continue\n const call = open.get(id)\n if (call === undefined) {\n orphanResultSeqs.push(seq)\n continue\n }\n // A pair is healthy only when every node between the call and this\n // result is a tool/result of the SAME call node (multi-call messages).\n // Any other node in between makes the pair unserializable for strict\n // providers: prune both ends.\n const callNodeIds = callIdsBySeq.get(call.seq)\n let adjacent = false\n if (callNodeIds !== undefined) {\n adjacent = true\n for (let mid = call.index + 1; mid < index; mid += 1) {\n const midEvent = session.events[nodes[mid]!]\n if (midEvent === undefined || midEvent.type !== 'tool/result') {\n adjacent = false\n break\n }\n const midId = toolCallIdOfResultEvent(midEvent)\n if (midId === null || !callNodeIds.includes(midId)) {\n adjacent = false\n break\n }\n }\n }\n open.delete(id)\n if (!adjacent) brokenResults.set(seq, call.seq)\n }\n }\n // call node seq -> ids of that node whose result is broken (non-adjacent).\n const brokenIdsByCallSeq = new Map<number, string[]>()\n for (const [resultSeq, callSeq] of brokenResults) {\n const id = toolCallIdOfResultEvent(session.events[resultSeq]!)\n if (id !== null) {\n const list = brokenIdsByCallSeq.get(callSeq) ?? []\n list.push(id)\n brokenIdsByCallSeq.set(callSeq, list)\n }\n }\n const hiddenSet = new Set<number>(orphanResultSeqs)\n for (const resultSeq of brokenResults.keys()) hiddenSet.add(resultSeq)\n for (const [callSeq, ids] of callIdsBySeq) {\n const brokenIds = brokenIdsByCallSeq.get(callSeq)\n // Only hide an assistant node when NONE of its calls are usable: every id\n // must lack a result (open) or have a broken result. A mixed node (some\n // healthy results) must stay so its valid results are not orphaned by\n // hiding the call — and a node carrying an in-flight call can never be\n // pruned, or the pending result lands orphaned.\n const allUnpaired = !ids.some((candidate) => inFlightCallIds.has(candidate))\n && ids.every((candidate) => open.has(candidate) || brokenIds?.includes(candidate) === true)\n if (allUnpaired) hiddenSet.add(callSeq)\n }\n const hidden = [...hiddenSet].sort((a, b) => a - b)\n let count = 0\n for (const seq of hidden) {\n const event = session.events[seq]\n if (event === undefined) continue\n const { provider, model } = assistantProviderModel(event)\n hideSurfaceSeqs(session, [seq], provider, model)\n count += 1\n }\n return count\n}\n\n/**\n * All tool-call ids currently visible on the surface with no matching\n * tool/result yet — the in-flight calls of the current step. Sibling tools\n * called in the same assistant message as `compress` are in-flight too, so\n * `handleCompress` must protect the whole set (not just its own call id) or\n * the sibling call would be pruned as an orphan and its result would land\n * orphaned (HTTP 400 until the next cleanup).\n */\nexport function openToolCallIds(session: Session): Set<string> {\n const open = new Set<string>()\n for (const seq of session.surface.nodes) {\n const event = session.events[seq]\n if (event === undefined) continue\n if (event.type === 'assistant/message') {\n for (const id of toolCallIdsOfEvent(event)) open.add(id)\n } else if (event.type === 'tool/result') {\n const id = toolCallIdOfResultEvent(event)\n if (id !== null) open.delete(id)\n }\n }\n return open\n}\n\n/**\n * Schedule `hideCompressToolPair` on the microtask queue. `session.append`\n * is NOT reentrant: running it synchronously inside a `session/event`\n * listener (while the outer append is still publishing) throws \"session\n * append cannot reenter while another append is being published\" on live,\n * store-attached sessions, and the dispatcher silently swallows the error —\n * so a synchronous hide is a silent no-op in production. A microtask drains\n * after the current append fully publishes and before the agent loop resumes,\n * so the pair is hidden before the next request is built.\n */\nexport function deferCompressPairHide(\n session: Session,\n callId: string,\n resultSeq: number,\n onError?: (error: unknown) => void,\n): void {\n queueMicrotask(() => {\n try {\n hideCompressToolPair(session, callId, resultSeq)\n } catch (error) {\n onError?.(error)\n }\n })\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 // Orphan tool messages corrupt the pairing balance cache and fragment every\n // large span. Prune them before scanning so the range table reflects the\n // actually compressible surface (issue #18).\n stripOrphanedSurfaceToolMessages(session)\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 // Surface nodes are NOT guaranteed to be ordered: a compaction replace lands\n // the checkpoint node first, so [15, 6, 7, …]. Report the span as min..max\n // rather than first..last, which would read \"seqs 15..12\" after a compress.\n let first = nodes[0]!\n let last = nodes[0]!\n for (const seq of nodes) {\n if (seq < first) first = seq\n if (seq > last) last = seq\n }\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/**\n * Resolve a kernel block ref (`bN`) — as shown by the model tool `acp_status`\n * (kernel `buildStatusReport` renders `block.blockId`) — to the durable\n * compaction id the decompress/search tools accept. Returns null when `bN` is\n * not an exact registry key (unknown ref). Only matches the canonical `bN`\n * form (`/^b\\d+$/`); anything else is not a kernel ref and returns null so the\n * caller falls back to its compaction-id prefix match.\n */\nexport function blockIdOfKernelRef(session: Session, kernelRef: string): string | null {\n if (!/^b\\d+$/.test(kernelRef)) return null\n const entry = blockRegistry(session).find((r) => r.kernelBlockId === kernelRef)\n return entry?.blockId ?? null\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 * The tool-call id of one tool/result surface message, or null.\n *\n * Real DSH tool-result events carry NO `message.toolCallId` (hard-won rule\n * 10): the identity lives in the nested `{ type: 'tool-result', toolCallId }`\n * content block, falling back to `message.source.callId`. Shared with\n * `src/region.ts`'s call/result pairing — one implementation, never a copy.\n */\nexport function toolCallIdOfResultEvent(event: SessionEvent): string | null {\n if (event.type !== 'tool/result') return null\n const message = (event.data as {\n message?: { content?: Array<{ type?: unknown; toolCallId?: unknown }>; source?: { callId?: unknown } }\n }).message\n const block = Array.isArray(message?.content)\n ? message.content.find((candidate) => candidate?.type === 'tool-result')\n : undefined\n const id = block?.toolCallId ?? message?.source?.callId\n return typeof id === 'string' ? id : null\n}\n\n/**\n * Index of assistant tool-call `id` → tool `name`, used to attribute\n * tool/result messages to their tool. Real DSH tool-results carry no\n * `message.toolName` (rule 10), so the projection backfills it from the\n * matching assistant tool-call. Scans ALL events up front (order-independent:\n * a result may precede its call in the array) and covers shadowed calls too.\n */\nexport function buildToolCallIndex(events: readonly SessionEvent[]): ReadonlyMap<string, string> {\n const index = new Map<string, string>()\n for (const event of events) {\n if (event.type !== 'assistant/message') continue\n const content = (event.data as { message?: { content?: unknown } }).message?.content\n if (!Array.isArray(content)) continue\n for (const block of content) {\n const candidate = block as { type?: unknown; id?: unknown; name?: unknown } | null\n if (candidate !== null && typeof candidate === 'object' && candidate.type === 'tool-call' && typeof candidate.id === 'string') {\n index.set(candidate.id, typeof candidate.name === 'string' ? candidate.name : '')\n }\n }\n }\n return index\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 (role 'tool'); toolName/toolCallId are\n * backfilled from `toolNames` (assistant tool-call\n * index) — real DSH events do not carry them at the\n * message level. Without an index the result stays\n * untagged (`toolName: ''`), never \"text\".\n * Non-surface events project to nothing.\n */\nexport function projectEvent(event: SessionEvent, toolNames?: ReadonlyMap<string, string>): 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 const key = toolCallIdOfResultEvent(event)\n return [{\n id: String(event.seq),\n role: 'tool',\n contentType: 'tool-result',\n toolName: toolNames?.get(key ?? '') ?? '',\n toolCallId: message?.toolCallId ?? key ?? '',\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[], toolNames?: ReadonlyMap<string, string>): CoreMessage[] {\n const index = toolNames ?? buildToolCallIndex(events)\n const out: CoreMessage[] = []\n for (const event of events) out.push(...projectEvent(event, index))\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 { buildStatusReport, defaultCountTokens, type CompressionCore } from 'acp-kernel'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport type { Session, SessionEvent } from '@deepseek-ai/dsh-session'\nimport type { AcpStateStore } from './state.ts'\nimport { kernelConfigFor, type KernelConfigInput } from './config.ts'\nimport { resolveTokenCount } from './nudge.ts'\nimport type { AcpWindow } from './window.ts'\nimport {\n AlreadyCompressedRangeError,\n blockIdOfKernelRef,\n blockRefForSummarySeq,\n compactionIdsOfKernelBlocks,\n expandShadowedSeqs,\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n stripOrphanedSurfaceToolMessages,\n openToolCallIds,\n surfaceSummary,\n type ResolvedSurfaceRange,\n} from './region.ts'\nimport { allLogMessages, buildToolCallIndex, eventsToCoreMessages, extractEventText, surfaceEventsOf } from './messages.ts'\nimport { DEFAULT_RESOLVED, type ResolvedPrompts } from './prompts.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 /** Resolved prompt templates (optional: falls back to DEFAULT_RESOLVED). */\n readonly prompts?: ResolvedPrompts\n /**\n * Call ids of compress invocations that created a durable block. The engine\n * listens for the matching `tool/result` and hides the call/result pair from\n * the surface, preventing the compaction summary from sitting between them\n * (strict providers reject that sequence with HTTP 400).\n */\n readonly compressCallIdsToHide?: Set<string>\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 // Tolerated wrapped-arguments form: some models emit\n // `{ \"arguments\": \"{\\\"content\\\": [...]}\" }` (double-nested) or\n // `{ \"arguments\": { \"content\": [...] } }` instead of the unwrapped\n // `{ \"content\": [...] }`. The old DSH validator surfaced this as\n // `invalid arguments: \"arguments\" must be an object` and the model retried\n // forever. `arguments` is accepted as an optional JSON node so the wrapped\n // shape passes schema validation; `handleCompress` unwraps it and falls back\n // to a clear runtime error when neither form carries content. `content` is\n // intentionally NOT `required: true` — a required property would reject the\n // wrapped shape before `handleCompress` can see it. The tool description\n // still tells the model content is mandatory.\n arguments: { type: 'json', description: 'Tolerated wrapped-arguments form (model-generated); unwrapped in handleCompress. Prefer passing content directly.' },\n topic: { type: 'string' as const, description: 'Fallback topic for entries without their own.' },\n content: {\n type: 'array' as const,\n description: 'One or more ranges to compress, each with startSeq/endSeq boundaries (surface seqs) and a dense summary. Required — pass it directly, not wrapped in an arguments key.',\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 /** Tolerated wrapped-arguments form (model-generated double-nesting). */\n arguments?: string | { content?: CompressArgs['content'] }\n topic?: string\n content?: Array<{ startSeq: number | string; endSeq: number | string; summary: string; topic?: string }>\n}\n\n/**\n * Unwrap the tolerated wrapped-arguments forms back to the canonical shape:\n * `{ arguments: \"{\\\"content\\\": [...]}\" }` or `{ arguments: { content: [...] } }`\n * → `{ content: [...] }`. The direct `{ content: [...] }` form passes through\n * untouched. Returns null when no form carries content (caller raises).\n */\nfunction unwrapCompressArgs(args: CompressArgs): CompressArgs | null {\n if (args.content !== undefined) return args\n if (args.arguments === undefined) return null\n let inner: unknown = args.arguments\n if (typeof inner === 'string') {\n try {\n inner = JSON.parse(inner)\n } catch {\n return null\n }\n }\n if (typeof inner !== 'object' || inner === null || Array.isArray(inner)) return null\n const content = (inner as { content?: unknown }).content\n if (content === undefined) return null\n return { ...args, content: content as CompressArgs['content'] }\n}\n\n/**\n * Peel the tolerated wrapped-arguments envelope `{ arguments: {…} }` that some\n * model channels emit for ANY tool — the same double-nesting that birthed\n * `unwrapCompressArgs` (live-verified on acp_status: a drilldown call arrived\n * as `{\"arguments\":{\"scope\":\"compressed\"}}` and was silently dropped, since\n * only compress unwrapped). The envelope may be an object or a JSON string;\n * inner keys win over outer duplicates. Args without an envelope pass through\n * untouched.\n */\nfunction unwrapEnvelope<T extends object>(args: T): T {\n const envelope = (args as { arguments?: unknown }).arguments\n if (envelope === undefined) return args\n let inner: unknown = envelope\n if (typeof inner === 'string') {\n try {\n inner = JSON.parse(inner)\n } catch {\n return args\n }\n }\n if (typeof inner !== 'object' || inner === null || Array.isArray(inner)) return args\n return { ...args, ...(inner as object) } as T\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 // Clean orphan tool messages before any range solve: a single orphan result\n // corrupts the pairing balance cache and rejects every large range (issue\n // #18). Every call still in flight — the compress call itself AND any\n // sibling tool called in the same assistant message — must be excluded from\n // orphan pruning: its tool/result lands at the end of the step, and pruning\n // the call now would orphan that result.\n stripOrphanedSurfaceToolMessages(session, openToolCallIds(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 uses the same priority chain as the\n // nudge (projectedTokens → surfaceTokens → character heuristic).\n const coreMessages = allLogMessages(session)\n const surfaceMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const tokenCount = resolveTokenCount(agent, surfaceMessages)\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 // Tolerate the wrapped-arguments forms some models emit (double-nested\n // `{ arguments: \"...\" }`), which the old DSH validator surfaced as\n // `\"arguments\" must be an object` and sent the model into a retry loop.\n const unwrapped = unwrapCompressArgs(args)\n if (unwrapped === null) {\n return {\n text: 'compress: missing content — pass the content array directly: compress({ content: [{ startSeq, endSeq, summary }] })',\n }\n }\n args = unwrapped\n\n const ranges: Array<\n ResolvedSurfaceRange & {\n startSeq: number\n endSeq: number\n startRef: string\n endRef: string\n summary: string\n topic?: string\n }\n > = []\n // Ranges whose whole span was already shadowed by earlier compressions.\n // They land as advisory warnings, never as errors or phantom blocks.\n const alreadyCompressedNotes: string[] = []\n for (const range of args.content!) {\n const startSeq = parseSeq(range.startSeq)\n const endSeq = parseSeq(range.endSeq)\n let resolved: ResolvedSurfaceRange\n try {\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 // Edges shadowed by an earlier compression (stale nudge table / old\n // compress result) are remapped to the still-live content of the span.\n resolved = resolveSurfaceRange(session, startSeq, endSeq)\n } catch (error) {\n if (error instanceof AlreadyCompressedRangeError) {\n const covering = error.coveringBlockIds\n const blockNote = covering.length === 0\n ? ''\n : ` (block ${covering[0]!.slice(0, 8)}${covering.length > 1 ? ` +${covering.length - 1} more` : ''})`\n alreadyCompressedNotes.push(\n ` seqs ${error.start}..${error.end} already compressed${blockNote} — nothing to reclaim; decompress to recover the originals`,\n )\n continue\n }\n throw error\n }\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, resolved.start)\n const endBlockRef = blockRefForSummarySeq(session, resolved.end)\n const startRef = startBlockRef ?? byRaw[String(resolved.start)]\n const endRef = endBlockRef ?? byRaw[String(resolved.end)]\n if (startRef === undefined || endRef === undefined) {\n throw new Error(\n `billion-context-dsh: seq ${resolved.start}..${resolved.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 ranges.push({\n ...resolved,\n startSeq,\n endSeq,\n startRef,\n endRef,\n summary: range.summary,\n ...(range.topic ?? args.topic) === undefined ? {} : { topic: range.topic ?? args.topic },\n })\n }\n\n // Nothing to do: every requested range was already compressed.\n if (ranges.length === 0) {\n const text = ['Compressed 0 block(s), ~0 tokens reclaimed.', ...alreadyCompressedNotes]\n if (alreadyCompressedNotes.length > 0) {\n text.push(' (all requested ranges were already compressed — decompress a block to recover its originals)')\n }\n return { text: text.join('\\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 // A kernel error for ONE range must not poison the whole call: the other\n // ranges still created blocks. This matters for issue #18's \"phantom range\"\n // — messages absorbed into an earlier block's effectiveMessageIds (kernel\n // boundary adjustment) but still live on the surface resolve fine but make\n // the kernel throw \"Range contains no compressible messages\". Fail only\n // when NOTHING landed; otherwise land the successes and surface the\n // failures as advisory lines below.\n if (applied.result.errors.length > 0 && applied.result.blocksCreated === 0) {\n return { text: `compress failed: ${applied.result.errors.join('; ')}` }\n }\n env.store.set(session, applied.state)\n if (applied.result.blocksCreated > 0) {\n // Hide this compress call/result after the tool result lands, so the\n // compaction summary never sits between an assistant tool_calls block and\n // its tool response (strict providers reject that sequence).\n env.compressCallIdsToHide?.add(exec.callId)\n }\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 (const range of ranges) {\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: range.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 const note = range.recovered === true\n ? ` (seqs ${range.startSeq}..${range.endSeq} were already shadowed — compressed the live remainder ${start}..${end})`\n : adjusted\n ? ` (adjusted from ${range.startSeq}..${range.endSeq} to balanced edges)`\n : ''\n lines.push(\n ` block ${compactionId.slice(0, 8)}: seqs ${start}..${end}, ${shadowed.length} messages shadowed${tierLabel}${note}`,\n )\n }\n\n const summaryLine = `Compressed ${applied.result.blocksCreated} block(s), ~${applied.result.tokensCompressed} tokens reclaimed.`\n const totalSkipped = skippedRanges + alreadyCompressedNotes.length\n const failedLines = applied.result.errors.map((error) => ` ${error}`)\n const warningLines = [...freeWarnings.map((warning) => ` ${warning}`), ...failedLines, ...alreadyCompressedNotes, ...lines]\n const footer = totalSkipped > 0\n ? ` (${totalSkipped} range(s) skipped or failed — see 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: the kernel block ref `bN` shown by acp_status (e.g. b1), or a compaction id / prefix from search_context.' },\n} as const\n\ninterface DecompressArgs {\n blockId: string\n}\n\n/** Resolve a block arg to its durable compaction id: exact `bN` kernel ref\n * first (acp_status shows `bN`), then the compaction-id prefix match that\n * search_context and /acp have always used. The `bN` branch is exact\n * (`/^b\\d+$/` with `$`), so a UUID that happens to start with `b1` cannot be\n * shadowed — full UUIDs and 8-char prefixes never match the anchored regex. */\nfunction resolveBlockId(session: Session, arg: string): string | null {\n const byKernelRef = blockIdOfKernelRef(session, arg)\n if (byKernelRef !== null) return byKernelRef\n const ledger = rebuildBlockLedger(session.events)\n const byPrefix = ledger.find((entry) => entry.blockId.startsWith(arg))\n return byPrefix?.blockId ?? null\n}\n\nfunction handleDecompress(_env: ToolEnvironment, rawArgs: DecompressArgs, exec: ToolRunContext): TextOutput {\n const args = unwrapEnvelope<DecompressArgs>(rawArgs)\n const session = requireAgent(exec).session\n const blockId = resolveBlockId(session, args.blockId)\n if (blockId === null) {\n return { text: `decompress: block \"${args.blockId}\" not found (see acp_status for the block list)` }\n }\n const ledger = rebuildBlockLedger(session.events)\n const block = ledger.find((entry) => entry.blockId === 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, rawArgs: SearchArgs, exec: ToolRunContext): TextOutput {\n const args = unwrapEnvelope<SearchArgs>(rawArgs)\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\n/** acp_status drilldown passthrough (kernel buildStatusReport options). All\n * keys optional — no args = overview. `view`/`tool`/`sort`/`limit` only have\n * meaning under `scope:\"uncompressed\"` (`tool` narrows to `view:\"messages\"`;\n * `sort:\"age\"` applies to `scope:\"compressed\"`); the kernel ignores them in\n * overview mode (upstream status-tool docstring documented the same scope).\n * DSH schema compiler: `string` + `enum` supported, no `required: true`\n * anywhere → all optional (schema.js:192-210). */\nconst statusParameters = {\n scope: {\n type: 'string' as const,\n enum: ['compressed', 'uncompressed'] as const,\n description: 'Drilldown scope: \"compressed\" lists compressed blocks, \"uncompressed\" lists visible messages. Omit for the overview.',\n },\n view: {\n type: 'string' as const,\n enum: ['ranges', 'messages'] as const,\n description: 'Drilldown view under scope:\"uncompressed\": \"ranges\" merges visible messages into ranges (default), \"messages\" lists every message.',\n },\n tool: {\n type: 'string' as const,\n description: 'Filter drilldown rows to one tool name (scope:\"uncompressed\" + view:\"messages\" only).',\n },\n sort: {\n type: 'string' as const,\n enum: ['size', 'time', 'tool', 'age'] as const,\n description: 'Row order: size (default, most tokens first), time, tool; \"age\" applies to compressed blocks.',\n },\n limit: {\n type: 'integer' as const,\n description: 'Cap on rows or blocks shown (default 30).',\n },\n}\n\ninterface StatusArgs {\n scope?: 'compressed' | 'uncompressed'\n view?: 'ranges' | 'messages'\n tool?: string\n sort?: 'size' | 'time' | 'tool' | 'age'\n limit?: number\n}\n\n/** A compaction checkpoint summary node (`source.plugin === 'compact'`). These\n * are NOT in any block's `effectiveMessageIds`, so feeding them to\n * `buildStatusReport` would double-count the summary — once as `block.summary`\n * (summaryTokens) and once as a visible text message (totalText). Excluded\n * before status rendering (design §4.2 P1-3). */\nfunction isCheckpointEvent(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\nasync function handleStatus(env: ToolEnvironment, rawArgs: StatusArgs, exec: ToolRunContext): Promise<TextOutput> {\n // The model channel may wrap ANY tool's args under `{ arguments: {…} }`;\n // peel it or drilldown params never reach buildStatusReport (live-verified\n // `{\"arguments\":{\"scope\":\"compressed\"}}` silently rendered the overview).\n const args = unwrapEnvelope<StatusArgs>(rawArgs)\n const agent = requireAgent(exec)\n const session = agent.session\n const state = env.store.stateFor(session)\n const surface = surfaceEventsOf(session)\n // One tool-call index for both projections below (P2-5): tool/result\n // toolName/toolCallId are backfilled from the assistant tool-calls.\n const toolNames = buildToolCallIndex(surface)\n const coreMessages = allLogMessages(session)\n const surfaceMessages = eventsToCoreMessages(surface, toolNames)\n const tokenCount = resolveTokenCount(agent, surfaceMessages)\n const config = kernelConfigFor(env)\n // Run the same pipeline the context transform runs, so what acp_status\n // reports matches what the model actually receives. The returned turn.state\n // carries the freshly assigned refs; it is NOT persisted — acp_status is a\n // read-only view, and env.store.set would advance the nudge baseline a\n // second time in the same turn (design §6.1 P2-2).\n const turn = env.kernel.processTurn({ messages: coreMessages, state, config, tokenCount })\n // Status messages = visible surface EXCLUDING checkpoint summary nodes (P1-3).\n const statusMessages = eventsToCoreMessages(\n surface.filter((event) => !isCheckpointEvent(event)),\n toolNames,\n )\n // Upstream-aligned: the kernel renders the breakdown (percentages of the\n // VISIBLE total — no window semantics; drilldown scope/view/tool/sort/limit\n // pass through verbatim); the engine only appends the nudge decision line,\n // the DSH Surface anchor, and — in drilldown mode — the mN-vs-seq note.\n const report = buildStatusReport(turn.state, statusMessages, defaultCountTokens, args)\n const lines = [report]\n // Mirror upstream pi (`if (args.scope) return base`): a drilldown request\n // answers with the kernel report alone — the nudge decision line is an\n // overview concept. The Surface anchor stays in ALL modes: it is the model's\n // compressible-ref locator (design P2-1).\n if (args.scope === undefined) {\n const nudge = turn.nudge\n if (nudge !== undefined) {\n lines.push('', `Nudge: ${nudge.shouldInject ? 'ACTIVE' : 'idle'} — ${nudge.reason}`)\n }\n }\n lines.push('', `Surface: ${surfaceSummary(session)}`)\n // Drilldown rows carry kernel refs (mN, dense log-order ids). The model must\n // NOT feed them to compress — compress speaks surface seqs, and the Surface\n // anchor above is the only compressible-ref source (design §9 P2-3). Slated\n // for direct seq adaptation when the search_context seq dialect lands (#23).\n if (args.scope === 'uncompressed') {\n lines.push('', 'Note: drilldown rows are kernel refs (mN) for size awareness — compress uses the Surface: seqs above, never mN.')\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 const prompts = env.prompts ?? DEFAULT_RESOLVED\n return [\n defineTool({\n name: 'compress',\n description: prompts.tools.compress,\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: prompts.tools.decompress,\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: prompts.tools.searchContext,\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: prompts.tools.acpStatus,\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 * 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 COMPRESS_PHILOSOPHY,\n TIER2_DISTILL_RULES,\n TIER3_CONDENSE_RULES,\n defaultCountTokens,\n renderNudgeText,\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'\nimport { DEFAULT_RESOLVED, renderTemplate, type ResolvedPrompts } from './prompts.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 /** Resolved prompt templates (optional: falls back to DEFAULT_RESOLVED). */\n readonly prompts?: ResolvedPrompts\n}\n\nexport interface NudgeOutcome {\n readonly message: UserMessage\n readonly emergency: boolean\n}\n\n/**\n * Resolve the best available token count for ACP pressure decisions.\n *\n * Priority chain:\n * 1. `sessionProjections.contextPressure.projectedTokens` — matches the UI's\n * context-occupancy display (includes fixed overhead: system prompt, tool\n * definitions, AGENTS.md, etc.). Provider-anchored; reacts to compaction.\n * 2. `tokenMeter.measure(session).surfaceTokens` — heuristic surface-only\n * estimate (pure conversation messages, no fixed overhead). Falls back\n * when sessionProjections is unavailable or has no provider anchor yet.\n * 3. `defaultCountTokens` character heuristic — last resort for tests and\n * minimal hosts that lack the token-meter service.\n */\nexport function resolveTokenCount(agent: Agent, coreMessages: CoreMessage[]): number {\n // 1. Prefer sessionProjections.contextPressure.projectedTokens (matches UI).\n const projections = agent.ctx?.get?.('sessionProjections') as\n | { snapshot?: (session: unknown) => { values?: { contextPressure?: { projectedTokens?: number } } } }\n | undefined\n const projected = projections?.snapshot?.(agent.session)?.values?.contextPressure?.projectedTokens\n if (typeof projected === 'number' && projected > 0) return projected\n\n // 2. Fallback to tokenMeter surfaceTokens (heuristic, no fixed overhead).\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\n // 3. Last resort: character heuristic.\n return coreMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\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(\n session: import('@deepseek-ai/dsh-session').Session,\n prompts: ResolvedPrompts = DEFAULT_RESOLVED,\n): string {\n const ranges = buildCompressibleSeqRanges(session).slice(0, 6)\n // 零范围:整块省略(保留现状的提前返回与 nudge 尾部 '\\n')。\n if (ranges.length === 0) return ''\n const lines = ranges.map((range) =>\n renderTemplate(prompts.rangeTable.line, {\n start: range.start,\n end: range.end,\n count: range.count,\n tokens: range.tokens,\n }),\n )\n return [\n // 前导空串元素产生 nudge 中范围表前的唯一空行(§4:parts 层不再加分隔)。\n '',\n renderTemplate(prompts.rangeTable.header, { surface: surfaceSummary(session) }),\n renderTemplate(prompts.rangeTable.title, { count: ranges.length }),\n ...lines,\n prompts.rangeTable.footer,\n ].join('\\n')\n}\n\n/**\n * The token count driving pressure decisions. Prefer `resolveTokenCount` which\n * uses `sessionProjections.contextPressure.projectedTokens` (matches the UI's\n * context-occupancy display, including fixed overhead). Falls back to\n * `tokenMeter.measure(session).surfaceTokens`, then `defaultCountTokens`\n * character heuristic for tests and minimal hosts.\n */\nfunction measuredTokenCount(agent: Agent, coreMessages: CoreMessage[]): number {\n return resolveTokenCount(agent, coreMessages)\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, env.prompts)\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 * Render the nudge message text. DEFAULT (no `config.prompts.nudge` override)\n * calls the kernel's own `renderNudgeText` — EFFICIENCY_NOTE/EMERGENCY_HEADER,\n * context breakdown, HOW_TO_COMPRESS_RULES, tier rules, and the batch tip all\n * come from acp-kernel verbatim (the kernel-alignment principle). Only the\n * ref-ID-oriented segments are replaced with our seq-based equivalents,\n * because DSH has no `<acp>` ref tags — see docs/dsh-porting-verification.md:\n * - `rangesStr` (mNNNNN refs) → the surface-seq range table;\n * - the emergency JSON example (startId/endId) → a seq example;\n * - the tier trigger block (block ids bN) → our tier line with surface seqs.\n * When a host overrides any `prompts.nudge` slot, the template path is used so\n * `config.prompts` keeps full control (custom copy wins over kernel defaults).\n */\nexport function buildNudgeText(\n nudge: NudgeDecision,\n emergency: boolean,\n session: import('@deepseek-ai/dsh-session').Session,\n prompts: ResolvedPrompts = DEFAULT_RESOLVED,\n): string {\n // A host override of any nudge slot → template rendering (config.prompts\n // keeps its v0.1.9 contract: custom copy wins). Only the pristine default\n // reference reaches the kernel path.\n if (prompts.nudge !== DEFAULT_RESOLVED.nudge) {\n return renderNudgeFromTemplates(nudge, emergency, session, prompts)\n }\n const rendered = renderNudgeText(nudge)\n return adaptKernelNudgeToSeq(rendered.text, nudge, session, prompts)\n}\n\n/**\n * Take the kernel-rendered nudge text and replace its ref-ID-oriented segments\n * with our surface-seq equivalents. Everything else (frame, philosophy,\n * breakdown, HOW_TO_COMPRESS_RULES, tier rules, tip) stays kernel verbatim.\n */\nfunction adaptKernelNudgeToSeq(\n text: string,\n nudge: NudgeDecision,\n session: import('@deepseek-ai/dsh-session').Session,\n prompts: ResolvedPrompts,\n): string {\n let out = text\n // Tier nudges: replace the kernel trigger block (block ids bN) with our tier\n // line carrying surface seqs. The kernel's TIER2/3 rules stay in the tail.\n if ((nudge.tier === 2 || nudge.tier === 3) && (nudge.tierTargetBlocks?.length ?? 0) > 0) {\n out = replaceTierTrigger(out, nudge, session, prompts)\n } else if (out.includes('\"startId\"')) {\n // Emergency nudges: replace the ref-ID JSON example with a seq example.\n out = replaceEmergencyExample(out)\n }\n // Replace the ref-ID range table (mNNNNN) with the surface-seq table.\n // A zero-range table leaves the kernel's own \"[No specific ranges detected]\"\n // notice intact — it is a better prompt than an empty table.\n const seqTable = rangeTable(session, prompts)\n if (seqTable !== '') out = replaceRangesStr(out, seqTable)\n return out\n}\n\n/** Replace the kernel rangesStr segment (`Compressible ranges (N, oldest first):…`) with our seq table. */\nfunction replaceRangesStr(text: string, seqTable: string): string {\n const match = text.match(/\\n\\n(?:Compressible ranges \\(|\\[No specific ranges detected)/)\n if (!match) return text\n const start = match.index!\n const rest = text.slice(start + 2)\n const next = rest.match(/\\n\\n/)\n const end = next !== null ? start + 2 + next.index! : text.length\n const before = text.slice(0, start)\n const after = text.slice(end)\n // seqTable starts with '\\n' (the range table's leading blank line), so\n // `before` + '\\n' + seqTable yields one blank line before the table.\n return before + '\\n' + seqTable + after\n}\n\n/** Replace the kernel tier trigger segment (`[TIER n …TRIGGER]…Example: compress(…)`) with our tier line. */\nfunction replaceTierTrigger(\n text: string,\n nudge: NudgeDecision,\n session: import('@deepseek-ai/dsh-session').Session,\n prompts: ResolvedPrompts,\n): string {\n const start = text.search(/\\n\\n(?:\\[TIER \\d|\\[EMERGENCY — TIER \\d)/)\n if (start === -1) return text\n const rest = text.slice(start + 2)\n const next = rest.match(/\\n\\nHOW TO COMPRESS/)\n const end = next !== null ? start + 2 + next.index! : text.length\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 const tierValue = nudge.tier === null ? 2 : nudge.tier\n const tierLine = renderTemplate(prompts.nudge.tier, {\n tier: tierValue,\n count: targets.length,\n prevTier: tierValue - 1,\n tokens,\n seqs: summarySeqs.join(', '),\n })\n return text.slice(0, start) + '\\n\\n' + tierLine + text.slice(end)\n}\n\n/** Replace the kernel emergency JSON example (startId/endId) with a seq example. */\nfunction replaceEmergencyExample(text: string): string {\n const start = text.search(/\\n\\n\\{ \"topic\":/)\n if (start === -1) return text\n const rest = text.slice(start + 2)\n const next = rest.match(/\\n\\nCompressible ranges |\\n\\n\\[No specific/)\n const end = next !== null ? start + 2 + next.index! : text.length\n return text.slice(0, start)\n + '\\n\\ncompress({ content: [{ startSeq, endSeq, summary }] }) — use the seqs from the range table above.'\n + text.slice(end)\n}\n\n/**\n * Template rendering path (used only when a host overrides a `prompts.nudge`\n * slot). Kept byte-compatible with the pre-refactor assembly: frame → breakdown\n * → growth → guidance → tier(+rules)/range table → tip.\n */\nfunction renderNudgeFromTemplates(\n nudge: NudgeDecision,\n emergency: boolean,\n session: import('@deepseek-ai/dsh-session').Session,\n prompts: ResolvedPrompts,\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 = renderTemplate(\n emergency ? prompts.nudge.emergency : prompts.nudge.normal,\n { pct, philosophy: COMPRESS_PHILOSOPHY },\n )\n const parts: string[] = [frame]\n\n // Context breakdown (kernel style, from NudgeDecision.contextBreakdown).\n if (nudge.contextBreakdown) {\n const bd = nudge.contextBreakdown\n const breakdown = renderTemplate(prompts.nudge.breakdown, {\n system: Math.round(bd.system / 1000),\n tool: Math.round(bd.tool / 1000),\n summaries: Math.round(bd.summaries / 1000),\n code: Math.round(bd.code / 1000),\n text: Math.round(bd.text / 1000),\n })\n if (breakdown !== '') parts.push('', breakdown)\n if (bd.growth > 0) {\n const growth = renderTemplate(prompts.nudge.growth, { growth: Math.round(bd.growth / 1000) })\n if (growth !== '') parts.push(growth)\n }\n }\n\n // HOW_TO_COMPRESS_RULES as guidance (kernel puts it in every nudge).\n if (prompts.nudge.guidance !== '') parts.push('', prompts.nudge.guidance)\n\n // Tier line (distillation / condensation suggestion) + tier-specific rules.\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 const tierLine = renderTemplate(prompts.nudge.tier, {\n tier: nudge.tier,\n count: targets.length,\n prevTier: nudge.tier - 1,\n tokens,\n seqs: summarySeqs.join(', '),\n })\n if (tierLine !== '') parts.push(tierLine)\n // Tier-specific rules from kernel (TIER2_DISTILL_RULES / TIER3_CONDENSE_RULES).\n const tierRules = nudge.tier === 2 ? TIER2_DISTILL_RULES : TIER3_CONDENSE_RULES\n parts.push('', tierRules)\n } else {\n // Range table for non-tier nudges (DSH-specific: seq-based, not ref-ID-based).\n parts.push(rangeTable(session, prompts))\n }\n\n // Batch-compress tip (from kernel's nudge-text.ts style).\n if (prompts.nudge.tip !== '') parts.push('', prompts.nudge.tip)\n\n return parts.join('\\n')\n}\n","/**\n * M4 — configurable prompt templates: the per-stage model-visible texts\n * (nudge frames, range table, system prompt, tool descriptions) rendered from\n * `config.prompts` templates with named placeholders.\n *\n * Design: docs/configurable-prompts-design.md (v4).\n * - placeholders are `{identifier}` only; literal braces like\n * `compress({ content: [...] })` are left untouched (spaces/commas break the\n * identifier rule);\n * - resolvePrompts merges user overrides over DEFAULT_PROMPTS per key\n * (null/undefined → default, string → override; group-level null → whole\n * group default for YAML hosts) and validates unknown placeholders at\n * construction time (fail-fast, no silent typos);\n * - renderTemplate throws when a known placeholder has no value — callers\n * must provide every value (e.g. tokens via a typeof fallback).\n * @module billion-context-dsh/prompts\n */\n\nimport { COMPRESS_PHILOSOPHY, HOW_TO_COMPRESS_RULES, TIER2_DISTILL_RULES, TIER3_CONDENSE_RULES } from 'acp-kernel'\n\n/** 用户可写值:字符串模板,或 null(= 用默认,等价于不写)。YAML 宿主写 null 是合法输入。 */\nexport type PromptInput = string | null\n\n/** 按组生成\"每键可选、可 null\"的覆盖类型。 */\nexport type PromptOverride<T> = { [K in keyof T]?: PromptInput }\n\nexport interface NudgePrompts {\n /** 普通档首句。占位符:{pct} {philosophy} */\n normal: string\n /** 紧急档首句。占位符:{pct} {philosophy} */\n emergency: string\n /** 指导行(HOW_TO_COMPRESS_RULES)。无占位符 */\n guidance: string\n /** tier 蒸馏行。占位符:{tier} {count} {prevTier} {tokens} {seqs} */\n tier: string\n /** 上下文分解。占位符:{system} {tool} {summaries} {code} {text} */\n breakdown: string\n /** 增长行。占位符:{growth} */\n growth: string\n /** 溢出提示。无占位符 */\n tip: string\n}\n\nexport interface RangeTablePrompts {\n /** 表头。占位符:{surface} */\n header: string\n /** 标题。占位符:{count}(表格行数) */\n title: string\n /** 每行。占位符:{start} {end} {count} {tokens} */\n line: string\n /** 表尾调用语法。无占位符 */\n footer: string\n}\n\nexport interface ToolPrompts {\n /** 工具描述(纯文本,无占位符) */\n compress: string\n decompress: string\n searchContext: string\n acpStatus: string\n}\n\nexport interface AcpPrompts {\n readonly nudge?: PromptOverride<NudgePrompts>\n readonly rangeTable?: PromptOverride<RangeTablePrompts>\n readonly tools?: PromptOverride<ToolPrompts>\n /** 整段 system prompt 模板;`{philosophy}` 引用 kernel 的 COMPRESS_PHILOSOPHY */\n readonly systemPrompt?: PromptInput\n}\n\n/** 解析结果 —— 所有字段已填满(纯 string,无 null)、已校验。构造一次,全程复用。 */\nexport interface ResolvedPrompts {\n readonly nudge: NudgePrompts\n readonly rangeTable: RangeTablePrompts\n readonly tools: ToolPrompts\n /** 注意:这是【模板】(含 {philosophy}),不是渲染结果。渲染用 renderSystemPrompt。 */\n readonly systemPromptTemplate: string\n}\n\n/** 每槽允许的占位符名集合(构建期校验用)。 */\nconst NUDGE_ALLOWED: { [K in keyof NudgePrompts]: ReadonlySet<string> } = {\n normal: new Set(['pct', 'philosophy']),\n emergency: new Set(['pct', 'philosophy']),\n guidance: new Set(),\n tier: new Set(['tier', 'count', 'prevTier', 'tokens', 'seqs']),\n breakdown: new Set(['system', 'tool', 'summaries', 'code', 'text']),\n growth: new Set(['growth']),\n tip: new Set(),\n}\nconst RANGE_TABLE_ALLOWED: { [K in keyof RangeTablePrompts]: ReadonlySet<string> } = {\n header: new Set(['surface']),\n title: new Set(['count']),\n line: new Set(['start', 'end', 'count', 'tokens']),\n footer: new Set(),\n}\nconst TOOLS_ALLOWED: { [K in keyof ToolPrompts]: ReadonlySet<string> } = {\n compress: new Set(),\n decompress: new Set(),\n searchContext: new Set(),\n acpStatus: new Set(),\n}\nconst SYSTEM_ALLOWED = new Set(['philosophy', 'howToCompressRules', 'tier2DistillRules', 'tier3CondenseRules'])\n\n/** 校验单个模板:未知 `{ident}` → throw(带槽位路径)。默认模板开发期已核验,不重扫。 */\nfunction validateTemplate(template: string, allowed: ReadonlySet<string>, path: string): string {\n const re = /\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g\n let match: RegExpExecArray | null\n while ((match = re.exec(template)) !== null) {\n const name = match[1]!\n if (!allowed.has(name)) {\n throw new Error(\n `${path} contains unknown placeholder {${name}} — allowed: ${[...allowed].join(', ') || '(none)'}`,\n )\n }\n }\n return template\n}\n\n/**\n * 纯替换。两个契约:\n * 1. 未知占位符不可能到达这里(构建期已校验);\n * 2. 已知占位符缺值 = 编程错误 → throw(绝不静默渲染空串)。\n */\nexport function renderTemplate(template: string, vars: Record<string, string | number>): string {\n return template.replace(/\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_match, name: string) => {\n const value = vars[name]\n if (value === undefined) {\n throw new Error(\n `renderTemplate: missing value for placeholder {${name}} in template \"${template.slice(0, 60)}…\"`,\n )\n }\n return String(value)\n })\n}\n\n/**\n * 逐键合并:null / undefined → 默认;字符串 → 覆盖默认(不用 spread,\n * 否则 null 会覆盖默认,与\"null = 用默认\"矛盾)。组级 null/undefined →\n * 整组用默认(YAML 宿主可能写 `{ nudge: null }`,W3)。\n */\nfunction mergeGroup<T extends Record<keyof T, string>>(\n defaults: T,\n override: PromptOverride<T> | null | undefined,\n allowed: { [K in keyof T]: ReadonlySet<string> },\n path: string,\n): T {\n if (override == null) return defaults\n const out = {} as { [K in keyof T]: string }\n for (const key of Object.keys(defaults) as Array<keyof T>) {\n const value = override[key]\n out[key] = value === null || value === undefined\n ? defaults[key]\n : validateTemplate(value, allowed[key], `${path}.${String(key)}`)\n }\n return out as T\n}\n\n/**\n * 深合并 + 校验;引擎构造期调用一次,出错即抛(fail-fast)。\n * 未传入时返回 DEFAULT_RESOLVED,零校验重跑。\n */\nexport function resolvePrompts(input?: AcpPrompts): ResolvedPrompts {\n if (input === undefined) return DEFAULT_RESOLVED\n return {\n nudge: mergeGroup(DEFAULT_PROMPTS.nudge, input.nudge, NUDGE_ALLOWED, 'prompts.nudge'),\n rangeTable: mergeGroup(DEFAULT_PROMPTS.rangeTable, input.rangeTable, RANGE_TABLE_ALLOWED, 'prompts.rangeTable'),\n tools: mergeGroup(DEFAULT_PROMPTS.tools, input.tools, TOOLS_ALLOWED, 'prompts.tools'),\n systemPromptTemplate:\n input.systemPrompt === null || input.systemPrompt === undefined\n ? DEFAULT_PROMPTS.systemPromptTemplate\n : validateTemplate(input.systemPrompt, SYSTEM_ALLOWED, 'prompts.systemPrompt'),\n }\n}\n\n/** 渲染 system prompt 模板(注入 kernel 压缩哲学、压缩规则、蒸馏规则)。 */\nexport function renderSystemPrompt(prompts: ResolvedPrompts): string {\n return renderTemplate(prompts.systemPromptTemplate, {\n philosophy: COMPRESS_PHILOSOPHY,\n howToCompressRules: HOW_TO_COMPRESS_RULES,\n tier2DistillRules: TIER2_DISTILL_RULES,\n tier3CondenseRules: TIER3_CONDENSE_RULES,\n })\n}\n\n/**\n * 默认模板 —— 与 v4 之前的硬编码文案逐字节一致\n * (回归锚点见 tests/prompts.test.ts 的硬编码字面量快照)。\n */\nexport const DEFAULT_PROMPTS: ResolvedPrompts = {\n nudge: {\n // 与 kernel nudge-text.ts EFFICIENCY_NOTE 逐字对齐——不含 \"Context usage is at X%\"\n // 陈述(usage 只通过 breakdown 传达);{pct} 仍可用作自定义占位符。\n normal: 'This is an efficiency nudge to compress early and keep context lean — not an overflow warning. A separate, stronger alert will appear if the context is actually full.\\n\\n{philosophy}',\n emergency: '⚠️ Context limit reached — compress now. Prioritize consumed tool outputs.\\n\\n{philosophy}',\n guidance: HOW_TO_COMPRESS_RULES,\n tier: 'Tier {tier}: {count} tier-{prevTier} block(s) distillable ({tokens} tokens) — compress their summary node(s) [seqs {seqs}] to reclaim the original messages.',\n breakdown: 'Context breakdown: {system}K system | {tool}K tool | {summaries}K summaries | {code}K code | {text}K text',\n growth: '+{growth}K since last nudge',\n tip: '💡 Compress all ranges in one call (pass multiple content entries: `content: [{...}, {...}]`).',\n },\n rangeTable: {\n header: 'Surface: {surface}',\n title: 'Compressible ranges (suggestions only — compress any consumed span; refs are surface seqs):',\n line: ' - seq {start}..{end} — {count} messages, ~{tokens} tokens',\n footer: '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'\n + 'Snapshot taken at nudge time: the seqs go stale once the surface moves (a later compress shadows them), so re-run acp_status for fresh refs before compressing.',\n },\n tools: {\n compress: 'Replace older conversation ranges with dense summaries you write. Each message seq is a surface reference. Single range: compress({ content: [{ startSeq, endSeq, summary }] }). Batch multiple unrelated ranges in one call (each content entry becomes its own block); keep ranges disjoint. Never compress content the current step is actively using. Compress boundaries are SURFACE SEQS (acp_status Surface: row, latest nudge table) — NOT the block refs (bN, e.g. b1) that acp_status COMPRESSED BLOCKS shows, which are for decompress only. Seq refs must come from the CURRENT surface (acp_status or the latest nudge): a span whose edges were shadowed by an earlier compress is auto-remapped to its still-live content, a fully compressed span is reported as already compressed, and invented/other-session seqs fail with guidance.',\n decompress: 'Recover the original content of a compressed block by its blockId — the kernel block ref `bN` shown by acp_status (e.g. b1), or a compaction id from search_context (read-only; does not unshadow the range).',\n searchContext: 'Search inside compressed blocks (summaries and original content) for information the model no longer sees in context.',\n acpStatus: 'Context status: overview of the current context — CONTEXT BREAKDOWN (tool/text/summaries token shares of the visible total), COMPRESSED BLOCKS ledger, and the nudge decision. No args = overview. Percentages are shares of the visible content, not the context window. Note: the block refs in COMPRESSED BLOCKS (bN, e.g. b1) are for decompress; compress uses the Surface: seq range, not bN. Drilldown: pass scope:\"compressed\" for a per-block list, or scope:\"uncompressed\" with view:\"messages\" (every visible message) / view:\"ranges\" (merged ranges); tool filters to one tool name, sort reorders (size/time/tool; age for compressed), limit caps rows (default 30). Drilldown row refs are kernel ids (mN) for size awareness only — compress always uses the Surface: seqs, never mN.',\n },\n systemPromptTemplate: `Active Context Pruning — model-driven context management\n\nYOU decide whether and when to compress context. The nudge is an efficiency notification: when you see one, consider which ranges you have genuinely consumed and could summarise to keep working context lean.\n\n{philosophy}\n\nWHEN TO COMPRESS:\n- A sub-agent or delegated task has returned a large result that you have already extracted the key facts from.\n- Verbose command output (build/test logs, git diff, directory listings) where you have already used the information you need.\n- Exploration that led nowhere.\n- Repeated reads of the same file or repeated status checks once the decision is recorded.\n- Resolved discussion threads where a decision has been captured in summary or in code.\n- Intermediate steps of a completed multi-step task, once the final result is recorded.\n- A task phase has ended — bug hunt complete, root cause found, exploration done, research sprint wrapped.\n\nWHEN NOT TO COMPRESS:\n- Content the current step is actively reading or reasoning about.\n- Important user messages — preserve their exact intent, constraints, and acceptance criteria.\n- Protected tool outputs — hard-excluded from compression ranges, survive intact in visible context.\n\n{howToCompressRules}\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. Seq refs must be on the current surface: seqs from older nudges or earlier compresses go stale as the surface moves, so a stale span is auto-remapped to its still-live remainder (the result reports the adjusted span), a fully compressed span is reported as already compressed, and invented/other-session seqs fail with guidance. The block refs (bN, e.g. b1) in acp_status COMPRESSED BLOCKS are for decompress, NOT compress boundaries.\n- decompress: recover a compressed block's original content, read-only. decompress({ blockId }) — accept the bN ref shown by acp_status (e.g. b1) or a compaction id.\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 right before compressing — the only seqs that never go stale are the ones you just read. Drilldown (scope/view/tool/sort/limit) lists per-message or per-block sizes; drilldown rows are kernel ids (mN) for size awareness — compress uses seqs, never mN.\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\n{tier2DistillRules}\n\n{tier3CondenseRules}\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\n/** 模块级默认缓存:默认参/兜底直接引用,避免每次调用重跑校验。 */\nexport const DEFAULT_RESOLVED: ResolvedPrompts = DEFAULT_PROMPTS\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 { resolveTokenCount } from './nudge.ts'\nimport {\n blockIdOfKernelRef,\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 = resolveTokenCount(agent, coreMessages)\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 // Accept the kernel block ref (`bN`) the model tool acp_status shows, as\n // well as the compaction-id prefix (same dual-id resolution as the tool).\n const blockId = blockIdOfKernelRef(session, args[0]!)\n const ledger = rebuildBlockLedger(session.events)\n const block = blockId === null\n ? ledger.find((entry) => entry.blockId.startsWith(args[0]!))\n : ledger.find((entry) => entry.blockId === blockId)\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 * 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 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.\n *\n * The text is DEFAULT_PROMPTS.systemPromptTemplate rendered with the kernel's\n * COMPRESS_PHILOSOPHY and HOW_TO_COMPRESS_RULES; hosts can override the whole\n * section via `config.prompts.systemPrompt` (see docs/configurable-prompts-design.md).\n * @module billion-context-dsh/system-prompt\n */\n\nimport { DEFAULT_PROMPTS, renderSystemPrompt } from './prompts.ts'\n\nexport const ACP_SYSTEM_PROMPT = renderSystemPrompt(DEFAULT_PROMPTS)\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","COMPRESS_PHILOSOPHY","TIER2_DISTILL_RULES","TIER3_CONDENSE_RULES","defaultCountTokens","createUserMessage","defaultCountTokens","createUserMessage","COMPRESS_PHILOSOPHY","TIER2_DISTILL_RULES","TIER3_CONDENSE_RULES","defaultCountTokens","defaultCountTokens","defaultCountTokens"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/state.ts","../src/region.ts","../src/messages.ts","../src/tools.ts","../src/config.ts","../src/nudge.ts","../src/prompts.ts","../src/commands.ts","../src/window.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_ORDER } from './system-prompt.ts'\nimport { renderSystemPrompt, resolvePrompts, type AcpPrompts, type ResolvedPrompts } from './prompts.ts'\nimport { DEFAULT_CONTEXT_WINDOW, detectContextWindow, type AcpWindow } from './window.ts'\nimport { deferCompressPairHide, stripOrphanedSurfaceToolMessages } from './region.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 {\n DEFAULT_PROMPTS,\n DEFAULT_RESOLVED,\n renderSystemPrompt,\n renderTemplate,\n resolvePrompts,\n type AcpPrompts,\n type NudgePrompts,\n type PromptInput,\n type PromptOverride,\n type RangeTablePrompts,\n type ResolvedPrompts,\n type ToolPrompts,\n} from './prompts.ts'\nexport { makeTools, type ToolEnvironment } from './tools.ts'\nexport { acpCommand } from './commands.ts'\nexport { buildNudge, resolveTokenCount, 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 AlreadyCompressedRangeError,\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n findOpenTurn,\n assertNoActiveCompaction,\n blockRegistry,\n blockRefForSummarySeq,\n compactionIdsOfKernelBlocks,\n summarySeqOfKernelBlock,\n expandShadowedSeqs,\n hideCompressToolPair,\n stripOrphanedSurfaceToolMessages,\n type AcpBlockLedgerEntry,\n type CompactionTransactionInput,\n type ResolvedSurfaceRange,\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 /**\n * Custom token-count function for the kernel's internal estimation.\n * Defaults to the kernel's `defaultCountTokens` (CJK: 1 char = 1 token,\n * other: 4 chars = 1 token — aligns with billion-context-pi).\n * Can be overridden for provider-specific tokenization, e.g. DeepSeek's\n * official coefficient: 1 CJK char ≈ 0.6 tokens, 1 other char ≈ 0.3 tokens.\n * Only affects the kernel's internal estimation (compressible range sizing,\n * nudge text, growth branch pending); the `projectedTokens` reading from\n * `sessionProjections` (used for nudge pressure decisions and acp_status)\n * is provider-anchored and unaffected by this function.\n */\n readonly countTokens?: (text: string) => number\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 /** Per-stage prompt template overrides (nudge / range table / system prompt / tool descriptions). See docs/configurable-prompts-design.md. */\n readonly prompts?: AcpPrompts\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 /** Resolved prompt templates (validated at construction — fail-fast on template typos). */\n readonly prompts: ResolvedPrompts\n\n private readonly lastNudgeTurn = new Map<string, number>()\n /** Successful compress call ids awaiting their tool/result so the pair can be hidden. */\n private readonly compressCallIdsToHide = new Set<string>()\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 // Resolve + validate prompt templates BEFORE building env: a template typo\n // must fail engine construction, never silently leak into model context.\n this.prompts = resolvePrompts(config.prompts)\n const ports = this.config.countTokens !== undefined ? { countTokens: this.config.countTokens } : {}\n this.kernel = createCore(ports)\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 prompts: this.prompts,\n compressCallIdsToHide: this.compressCallIdsToHide,\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 // After a successful compress tool result is appended, hide its\n // call/result pair. The durable summary node was inserted mid-turn (before\n // the result), so leaving the pair visible would put a user message between\n // an assistant tool_calls block and its tool response — strict providers\n // reject that request with HTTP 400 (issue #18).\n ctx.on('session/event', (session, event) => {\n if (event.type !== 'tool/result') return\n const message = event.data.message\n const block = message.content[0]\n const callId = block?.toolCallId ?? message.source.callId\n if (typeof callId !== 'string' || !this.compressCallIdsToHide.has(callId)) return\n this.compressCallIdsToHide.delete(callId)\n // session.append is NOT reentrant: calling it synchronously inside this\n // session/event dispatch (the outer append still holds the reentry lock)\n // throws \"session append cannot reenter while another append is being\n // published\" on live, store-attached sessions, and the dispatcher\n // silently swallows the error — the hide would be a no-op. Defer it to a\n // microtask: microtasks drain after the append fully publishes and\n // before the agent loop resumes, so the pair is hidden before the next\n // request is built.\n deferCompressPairHide(session, callId, event.seq, (error) => {\n ctx.logger.warn(`billion-context-dsh: hide compress call/result pair failed: ${String(error)}`)\n })\n })\n ctx.on('agent/pre-step', async (payload, next) => {\n // A crash-interrupted tool leaves an orphan call/result on the surface:\n // it corrupts the pairing balance cache AND can 400 the next request\n // (strict providers reject tool messages without their call/response).\n // Clean them before EVERY step — not only when a nudge fires — so a\n // low-pressure session never hits the orphan 400 (issue #18). No call is\n // in flight at pre-step (the previous step's tools all landed), so the\n // default empty in-flight set is safe.\n stripOrphanedSurfaceToolMessages(payload.agent.session)\n if (!this.config.autoNudge) return 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 // The load-bearing ACP guidance lives in the system prompt ONCE; nudges\n // stay short and advisory (model-driven: the model decides). The\n // systemPrompt service may not be registered yet on cold start (cordis\n // starts unrelated composition rows concurrently), so apply the same\n // retry pattern as tools and commands: eager registration, then\n // re-attempt when the service appears via `internal/service`; guard so a\n // late callback never double-registers.\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: renderSystemPrompt(this.prompts),\n })\n } else {\n let done = false\n const registerSystemPrompt = (): void => {\n if (done) return\n const registry = ctx.get('systemPrompt')\n if (registry === undefined) return\n done = true\n registry.section({\n name: 'billion-context-dsh',\n order: ACP_SYSTEM_PROMPT_ORDER,\n text: renderSystemPrompt(this.prompts),\n })\n }\n ctx.on('internal/service', (name: unknown) => {\n if (name === 'systemPrompt') registerSystemPrompt()\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 { createAssistantMessage, createUserMessage, type ContentBlock } from '@deepseek-ai/dsh-llm'\nimport { defaultCountTokens } from 'acp-kernel'\nimport { extractEventText, extractText, toolCallIdOfResultEvent } 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 * A requested range whose EVERY live message was already shadowed by one or\n * more blocks. The compress tool catches this and reports the range as already\n * compressed (with the covering block ids) instead of folding block summary\n * nodes as plain messages or erroring out. Distillation stays an explicit act:\n * target a LIVE checkpoint seq directly to distill (tier 2/3).\n */\nexport class AlreadyCompressedRangeError extends Error {\n constructor(\n readonly start: number,\n readonly end: number,\n readonly coveringBlockIds: readonly string[],\n ) {\n super(\n `billion-context-dsh: seq ${start}..${end} already compressed — `\n + 'no live content remains in that span',\n )\n this.name = 'AlreadyCompressedRangeError'\n }\n}\n\ntype StaleRangeRecovery =\n | { kind: 'ok'; start: number; end: number }\n | { kind: 'already-compressed'; coveringBlockIds: string[] }\n | { kind: 'unresolvable'; failedEdge: number }\n\n/**\n * Rebuild a requested range whose edges are no longer on the current surface.\n * The dominant cause is staleness: the seqs came from an older nudge table or\n * a previous compress result, and an earlier compression SHADOWED them (they\n * stay in the append-only log, but are gone from the surface). The recovery:\n *\n * 1. An edge that does not exist in the log at all (invented, or from another\n * session) is unresolvable — there is no way to guess what it meant.\n * 2. The still-LIVE surface nodes inside the requested span, in VALUE order\n * (the surface can be locally non-monotonic after replacements, so value\n * order is the only coherent span). If there are none, the whole span was\n * already compressed → 'already-compressed' with the covering block ids.\n * 3. Otherwise the range snaps to the first..last live PLAIN node in the\n * span. Block checkpoint nodes are deliberately excluded: distilling a\n * block on a STALE reference would silently change block structure the\n * model never intended to touch — distillation requires targeting a live\n * checkpoint seq directly.\n */\nfunction recoverStaleRange(session: Session, start: number, end: number): StaleRangeRecovery {\n if (session.events[start] === undefined || session.events[end] === undefined) {\n const failedEdge = session.events[start] === undefined ? start : end\n return { kind: 'unresolvable', failedEdge }\n }\n const liveInside = session.surface.nodes\n .filter((seq) => seq >= start && seq <= end)\n .sort((a, b) => a - b)\n const plain = liveInside.filter((seq) => !isCheckpointNode(session.events[seq]!))\n if (plain.length === 0) {\n const coveringBlockIds = rebuildBlockLedger(session.events)\n .filter((entry) => entry.shadowedSeqs.some((seq) => seq >= start && seq <= end))\n .map((entry) => entry.blockId)\n return { kind: 'already-compressed', coveringBlockIds }\n }\n return { kind: 'ok', start: plain[0]!, end: plain[plain.length - 1]! }\n}\n\nexport interface ResolvedSurfaceRange {\n readonly start: number\n readonly end: number\n /**\n * True when the requested edges were not on the current surface and were\n * remapped to the still-live content of the requested span (an earlier\n * compression shadowed them). Callers surface this so the model sees what\n * was actually compressed instead of silently shadowing a different span.\n */\n readonly recovered?: boolean\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. Reversed\n * ranges throw. An edge that sits inside a tool-call/result pair — or on a\n * multi-tool-call assistant message that has no bare-seq ref — is first nudged\n * inward to the nearest clean cut; if that collapses the range (e.g. the model\n * asked for a SINGLE tool result, which can never be balanced alone), the\n * range EXPANDS outward to the enclosing clean pair instead — a lone tool\n * message is almost always a \"consumed output\" the model genuinely wants to\n * compress. The returned range is what a caller should actually shadow.\n *\n * Missing edges are NOT an immediate error: the seqs were probably shadowed by\n * an earlier compression (stale nudge table / old compress result). The span\n * is rebuilt from its still-live remainder via recoverStaleRange — a fully\n * shadowed span throws AlreadyCompressedRangeError, a genuinely unknown edge\n * throws the not-in-surface guidance error. The returned range is what a\n * caller should actually shadow.\n */\nexport function resolveSurfaceRange(\n session: Session,\n start: number,\n end: number,\n): ResolvedSurfaceRange {\n const nodes = session.surface.nodes\n if (start > end) {\n throw new Error(`billion-context-dsh: reversed range ${start}..${end}`)\n }\n let requestedStartIdx = nodes.indexOf(start)\n let requestedEndIdx = nodes.indexOf(end)\n let recovered = false\n if (requestedStartIdx < 0 || requestedEndIdx < 0) {\n const stale = recoverStaleRange(session, start, end)\n if (stale.kind === 'unresolvable') {\n throw new Error(\n `billion-context-dsh: seq ${start}..${end} not in the current surface — `\n + `edge seq ${stale.failedEdge} is not in this session's log. `\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 (stale.kind === 'already-compressed') {\n throw new AlreadyCompressedRangeError(start, end, stale.coveringBlockIds)\n }\n start = stale.start\n end = stale.end\n recovered = true\n requestedStartIdx = nodes.indexOf(start)\n requestedEndIdx = nodes.indexOf(end)\n if (requestedStartIdx < 0 || requestedEndIdx < 0) {\n // Unreachable in practice (recovery returns live nodes), but never let\n // a negative index reach the balancing passes.\n throw new Error(\n `billion-context-dsh: seq ${start}..${end} not in the current surface — `\n + 'consult acp_status for the current surface range',\n )\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 recovered\n ? { start: nodes[startIdx]!, end: nodes[endIdx]!, recovered: true }\n : { start: nodes[startIdx]!, end: nodes[endIdx]! }\n }\n // A recovered span NEVER expands across block checkpoints: the model's\n // requested edges were stale, so growing the span into block territory could\n // fold content it never intended to touch. If the live remainder cannot be\n // balanced by shrinking alone, give up with guidance instead.\n if (recovered) {\n throw new Error(\n `billion-context-dsh: no tool-pairing-balanced live remainder around seq ${start}..${end} — `\n + 'narrow the range or consult acp_status for the current surface',\n )\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/** Tool-call ids carried by one assistant surface message. */\nfunction toolCallIdsOfEvent(event: SessionEvent): string[] {\n if (event.type !== 'assistant/message') return []\n const content = (event.data as { message?: { content?: unknown } }).message?.content\n if (!Array.isArray(content)) return []\n const ids: string[] = []\n for (const block of content) {\n if (block === null || typeof block !== 'object') continue\n const b = block as { type?: unknown; id?: unknown }\n if (b.type === 'tool-call' && typeof b.id === 'string') ids.push(b.id)\n }\n return ids\n}\n\n/**\n * Provider/model to stamp on a synthetic empty assistant pruning node.\n */\nfunction assistantProviderModel(event: SessionEvent): { provider: string; model: string } {\n if (event.type === 'assistant/message') {\n const message = (event.data as { message?: { source?: { provider?: unknown; model?: unknown } } }).message\n return {\n provider: typeof message?.source?.provider === 'string' ? message.source.provider : 'billion-context-dsh',\n model: typeof message?.source?.model === 'string' ? message.source.model : 'surface-prune',\n }\n }\n return { provider: 'billion-context-dsh', model: 'surface-prune' }\n}\n\n/**\n * Durable model-free prune: append `compaction/prune` as the shadow price, then\n * replace the given surface seqs with either a user message carrying `text`\n * (used for compress call/result hiding, so the model still sees the tool\n * outcome) or an EMPTY assistant message (used for orphan cleanup, which DSH\n * derives to nothing). The originals remain in the append-only log.\n */\nfunction hideSurfaceSeqs(\n session: Session,\n seqs: readonly number[],\n provider: string,\n model: string,\n text?: string,\n): void {\n if (seqs.length === 0) return\n const start = seqs[0]!\n const end = seqs[seqs.length - 1]!\n let shadowedTokenCount = 0\n for (const seq of seqs) {\n const event = session.events[seq]\n if (event !== undefined) shadowedTokenCount += defaultCountTokens(extractEventText(event))\n }\n session.append('compaction/prune', {\n shadowedRange: { start, end },\n shadowedSeqs: [...seqs],\n shadowedTokenCount,\n })\n if (text !== undefined) {\n session.append('user/message', createUserMessage({\n content: [{ type: 'text', text }],\n source: { kind: 'plugin', plugin: 'billion-context-dsh' },\n }), {\n surfaceOp: { op: 'replace', start, end },\n sourceEventSeqs: [...seqs],\n })\n return\n }\n session.append('assistant/message', {\n turn: findOpenTurn(session.events) ?? 0,\n step: 0,\n message: createAssistantMessage({ content: [], source: { provider, model } }),\n }, {\n surfaceOp: { op: 'replace', start, end },\n sourceEventSeqs: [...seqs],\n })\n}\n\n/**\n * Hide one successful `compress` tool's call/result pair after its tool/result\n * has been logged. The durable compaction summary is inserted BEFORE the\n * current tool result (the compress tool runs mid-turn), so leaving the pair on\n * the surface would produce `assistant(tool_calls) → user(summary) →\n * tool(result)` — rejected by strict providers. Replacing both nodes with a\n * plain user message (the result text) removes the pair from the derived\n * surface without touching the compaction block.\n */\nexport function hideCompressToolPair(session: Session, callId: string, resultSeq?: number): boolean {\n let callSeq: number | null = null\n for (const event of session.events) {\n if (event.type !== 'assistant/message') continue\n if (toolCallIdsOfEvent(event).includes(callId)) {\n callSeq = event.seq\n break\n }\n }\n if (callSeq === null) return false\n // Only hide a node that carries EXACTLY the compress call. Hiding a\n // multi-call node replaces the whole assistant message, which would orphan\n // the sibling calls' results (their call ids vanish with the node).\n const callNodeIds = toolCallIdsOfEvent(session.events[callSeq]!)\n if (callNodeIds.length !== 1 || callNodeIds[0] !== callId) return false\n let resolvedResultSeq = resultSeq ?? null\n if (resolvedResultSeq === null) {\n for (const event of session.events) {\n if (event.type === 'tool/result' && toolCallIdOfResultEvent(event) === callId) {\n resolvedResultSeq = event.seq\n break\n }\n }\n }\n if (resolvedResultSeq === null) return false\n const nodes = session.surface.nodes\n const startIdx = nodes.indexOf(callSeq)\n const endIdx = nodes.indexOf(resolvedResultSeq)\n // Only hide an actually adjacent pair; never shadow unrelated messages that\n // happen to sit between a stale call and result.\n if (startIdx < 0 || endIdx < 0 || endIdx - startIdx !== 1) return false\n const { provider, model } = assistantProviderModel(session.events[callSeq]!)\n const resultEvent = session.events[resolvedResultSeq]\n const resultText = resultEvent === undefined ? '' : extractEventText(resultEvent)\n hideSurfaceSeqs(session, [callSeq, resolvedResultSeq], provider, model, resultText.trim().length > 0 ? resultText : undefined)\n return true\n}\n\n/**\n * Surface-level orphan cleanup: hide tool/result nodes with no matching call,\n * assistant tool-call nodes whose calls all lack results, and \"broken pairs\"\n * whose result is NOT adjacent to the call node on the surface (a\n * non-tool/result node — typically the compaction summary a buggy older\n * version inserted between a compress call and its result — sits between\n * them). A single orphan result corrupts the whole tool-pairing balance cache\n * (every range resolve throws), orphan calls fragment large ranges into tiny\n * uncompressed fragments, and a broken pair cannot serialize for strict\n * providers — the mechanisms behind issue #18's \"only ~28 tokens visible\".\n * Uses the same durable prune protocol as `hideSurfaceSeqs`, so the removed\n * nodes stay recoverable from the append-only log.\n */\nexport function stripOrphanedSurfaceToolMessages(\n session: Session,\n inFlightCallIds: ReadonlySet<string> = new Set(),\n): number {\n const nodes = session.surface.nodes\n const callIdsBySeq = new Map<number, string[]>()\n // callId -> surface position of the assistant node carrying it, for calls\n // whose result has not been decided yet.\n const open = new Map<string, { seq: number; index: number }>()\n const orphanResultSeqs: number[] = []\n // result seq -> call node seq, for pairs whose result landed but is not\n // adjacent to the call node on the surface.\n const brokenResults = new Map<number, number>()\n for (let index = 0; index < nodes.length; index += 1) {\n const seq = nodes[index]!\n const event = session.events[seq]\n if (event === undefined) continue\n if (event.type === 'assistant/message') {\n const ids = toolCallIdsOfEvent(event)\n if (ids.length === 0) continue\n callIdsBySeq.set(seq, ids)\n for (const id of ids) {\n if (!open.has(id)) open.set(id, { seq, index })\n }\n } else if (event.type === 'tool/result') {\n const id = toolCallIdOfResultEvent(event)\n if (id === null) continue\n const call = open.get(id)\n if (call === undefined) {\n orphanResultSeqs.push(seq)\n continue\n }\n // A pair is healthy only when every node between the call and this\n // result is a tool/result of the SAME call node (multi-call messages).\n // Any other node in between makes the pair unserializable for strict\n // providers: prune both ends.\n const callNodeIds = callIdsBySeq.get(call.seq)\n let adjacent = false\n if (callNodeIds !== undefined) {\n adjacent = true\n for (let mid = call.index + 1; mid < index; mid += 1) {\n const midEvent = session.events[nodes[mid]!]\n if (midEvent === undefined || midEvent.type !== 'tool/result') {\n adjacent = false\n break\n }\n const midId = toolCallIdOfResultEvent(midEvent)\n if (midId === null || !callNodeIds.includes(midId)) {\n adjacent = false\n break\n }\n }\n }\n open.delete(id)\n if (!adjacent) brokenResults.set(seq, call.seq)\n }\n }\n // call node seq -> ids of that node whose result is broken (non-adjacent).\n const brokenIdsByCallSeq = new Map<number, string[]>()\n for (const [resultSeq, callSeq] of brokenResults) {\n const id = toolCallIdOfResultEvent(session.events[resultSeq]!)\n if (id !== null) {\n const list = brokenIdsByCallSeq.get(callSeq) ?? []\n list.push(id)\n brokenIdsByCallSeq.set(callSeq, list)\n }\n }\n const hiddenSet = new Set<number>(orphanResultSeqs)\n for (const resultSeq of brokenResults.keys()) hiddenSet.add(resultSeq)\n for (const [callSeq, ids] of callIdsBySeq) {\n const brokenIds = brokenIdsByCallSeq.get(callSeq)\n // Only hide an assistant node when NONE of its calls are usable: every id\n // must lack a result (open) or have a broken result. A mixed node (some\n // healthy results) must stay so its valid results are not orphaned by\n // hiding the call — and a node carrying an in-flight call can never be\n // pruned, or the pending result lands orphaned.\n const allUnpaired = !ids.some((candidate) => inFlightCallIds.has(candidate))\n && ids.every((candidate) => open.has(candidate) || brokenIds?.includes(candidate) === true)\n if (allUnpaired) hiddenSet.add(callSeq)\n }\n const hidden = [...hiddenSet].sort((a, b) => a - b)\n let count = 0\n for (const seq of hidden) {\n const event = session.events[seq]\n if (event === undefined) continue\n const { provider, model } = assistantProviderModel(event)\n hideSurfaceSeqs(session, [seq], provider, model)\n count += 1\n }\n return count\n}\n\n/**\n * All tool-call ids currently visible on the surface with no matching\n * tool/result yet — the in-flight calls of the current step. Sibling tools\n * called in the same assistant message as `compress` are in-flight too, so\n * `handleCompress` must protect the whole set (not just its own call id) or\n * the sibling call would be pruned as an orphan and its result would land\n * orphaned (HTTP 400 until the next cleanup).\n */\nexport function openToolCallIds(session: Session): Set<string> {\n const open = new Set<string>()\n for (const seq of session.surface.nodes) {\n const event = session.events[seq]\n if (event === undefined) continue\n if (event.type === 'assistant/message') {\n for (const id of toolCallIdsOfEvent(event)) open.add(id)\n } else if (event.type === 'tool/result') {\n const id = toolCallIdOfResultEvent(event)\n if (id !== null) open.delete(id)\n }\n }\n return open\n}\n\n/**\n * Schedule `hideCompressToolPair` on the microtask queue. `session.append`\n * is NOT reentrant: running it synchronously inside a `session/event`\n * listener (while the outer append is still publishing) throws \"session\n * append cannot reenter while another append is being published\" on live,\n * store-attached sessions, and the dispatcher silently swallows the error —\n * so a synchronous hide is a silent no-op in production. A microtask drains\n * after the current append fully publishes and before the agent loop resumes,\n * so the pair is hidden before the next request is built.\n */\nexport function deferCompressPairHide(\n session: Session,\n callId: string,\n resultSeq: number,\n onError?: (error: unknown) => void,\n): void {\n queueMicrotask(() => {\n try {\n hideCompressToolPair(session, callId, resultSeq)\n } catch (error) {\n onError?.(error)\n }\n })\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 // Orphan tool messages corrupt the pairing balance cache and fragment every\n // large span. Prune them before scanning so the range table reflects the\n // actually compressible surface (issue #18).\n stripOrphanedSurfaceToolMessages(session)\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 // Surface nodes are NOT guaranteed to be ordered: a compaction replace lands\n // the checkpoint node first, so [15, 6, 7, …]. Report the span as min..max\n // rather than first..last, which would read \"seqs 15..12\" after a compress.\n let first = nodes[0]!\n let last = nodes[0]!\n for (const seq of nodes) {\n if (seq < first) first = seq\n if (seq > last) last = seq\n }\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/**\n * Resolve a kernel block ref (`bN`) — as shown by the model tool `acp_status`\n * (kernel `buildStatusReport` renders `block.blockId`) — to the durable\n * compaction id the decompress/search tools accept. Returns null when `bN` is\n * not an exact registry key (unknown ref). Only matches the canonical `bN`\n * form (`/^b\\d+$/`); anything else is not a kernel ref and returns null so the\n * caller falls back to its compaction-id prefix match.\n */\nexport function blockIdOfKernelRef(session: Session, kernelRef: string): string | null {\n if (!/^b\\d+$/.test(kernelRef)) return null\n const entry = blockRegistry(session).find((r) => r.kernelBlockId === kernelRef)\n return entry?.blockId ?? null\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 * The tool-call id of one tool/result surface message, or null.\n *\n * Real DSH tool-result events carry NO `message.toolCallId` (hard-won rule\n * 10): the identity lives in the nested `{ type: 'tool-result', toolCallId }`\n * content block, falling back to `message.source.callId`. Shared with\n * `src/region.ts`'s call/result pairing — one implementation, never a copy.\n */\nexport function toolCallIdOfResultEvent(event: SessionEvent): string | null {\n if (event.type !== 'tool/result') return null\n const message = (event.data as {\n message?: { content?: Array<{ type?: unknown; toolCallId?: unknown }>; source?: { callId?: unknown } }\n }).message\n const block = Array.isArray(message?.content)\n ? message.content.find((candidate) => candidate?.type === 'tool-result')\n : undefined\n const id = block?.toolCallId ?? message?.source?.callId\n return typeof id === 'string' ? id : null\n}\n\n/**\n * Index of assistant tool-call `id` → tool `name`, used to attribute\n * tool/result messages to their tool. Real DSH tool-results carry no\n * `message.toolName` (rule 10), so the projection backfills it from the\n * matching assistant tool-call. Scans ALL events up front (order-independent:\n * a result may precede its call in the array) and covers shadowed calls too.\n */\nexport function buildToolCallIndex(events: readonly SessionEvent[]): ReadonlyMap<string, string> {\n const index = new Map<string, string>()\n for (const event of events) {\n if (event.type !== 'assistant/message') continue\n const content = (event.data as { message?: { content?: unknown } }).message?.content\n if (!Array.isArray(content)) continue\n for (const block of content) {\n const candidate = block as { type?: unknown; id?: unknown; name?: unknown } | null\n if (candidate !== null && typeof candidate === 'object' && candidate.type === 'tool-call' && typeof candidate.id === 'string') {\n index.set(candidate.id, typeof candidate.name === 'string' ? candidate.name : '')\n }\n }\n }\n return index\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 (role 'tool'); toolName/toolCallId are\n * backfilled from `toolNames` (assistant tool-call\n * index) — real DSH events do not carry them at the\n * message level. Without an index the result stays\n * untagged (`toolName: ''`), never \"text\".\n * Non-surface events project to nothing.\n */\nexport function projectEvent(event: SessionEvent, toolNames?: ReadonlyMap<string, string>): 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 const key = toolCallIdOfResultEvent(event)\n return [{\n id: String(event.seq),\n role: 'tool',\n contentType: 'tool-result',\n toolName: toolNames?.get(key ?? '') ?? '',\n toolCallId: message?.toolCallId ?? key ?? '',\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[], toolNames?: ReadonlyMap<string, string>): CoreMessage[] {\n const index = toolNames ?? buildToolCallIndex(events)\n const out: CoreMessage[] = []\n for (const event of events) out.push(...projectEvent(event, index))\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 { buildStatusReport, defaultCountTokens, searchBlocks, type CompressionCore, type MessageRole, type SearchDoc } from 'acp-kernel'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport type { Session, SessionEvent } from '@deepseek-ai/dsh-session'\nimport type { AcpStateStore } from './state.ts'\nimport { kernelConfigFor, type KernelConfigInput } from './config.ts'\nimport { resolveTokenCount } from './nudge.ts'\nimport type { AcpWindow } from './window.ts'\nimport {\n AlreadyCompressedRangeError,\n blockIdOfKernelRef,\n blockRefForSummarySeq,\n compactionIdsOfKernelBlocks,\n expandShadowedSeqs,\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n stripOrphanedSurfaceToolMessages,\n openToolCallIds,\n surfaceSummary,\n type ResolvedSurfaceRange,\n} from './region.ts'\nimport { allLogMessages, buildToolCallIndex, eventsToCoreMessages, extractEventText, surfaceEventsOf } from './messages.ts'\nimport { DEFAULT_RESOLVED, type ResolvedPrompts } from './prompts.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 /** Resolved prompt templates (optional: falls back to DEFAULT_RESOLVED). */\n readonly prompts?: ResolvedPrompts\n /**\n * Call ids of compress invocations that created a durable block. The engine\n * listens for the matching `tool/result` and hides the call/result pair from\n * the surface, preventing the compaction summary from sitting between them\n * (strict providers reject that sequence with HTTP 400).\n */\n readonly compressCallIdsToHide?: Set<string>\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 // Tolerated wrapped-arguments form: some models emit\n // `{ \"arguments\": \"{\\\"content\\\": [...]}\" }` (double-nested) or\n // `{ \"arguments\": { \"content\": [...] } }` instead of the unwrapped\n // `{ \"content\": [...] }`. The old DSH validator surfaced this as\n // `invalid arguments: \"arguments\" must be an object` and the model retried\n // forever. `arguments` is accepted as an optional JSON node so the wrapped\n // shape passes schema validation; `handleCompress` unwraps it and falls back\n // to a clear runtime error when neither form carries content. `content` is\n // intentionally NOT `required: true` — a required property would reject the\n // wrapped shape before `handleCompress` can see it. The tool description\n // still tells the model content is mandatory.\n arguments: { type: 'json', description: 'Tolerated wrapped-arguments form (model-generated); unwrapped in handleCompress. Prefer passing content directly.' },\n topic: { type: 'string' as const, description: 'Fallback topic for entries without their own.' },\n content: {\n type: 'array' as const,\n description: 'One or more ranges to compress, each with startSeq/endSeq boundaries (surface seqs) and a dense summary. Required — pass it directly, not wrapped in an arguments key.',\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 /** Tolerated wrapped-arguments form (model-generated double-nesting). */\n arguments?: string | { content?: CompressArgs['content'] }\n topic?: string\n content?: Array<{ startSeq: number | string; endSeq: number | string; summary: string; topic?: string }>\n}\n\n/**\n * Unwrap the tolerated wrapped-arguments forms back to the canonical shape:\n * `{ arguments: \"{\\\"content\\\": [...]}\" }` or `{ arguments: { content: [...] } }`\n * → `{ content: [...] }`. The direct `{ content: [...] }` form passes through\n * untouched. Returns null when no form carries content (caller raises).\n */\nfunction unwrapCompressArgs(args: CompressArgs): CompressArgs | null {\n if (args.content !== undefined) return args\n if (args.arguments === undefined) return null\n let inner: unknown = args.arguments\n if (typeof inner === 'string') {\n try {\n inner = JSON.parse(inner)\n } catch {\n return null\n }\n }\n if (typeof inner !== 'object' || inner === null || Array.isArray(inner)) return null\n const content = (inner as { content?: unknown }).content\n if (content === undefined) return null\n return { ...args, content: content as CompressArgs['content'] }\n}\n\n/**\n * Peel the tolerated wrapped-arguments envelope `{ arguments: {…} }` that some\n * model channels emit for ANY tool — the same double-nesting that birthed\n * `unwrapCompressArgs` (live-verified on acp_status: a drilldown call arrived\n * as `{\"arguments\":{\"scope\":\"compressed\"}}` and was silently dropped, since\n * only compress unwrapped). The envelope may be an object or a JSON string;\n * inner keys win over outer duplicates. Args without an envelope pass through\n * untouched.\n */\nfunction unwrapEnvelope<T extends object>(args: T): T {\n const envelope = (args as { arguments?: unknown }).arguments\n if (envelope === undefined) return args\n let inner: unknown = envelope\n if (typeof inner === 'string') {\n try {\n inner = JSON.parse(inner)\n } catch {\n return args\n }\n }\n if (typeof inner !== 'object' || inner === null || Array.isArray(inner)) return args\n return { ...args, ...(inner as object) } as T\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 // Clean orphan tool messages before any range solve: a single orphan result\n // corrupts the pairing balance cache and rejects every large range (issue\n // #18). Every call still in flight — the compress call itself AND any\n // sibling tool called in the same assistant message — must be excluded from\n // orphan pruning: its tool/result lands at the end of the step, and pruning\n // the call now would orphan that result.\n stripOrphanedSurfaceToolMessages(session, openToolCallIds(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 uses the same priority chain as the\n // nudge (projectedTokens → surfaceTokens → character heuristic).\n const coreMessages = allLogMessages(session)\n const surfaceMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const tokenCount = resolveTokenCount(agent, surfaceMessages)\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 // Tolerate the wrapped-arguments forms some models emit (double-nested\n // `{ arguments: \"...\" }`), which the old DSH validator surfaced as\n // `\"arguments\" must be an object` and sent the model into a retry loop.\n const unwrapped = unwrapCompressArgs(args)\n if (unwrapped === null) {\n return {\n text: 'compress: missing content — pass the content array directly: compress({ content: [{ startSeq, endSeq, summary }] })',\n }\n }\n args = unwrapped\n\n const ranges: Array<\n ResolvedSurfaceRange & {\n startSeq: number\n endSeq: number\n startRef: string\n endRef: string\n summary: string\n topic?: string\n }\n > = []\n // Ranges whose whole span was already shadowed by earlier compressions.\n // They land as advisory warnings, never as errors or phantom blocks.\n const alreadyCompressedNotes: string[] = []\n for (const range of args.content!) {\n const startSeq = parseSeq(range.startSeq)\n const endSeq = parseSeq(range.endSeq)\n let resolved: ResolvedSurfaceRange\n try {\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 // Edges shadowed by an earlier compression (stale nudge table / old\n // compress result) are remapped to the still-live content of the span.\n resolved = resolveSurfaceRange(session, startSeq, endSeq)\n } catch (error) {\n if (error instanceof AlreadyCompressedRangeError) {\n const covering = error.coveringBlockIds\n const blockNote = covering.length === 0\n ? ''\n : ` (block ${covering[0]!.slice(0, 8)}${covering.length > 1 ? ` +${covering.length - 1} more` : ''})`\n alreadyCompressedNotes.push(\n ` seqs ${error.start}..${error.end} already compressed${blockNote} — nothing to reclaim; decompress to recover the originals`,\n )\n continue\n }\n throw error\n }\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, resolved.start)\n const endBlockRef = blockRefForSummarySeq(session, resolved.end)\n const startRef = startBlockRef ?? byRaw[String(resolved.start)]\n const endRef = endBlockRef ?? byRaw[String(resolved.end)]\n if (startRef === undefined || endRef === undefined) {\n throw new Error(\n `billion-context-dsh: seq ${resolved.start}..${resolved.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 ranges.push({\n ...resolved,\n startSeq,\n endSeq,\n startRef,\n endRef,\n summary: range.summary,\n ...(range.topic ?? args.topic) === undefined ? {} : { topic: range.topic ?? args.topic },\n })\n }\n\n // Nothing to do: every requested range was already compressed.\n if (ranges.length === 0) {\n const text = ['Compressed 0 block(s), ~0 tokens reclaimed.', ...alreadyCompressedNotes]\n if (alreadyCompressedNotes.length > 0) {\n text.push(' (all requested ranges were already compressed — decompress a block to recover its originals)')\n }\n return { text: text.join('\\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 // A kernel error for ONE range must not poison the whole call: the other\n // ranges still created blocks. This matters for issue #18's \"phantom range\"\n // — messages absorbed into an earlier block's effectiveMessageIds (kernel\n // boundary adjustment) but still live on the surface resolve fine but make\n // the kernel throw \"Range contains no compressible messages\". Fail only\n // when NOTHING landed; otherwise land the successes and surface the\n // failures as advisory lines below.\n if (applied.result.errors.length > 0 && applied.result.blocksCreated === 0) {\n return { text: `compress failed: ${applied.result.errors.join('; ')}` }\n }\n env.store.set(session, applied.state)\n if (applied.result.blocksCreated > 0) {\n // Hide this compress call/result after the tool result lands, so the\n // compaction summary never sits between an assistant tool_calls block and\n // its tool response (strict providers reject that sequence).\n env.compressCallIdsToHide?.add(exec.callId)\n }\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 (const range of ranges) {\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: range.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 const note = range.recovered === true\n ? ` (seqs ${range.startSeq}..${range.endSeq} were already shadowed — compressed the live remainder ${start}..${end})`\n : adjusted\n ? ` (adjusted from ${range.startSeq}..${range.endSeq} to balanced edges)`\n : ''\n lines.push(\n ` block ${compactionId.slice(0, 8)}: seqs ${start}..${end}, ${shadowed.length} messages shadowed${tierLabel}${note}`,\n )\n }\n\n const summaryLine = `Compressed ${applied.result.blocksCreated} block(s), ~${applied.result.tokensCompressed} tokens reclaimed.`\n const totalSkipped = skippedRanges + alreadyCompressedNotes.length\n const failedLines = applied.result.errors.map((error) => ` ${error}`)\n const warningLines = [...freeWarnings.map((warning) => ` ${warning}`), ...failedLines, ...alreadyCompressedNotes, ...lines]\n const footer = totalSkipped > 0\n ? ` (${totalSkipped} range(s) skipped or failed — see 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: the kernel block ref `bN` shown by acp_status (e.g. b1), or a compaction id / prefix from search_context.' },\n} as const\n\ninterface DecompressArgs {\n blockId: string\n}\n\n/** Resolve a block arg to its durable compaction id: exact `bN` kernel ref\n * first (acp_status shows `bN`), then the compaction-id prefix match that\n * search_context and /acp have always used. The `bN` branch is exact\n * (`/^b\\d+$/` with `$`), so a UUID that happens to start with `b1` cannot be\n * shadowed — full UUIDs and 8-char prefixes never match the anchored regex. */\nfunction resolveBlockId(session: Session, arg: string): string | null {\n const byKernelRef = blockIdOfKernelRef(session, arg)\n if (byKernelRef !== null) return byKernelRef\n const ledger = rebuildBlockLedger(session.events)\n const byPrefix = ledger.find((entry) => entry.blockId.startsWith(arg))\n return byPrefix?.blockId ?? null\n}\n\nfunction handleDecompress(_env: ToolEnvironment, rawArgs: DecompressArgs, exec: ToolRunContext): TextOutput {\n const args = unwrapEnvelope<DecompressArgs>(rawArgs)\n const session = requireAgent(exec).session\n const blockId = resolveBlockId(session, args.blockId)\n if (blockId === null) {\n return { text: `decompress: block \"${args.blockId}\" not found (see acp_status for the block list)` }\n }\n const ledger = rebuildBlockLedger(session.events)\n const block = ledger.find((entry) => entry.blockId === 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\n/** Event type → kernel message role (drives hybrid role weighting). */\nfunction roleOfEvent(event: SessionEvent): MessageRole | null {\n switch (event.type) {\n case 'user/message': return 'user'\n case 'assistant/message': return 'assistant'\n case 'tool/result': return 'tool'\n default: return null\n }\n}\n\n/**\n * Build the unified SearchDoc[] from the log: one block doc per ledger entry\n * (ref = compactionId, so `decompress({ blockId })` closes the loop) plus one\n * message doc per shadowed ORIGINAL (expanded through distilled parents; each\n * seq is claimed by the earliest/innermost block that covered it, mirroring\n * pi's owner map — decompress on that block recovers the original).\n */\nfunction buildSearchDocs(session: Session): SearchDoc[] {\n const ledger = rebuildBlockLedger(session.events)\n const docs: SearchDoc[] = []\n const claimed = new Set<number>()\n for (const block of ledger) {\n docs.push({\n kind: 'block',\n ref: block.blockId,\n text: block.summary,\n title: block.summary.slice(0, 60) || block.blockId,\n blockId: block.blockId,\n tier: block.tier,\n tokens: defaultCountTokens(block.summary),\n })\n for (const seq of expandShadowedSeqs(session, block.blockId)) {\n if (claimed.has(seq)) continue\n claimed.add(seq)\n const event = session.events[seq]\n if (event === undefined) continue\n const role = roleOfEvent(event)\n const text = extractEventText(event)\n if (role === null || text.length === 0) continue\n docs.push({\n kind: 'message',\n ref: `seq ${seq}`,\n text,\n title: `${role}: ${text.slice(0, 60)}`,\n role,\n blockId: block.blockId,\n tier: block.tier,\n tokens: defaultCountTokens(text),\n })\n }\n }\n return docs\n}\n\nfunction handleSearch(_env: ToolEnvironment, rawArgs: SearchArgs, exec: ToolRunContext): TextOutput {\n const args = unwrapEnvelope<SearchArgs>(rawArgs)\n const session = requireAgent(exec).session\n if (args.query.trim() === '') return { text: 'search_context: empty query (no matches)' }\n const docs = buildSearchDocs(session)\n // Trust the kernel: hybrid (0.7×BM25 stemmed + 0.3×fuzzy n-gram) is the\n // algorithm contract — no engine-side gate or threshold re-implements\n // search policy. Scores are surfaced so the model can judge a weak hit\n // (fuzzy-only tops out near 0.3).\n const results = searchBlocks(docs, args.query, { limit: args.limit ?? 5, previewLength: 160 })\n if (results.length === 0) return { text: `search_context: no matches for \"${args.query}\"` }\n const lines = results.map((r) => {\n const kind = r.kind === 'block' ? `block ${r.ref}` : `message ${r.ref} (${r.role ?? '?'}, in block ${r.blockId ?? '?'})`\n return ` - ${kind} (score ${r.score.toFixed(2)}): ${r.preview}`\n })\n return {\n text: `Matches for \"${args.query}\":\\n${lines.join('\\n')}\\n\\nDecompress with: decompress({ blockId })`,\n }\n}\n\n/** acp_status drilldown passthrough (kernel buildStatusReport options). All\n * keys optional — no args = overview. `view`/`tool`/`sort`/`limit` only have\n * meaning under `scope:\"uncompressed\"` (`tool` narrows to `view:\"messages\"`;\n * `sort:\"age\"` applies to `scope:\"compressed\"`); the kernel ignores them in\n * overview mode (upstream status-tool docstring documented the same scope).\n * DSH schema compiler: `string` + `enum` supported, no `required: true`\n * anywhere → all optional (schema.js:192-210). */\nconst statusParameters = {\n scope: {\n type: 'string' as const,\n enum: ['compressed', 'uncompressed'] as const,\n description: 'Drilldown scope: \"compressed\" lists compressed blocks, \"uncompressed\" lists visible messages. Omit for the overview.',\n },\n view: {\n type: 'string' as const,\n enum: ['ranges', 'messages'] as const,\n description: 'Drilldown view under scope:\"uncompressed\": \"ranges\" merges visible messages into ranges (default), \"messages\" lists every message.',\n },\n tool: {\n type: 'string' as const,\n description: 'Filter drilldown rows to one tool name (scope:\"uncompressed\" + view:\"messages\" only).',\n },\n sort: {\n type: 'string' as const,\n enum: ['size', 'time', 'tool', 'age'] as const,\n description: 'Row order: size (default, most tokens first), time, tool; \"age\" applies to compressed blocks.',\n },\n limit: {\n type: 'integer' as const,\n description: 'Cap on rows or blocks shown (default 30).',\n },\n}\n\ninterface StatusArgs {\n scope?: 'compressed' | 'uncompressed'\n view?: 'ranges' | 'messages'\n tool?: string\n sort?: 'size' | 'time' | 'tool' | 'age'\n limit?: number\n}\n\n/** A compaction checkpoint summary node (`source.plugin === 'compact'`). These\n * are NOT in any block's `effectiveMessageIds`, so feeding them to\n * `buildStatusReport` would double-count the summary — once as `block.summary`\n * (summaryTokens) and once as a visible text message (totalText). Excluded\n * before status rendering (design §4.2 P1-3). */\nfunction isCheckpointEvent(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\nasync function handleStatus(env: ToolEnvironment, rawArgs: StatusArgs, exec: ToolRunContext): Promise<TextOutput> {\n // The model channel may wrap ANY tool's args under `{ arguments: {…} }`;\n // peel it or drilldown params never reach buildStatusReport (live-verified\n // `{\"arguments\":{\"scope\":\"compressed\"}}` silently rendered the overview).\n const args = unwrapEnvelope<StatusArgs>(rawArgs)\n const agent = requireAgent(exec)\n const session = agent.session\n const state = env.store.stateFor(session)\n const surface = surfaceEventsOf(session)\n // One tool-call index for both projections below (P2-5): tool/result\n // toolName/toolCallId are backfilled from the assistant tool-calls.\n const toolNames = buildToolCallIndex(surface)\n const coreMessages = allLogMessages(session)\n const surfaceMessages = eventsToCoreMessages(surface, toolNames)\n const tokenCount = resolveTokenCount(agent, surfaceMessages)\n const config = kernelConfigFor(env)\n // Run the same pipeline the context transform runs, so what acp_status\n // reports matches what the model actually receives. The returned turn.state\n // carries the freshly assigned refs; it is NOT persisted — acp_status is a\n // read-only view, and env.store.set would advance the nudge baseline a\n // second time in the same turn (design §6.1 P2-2).\n const turn = env.kernel.processTurn({ messages: coreMessages, state, config, tokenCount })\n // Status messages = visible surface EXCLUDING checkpoint summary nodes (P1-3).\n const statusMessages = eventsToCoreMessages(\n surface.filter((event) => !isCheckpointEvent(event)),\n toolNames,\n )\n // Upstream-aligned: the kernel renders the breakdown (percentages of the\n // VISIBLE total — no window semantics; drilldown scope/view/tool/sort/limit\n // pass through verbatim); the engine only appends the nudge decision line,\n // the DSH Surface anchor, and — in drilldown mode — the mN-vs-seq note.\n const report = buildStatusReport(turn.state, statusMessages, defaultCountTokens, args)\n const lines = [report]\n // Mirror upstream pi (`if (args.scope) return base`): a drilldown request\n // answers with the kernel report alone — the nudge decision line is an\n // overview concept. The Surface anchor stays in ALL modes: it is the model's\n // compressible-ref locator (design P2-1).\n if (args.scope === undefined) {\n const nudge = turn.nudge\n if (nudge !== undefined) {\n lines.push('', `Nudge: ${nudge.shouldInject ? 'ACTIVE' : 'idle'} — ${nudge.reason}`)\n }\n }\n lines.push('', `Surface: ${surfaceSummary(session)}`)\n // Drilldown rows carry kernel refs (mN, dense log-order ids). The model must\n // NOT feed them to compress — compress speaks surface seqs, and the Surface\n // anchor above is the only compressible-ref source (design §9 P2-3). Slated\n // for direct seq adaptation when the search_context seq dialect lands (#23).\n if (args.scope === 'uncompressed') {\n lines.push('', 'Note: drilldown rows are kernel refs (mN) for size awareness — compress uses the Surface: seqs above, never mN.')\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 const prompts = env.prompts ?? DEFAULT_RESOLVED\n return [\n defineTool({\n name: 'compress',\n description: prompts.tools.compress,\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: prompts.tools.decompress,\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: prompts.tools.searchContext,\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: prompts.tools.acpStatus,\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 * 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 COMPRESS_PHILOSOPHY,\n TIER2_DISTILL_RULES,\n TIER3_CONDENSE_RULES,\n defaultCountTokens,\n renderNudgeText,\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'\nimport { DEFAULT_RESOLVED, renderTemplate, type ResolvedPrompts } from './prompts.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 /** Resolved prompt templates (optional: falls back to DEFAULT_RESOLVED). */\n readonly prompts?: ResolvedPrompts\n}\n\nexport interface NudgeOutcome {\n readonly message: UserMessage\n readonly emergency: boolean\n}\n\n/**\n * Resolve the best available token count for ACP pressure decisions.\n *\n * Priority chain:\n * 1. `sessionProjections.contextPressure.projectedTokens` — matches the UI's\n * context-occupancy display (includes fixed overhead: system prompt, tool\n * definitions, AGENTS.md, etc.). Provider-anchored; reacts to compaction.\n * 2. `tokenMeter.measure(session).surfaceTokens` — heuristic surface-only\n * estimate (pure conversation messages, no fixed overhead). Falls back\n * when sessionProjections is unavailable or has no provider anchor yet.\n * 3. `defaultCountTokens` character heuristic — last resort for tests and\n * minimal hosts that lack the token-meter service.\n */\nexport function resolveTokenCount(agent: Agent, coreMessages: CoreMessage[]): number {\n // 1. Prefer sessionProjections.contextPressure.projectedTokens (matches UI).\n const projections = agent.ctx?.get?.('sessionProjections') as\n | { snapshot?: (session: unknown) => { values?: { contextPressure?: { projectedTokens?: number } } } }\n | undefined\n const projected = projections?.snapshot?.(agent.session)?.values?.contextPressure?.projectedTokens\n if (typeof projected === 'number' && projected > 0) return projected\n\n // 2. Fallback to tokenMeter surfaceTokens (heuristic, no fixed overhead).\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\n // 3. Last resort: character heuristic.\n return coreMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\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(\n session: import('@deepseek-ai/dsh-session').Session,\n prompts: ResolvedPrompts = DEFAULT_RESOLVED,\n): string {\n const ranges = buildCompressibleSeqRanges(session).slice(0, 6)\n // 零范围:整块省略(保留现状的提前返回与 nudge 尾部 '\\n')。\n if (ranges.length === 0) return ''\n const lines = ranges.map((range) =>\n renderTemplate(prompts.rangeTable.line, {\n start: range.start,\n end: range.end,\n count: range.count,\n tokens: range.tokens,\n }),\n )\n return [\n // 前导空串元素产生 nudge 中范围表前的唯一空行(§4:parts 层不再加分隔)。\n '',\n renderTemplate(prompts.rangeTable.header, { surface: surfaceSummary(session) }),\n renderTemplate(prompts.rangeTable.title, { count: ranges.length }),\n ...lines,\n prompts.rangeTable.footer,\n ].join('\\n')\n}\n\n/**\n * The token count driving pressure decisions. Prefer `resolveTokenCount` which\n * uses `sessionProjections.contextPressure.projectedTokens` (matches the UI's\n * context-occupancy display, including fixed overhead). Falls back to\n * `tokenMeter.measure(session).surfaceTokens`, then `defaultCountTokens`\n * character heuristic for tests and minimal hosts.\n */\nfunction measuredTokenCount(agent: Agent, coreMessages: CoreMessage[]): number {\n return resolveTokenCount(agent, coreMessages)\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, env.prompts)\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 * Render the nudge message text. DEFAULT (no `config.prompts.nudge` override)\n * calls the kernel's own `renderNudgeText` — EFFICIENCY_NOTE/EMERGENCY_HEADER,\n * context breakdown, HOW_TO_COMPRESS_RULES, tier rules, and the batch tip all\n * come from acp-kernel verbatim (the kernel-alignment principle). Only the\n * ref-ID-oriented segments are replaced with our seq-based equivalents,\n * because DSH has no `<acp>` ref tags — see docs/dsh-porting-verification.md:\n * - `rangesStr` (mNNNNN refs) → the surface-seq range table;\n * - the emergency JSON example (startId/endId) → a seq example;\n * - the tier trigger block (block ids bN) → our tier line with surface seqs.\n * When a host overrides any `prompts.nudge` slot, the template path is used so\n * `config.prompts` keeps full control (custom copy wins over kernel defaults).\n */\nexport function buildNudgeText(\n nudge: NudgeDecision,\n emergency: boolean,\n session: import('@deepseek-ai/dsh-session').Session,\n prompts: ResolvedPrompts = DEFAULT_RESOLVED,\n): string {\n // A host override of any nudge slot → template rendering (config.prompts\n // keeps its v0.1.9 contract: custom copy wins). Only the pristine default\n // reference reaches the kernel path.\n if (prompts.nudge !== DEFAULT_RESOLVED.nudge) {\n return renderNudgeFromTemplates(nudge, emergency, session, prompts)\n }\n const rendered = renderNudgeText(nudge)\n return adaptKernelNudgeToSeq(rendered.text, nudge, session, prompts)\n}\n\n/**\n * Take the kernel-rendered nudge text and replace its ref-ID-oriented segments\n * with our surface-seq equivalents. Everything else (frame, philosophy,\n * breakdown, HOW_TO_COMPRESS_RULES, tier rules, tip) stays kernel verbatim.\n */\nfunction adaptKernelNudgeToSeq(\n text: string,\n nudge: NudgeDecision,\n session: import('@deepseek-ai/dsh-session').Session,\n prompts: ResolvedPrompts,\n): string {\n let out = text\n // Tier nudges: replace the kernel trigger block (block ids bN) with our tier\n // line carrying surface seqs. The kernel's TIER2/3 rules stay in the tail.\n if ((nudge.tier === 2 || nudge.tier === 3) && (nudge.tierTargetBlocks?.length ?? 0) > 0) {\n out = replaceTierTrigger(out, nudge, session, prompts)\n } else if (out.includes('\"startId\"')) {\n // Emergency nudges: replace the ref-ID JSON example with a seq example.\n out = replaceEmergencyExample(out)\n }\n // Replace the ref-ID range table (mNNNNN) with the surface-seq table.\n // A zero-range table leaves the kernel's own \"[No specific ranges detected]\"\n // notice intact — it is a better prompt than an empty table.\n const seqTable = rangeTable(session, prompts)\n if (seqTable !== '') out = replaceRangesStr(out, seqTable)\n return out\n}\n\n/** Replace the kernel rangesStr segment (`Compressible ranges (N, oldest first):…`) with our seq table. */\nfunction replaceRangesStr(text: string, seqTable: string): string {\n const match = text.match(/\\n\\n(?:Compressible ranges \\(|\\[No specific ranges detected)/)\n if (!match) return text\n const start = match.index!\n const rest = text.slice(start + 2)\n const next = rest.match(/\\n\\n/)\n const end = next !== null ? start + 2 + next.index! : text.length\n const before = text.slice(0, start)\n const after = text.slice(end)\n // seqTable starts with '\\n' (the range table's leading blank line), so\n // `before` + '\\n' + seqTable yields one blank line before the table.\n return before + '\\n' + seqTable + after\n}\n\n/** Replace the kernel tier trigger segment (`[TIER n …TRIGGER]…Example: compress(…)`) with our tier line. */\nfunction replaceTierTrigger(\n text: string,\n nudge: NudgeDecision,\n session: import('@deepseek-ai/dsh-session').Session,\n prompts: ResolvedPrompts,\n): string {\n const start = text.search(/\\n\\n(?:\\[TIER \\d|\\[EMERGENCY — TIER \\d)/)\n if (start === -1) return text\n const rest = text.slice(start + 2)\n const next = rest.match(/\\n\\nHOW TO COMPRESS/)\n const end = next !== null ? start + 2 + next.index! : text.length\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 const tierValue = nudge.tier === null ? 2 : nudge.tier\n const tierLine = renderTemplate(prompts.nudge.tier, {\n tier: tierValue,\n count: targets.length,\n prevTier: tierValue - 1,\n tokens,\n seqs: summarySeqs.join(', '),\n })\n return text.slice(0, start) + '\\n\\n' + tierLine + text.slice(end)\n}\n\n/** Replace the kernel emergency JSON example (startId/endId) with a seq example. */\nfunction replaceEmergencyExample(text: string): string {\n const start = text.search(/\\n\\n\\{ \"topic\":/)\n if (start === -1) return text\n const rest = text.slice(start + 2)\n const next = rest.match(/\\n\\nCompressible ranges |\\n\\n\\[No specific/)\n const end = next !== null ? start + 2 + next.index! : text.length\n return text.slice(0, start)\n + '\\n\\ncompress({ content: [{ startSeq, endSeq, summary }] }) — use the seqs from the range table above.'\n + text.slice(end)\n}\n\n/**\n * Template rendering path (used only when a host overrides a `prompts.nudge`\n * slot). Kept byte-compatible with the pre-refactor assembly: frame → breakdown\n * → growth → guidance → tier(+rules)/range table → tip.\n */\nfunction renderNudgeFromTemplates(\n nudge: NudgeDecision,\n emergency: boolean,\n session: import('@deepseek-ai/dsh-session').Session,\n prompts: ResolvedPrompts,\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 = renderTemplate(\n emergency ? prompts.nudge.emergency : prompts.nudge.normal,\n { pct, philosophy: COMPRESS_PHILOSOPHY },\n )\n const parts: string[] = [frame]\n\n // Context breakdown (kernel style, from NudgeDecision.contextBreakdown).\n if (nudge.contextBreakdown) {\n const bd = nudge.contextBreakdown\n const breakdown = renderTemplate(prompts.nudge.breakdown, {\n system: Math.round(bd.system / 1000),\n tool: Math.round(bd.tool / 1000),\n summaries: Math.round(bd.summaries / 1000),\n code: Math.round(bd.code / 1000),\n text: Math.round(bd.text / 1000),\n })\n if (breakdown !== '') parts.push('', breakdown)\n if (bd.growth > 0) {\n const growth = renderTemplate(prompts.nudge.growth, { growth: Math.round(bd.growth / 1000) })\n if (growth !== '') parts.push(growth)\n }\n }\n\n // HOW_TO_COMPRESS_RULES as guidance (kernel puts it in every nudge).\n if (prompts.nudge.guidance !== '') parts.push('', prompts.nudge.guidance)\n\n // Tier line (distillation / condensation suggestion) + tier-specific rules.\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 const tierLine = renderTemplate(prompts.nudge.tier, {\n tier: nudge.tier,\n count: targets.length,\n prevTier: nudge.tier - 1,\n tokens,\n seqs: summarySeqs.join(', '),\n })\n if (tierLine !== '') parts.push(tierLine)\n // Tier-specific rules from kernel (TIER2_DISTILL_RULES / TIER3_CONDENSE_RULES).\n const tierRules = nudge.tier === 2 ? TIER2_DISTILL_RULES : TIER3_CONDENSE_RULES\n parts.push('', tierRules)\n } else {\n // Range table for non-tier nudges (DSH-specific: seq-based, not ref-ID-based).\n parts.push(rangeTable(session, prompts))\n }\n\n // Batch-compress tip (from kernel's nudge-text.ts style).\n if (prompts.nudge.tip !== '') parts.push('', prompts.nudge.tip)\n\n return parts.join('\\n')\n}\n","/**\n * M4 — configurable prompt templates: the per-stage model-visible texts\n * (nudge frames, range table, system prompt, tool descriptions) rendered from\n * `config.prompts` templates with named placeholders.\n *\n * Design: docs/configurable-prompts-design.md (v4).\n * - placeholders are `{identifier}` only; literal braces like\n * `compress({ content: [...] })` are left untouched (spaces/commas break the\n * identifier rule);\n * - resolvePrompts merges user overrides over DEFAULT_PROMPTS per key\n * (null/undefined → default, string → override; group-level null → whole\n * group default for YAML hosts) and validates unknown placeholders at\n * construction time (fail-fast, no silent typos);\n * - renderTemplate throws when a known placeholder has no value — callers\n * must provide every value (e.g. tokens via a typeof fallback).\n * @module billion-context-dsh/prompts\n */\n\nimport { COMPRESS_PHILOSOPHY, HOW_TO_COMPRESS_RULES, TIER2_DISTILL_RULES, TIER3_CONDENSE_RULES } from 'acp-kernel'\n\n/** 用户可写值:字符串模板,或 null(= 用默认,等价于不写)。YAML 宿主写 null 是合法输入。 */\nexport type PromptInput = string | null\n\n/** 按组生成\"每键可选、可 null\"的覆盖类型。 */\nexport type PromptOverride<T> = { [K in keyof T]?: PromptInput }\n\nexport interface NudgePrompts {\n /** 普通档首句。占位符:{pct} {philosophy} */\n normal: string\n /** 紧急档首句。占位符:{pct} {philosophy} */\n emergency: string\n /** 指导行(HOW_TO_COMPRESS_RULES)。无占位符 */\n guidance: string\n /** tier 蒸馏行。占位符:{tier} {count} {prevTier} {tokens} {seqs} */\n tier: string\n /** 上下文分解。占位符:{system} {tool} {summaries} {code} {text} */\n breakdown: string\n /** 增长行。占位符:{growth} */\n growth: string\n /** 溢出提示。无占位符 */\n tip: string\n}\n\nexport interface RangeTablePrompts {\n /** 表头。占位符:{surface} */\n header: string\n /** 标题。占位符:{count}(表格行数) */\n title: string\n /** 每行。占位符:{start} {end} {count} {tokens} */\n line: string\n /** 表尾调用语法。无占位符 */\n footer: string\n}\n\nexport interface ToolPrompts {\n /** 工具描述(纯文本,无占位符) */\n compress: string\n decompress: string\n searchContext: string\n acpStatus: string\n}\n\nexport interface AcpPrompts {\n readonly nudge?: PromptOverride<NudgePrompts>\n readonly rangeTable?: PromptOverride<RangeTablePrompts>\n readonly tools?: PromptOverride<ToolPrompts>\n /** 整段 system prompt 模板;`{philosophy}` 引用 kernel 的 COMPRESS_PHILOSOPHY */\n readonly systemPrompt?: PromptInput\n}\n\n/** 解析结果 —— 所有字段已填满(纯 string,无 null)、已校验。构造一次,全程复用。 */\nexport interface ResolvedPrompts {\n readonly nudge: NudgePrompts\n readonly rangeTable: RangeTablePrompts\n readonly tools: ToolPrompts\n /** 注意:这是【模板】(含 {philosophy}),不是渲染结果。渲染用 renderSystemPrompt。 */\n readonly systemPromptTemplate: string\n}\n\n/** 每槽允许的占位符名集合(构建期校验用)。 */\nconst NUDGE_ALLOWED: { [K in keyof NudgePrompts]: ReadonlySet<string> } = {\n normal: new Set(['pct', 'philosophy']),\n emergency: new Set(['pct', 'philosophy']),\n guidance: new Set(),\n tier: new Set(['tier', 'count', 'prevTier', 'tokens', 'seqs']),\n breakdown: new Set(['system', 'tool', 'summaries', 'code', 'text']),\n growth: new Set(['growth']),\n tip: new Set(),\n}\nconst RANGE_TABLE_ALLOWED: { [K in keyof RangeTablePrompts]: ReadonlySet<string> } = {\n header: new Set(['surface']),\n title: new Set(['count']),\n line: new Set(['start', 'end', 'count', 'tokens']),\n footer: new Set(),\n}\nconst TOOLS_ALLOWED: { [K in keyof ToolPrompts]: ReadonlySet<string> } = {\n compress: new Set(),\n decompress: new Set(),\n searchContext: new Set(),\n acpStatus: new Set(),\n}\nconst SYSTEM_ALLOWED = new Set(['philosophy', 'howToCompressRules', 'tier2DistillRules', 'tier3CondenseRules'])\n\n/** 校验单个模板:未知 `{ident}` → throw(带槽位路径)。默认模板开发期已核验,不重扫。 */\nfunction validateTemplate(template: string, allowed: ReadonlySet<string>, path: string): string {\n const re = /\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g\n let match: RegExpExecArray | null\n while ((match = re.exec(template)) !== null) {\n const name = match[1]!\n if (!allowed.has(name)) {\n throw new Error(\n `${path} contains unknown placeholder {${name}} — allowed: ${[...allowed].join(', ') || '(none)'}`,\n )\n }\n }\n return template\n}\n\n/**\n * 纯替换。两个契约:\n * 1. 未知占位符不可能到达这里(构建期已校验);\n * 2. 已知占位符缺值 = 编程错误 → throw(绝不静默渲染空串)。\n */\nexport function renderTemplate(template: string, vars: Record<string, string | number>): string {\n return template.replace(/\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_match, name: string) => {\n const value = vars[name]\n if (value === undefined) {\n throw new Error(\n `renderTemplate: missing value for placeholder {${name}} in template \"${template.slice(0, 60)}…\"`,\n )\n }\n return String(value)\n })\n}\n\n/**\n * 逐键合并:null / undefined → 默认;字符串 → 覆盖默认(不用 spread,\n * 否则 null 会覆盖默认,与\"null = 用默认\"矛盾)。组级 null/undefined →\n * 整组用默认(YAML 宿主可能写 `{ nudge: null }`,W3)。\n */\nfunction mergeGroup<T extends Record<keyof T, string>>(\n defaults: T,\n override: PromptOverride<T> | null | undefined,\n allowed: { [K in keyof T]: ReadonlySet<string> },\n path: string,\n): T {\n if (override == null) return defaults\n const out = {} as { [K in keyof T]: string }\n for (const key of Object.keys(defaults) as Array<keyof T>) {\n const value = override[key]\n out[key] = value === null || value === undefined\n ? defaults[key]\n : validateTemplate(value, allowed[key], `${path}.${String(key)}`)\n }\n return out as T\n}\n\n/**\n * 深合并 + 校验;引擎构造期调用一次,出错即抛(fail-fast)。\n * 未传入时返回 DEFAULT_RESOLVED,零校验重跑。\n */\nexport function resolvePrompts(input?: AcpPrompts): ResolvedPrompts {\n if (input === undefined) return DEFAULT_RESOLVED\n return {\n nudge: mergeGroup(DEFAULT_PROMPTS.nudge, input.nudge, NUDGE_ALLOWED, 'prompts.nudge'),\n rangeTable: mergeGroup(DEFAULT_PROMPTS.rangeTable, input.rangeTable, RANGE_TABLE_ALLOWED, 'prompts.rangeTable'),\n tools: mergeGroup(DEFAULT_PROMPTS.tools, input.tools, TOOLS_ALLOWED, 'prompts.tools'),\n systemPromptTemplate:\n input.systemPrompt === null || input.systemPrompt === undefined\n ? DEFAULT_PROMPTS.systemPromptTemplate\n : validateTemplate(input.systemPrompt, SYSTEM_ALLOWED, 'prompts.systemPrompt'),\n }\n}\n\n/** 渲染 system prompt 模板(注入 kernel 压缩哲学、压缩规则、蒸馏规则)。 */\nexport function renderSystemPrompt(prompts: ResolvedPrompts): string {\n return renderTemplate(prompts.systemPromptTemplate, {\n philosophy: COMPRESS_PHILOSOPHY,\n howToCompressRules: HOW_TO_COMPRESS_RULES,\n tier2DistillRules: TIER2_DISTILL_RULES,\n tier3CondenseRules: TIER3_CONDENSE_RULES,\n })\n}\n\n/**\n * 默认模板 —— 与 v4 之前的硬编码文案逐字节一致\n * (回归锚点见 tests/prompts.test.ts 的硬编码字面量快照)。\n */\nexport const DEFAULT_PROMPTS: ResolvedPrompts = {\n nudge: {\n // 与 kernel nudge-text.ts EFFICIENCY_NOTE 逐字对齐——不含 \"Context usage is at X%\"\n // 陈述(usage 只通过 breakdown 传达);{pct} 仍可用作自定义占位符。\n normal: 'This is an efficiency nudge to compress early and keep context lean — not an overflow warning. A separate, stronger alert will appear if the context is actually full.\\n\\n{philosophy}',\n emergency: '⚠️ Context limit reached — compress now. Prioritize consumed tool outputs.\\n\\n{philosophy}',\n guidance: HOW_TO_COMPRESS_RULES,\n tier: 'Tier {tier}: {count} tier-{prevTier} block(s) distillable ({tokens} tokens) — compress their summary node(s) [seqs {seqs}] to reclaim the original messages.',\n breakdown: 'Context breakdown: {system}K system | {tool}K tool | {summaries}K summaries | {code}K code | {text}K text',\n growth: '+{growth}K since last nudge',\n tip: '💡 Compress all ranges in one call (pass multiple content entries: `content: [{...}, {...}]`).',\n },\n rangeTable: {\n header: 'Surface: {surface}',\n title: 'Compressible ranges (suggestions only — compress any consumed span; refs are surface seqs):',\n line: ' - seq {start}..{end} — {count} messages, ~{tokens} tokens',\n footer: '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'\n + 'Snapshot taken at nudge time: the seqs go stale once the surface moves (a later compress shadows them), so re-run acp_status for fresh refs before compressing.',\n },\n tools: {\n compress: 'Replace older conversation ranges with dense summaries you write. Each message seq is a surface reference. Single range: compress({ content: [{ startSeq, endSeq, summary }] }). Batch multiple unrelated ranges in one call (each content entry becomes its own block); keep ranges disjoint. Never compress content the current step is actively using. Compress boundaries are SURFACE SEQS (acp_status Surface: row, latest nudge table) — NOT the block refs (bN, e.g. b1) that acp_status COMPRESSED BLOCKS shows, which are for decompress only. Seq refs must come from the CURRENT surface (acp_status or the latest nudge): a span whose edges were shadowed by an earlier compress is auto-remapped to its still-live content, a fully compressed span is reported as already compressed, and invented/other-session seqs fail with guidance.',\n decompress: 'Recover the original content of a compressed block by its blockId — the kernel block ref `bN` shown by acp_status (e.g. b1), or a compaction id from search_context (read-only; does not unshadow the range).',\n searchContext: 'Search inside compressed blocks (summaries and original content) for information the model no longer sees in context.',\n acpStatus: 'Context status: overview of the current context — CONTEXT BREAKDOWN (tool/text/summaries token shares of the visible total), COMPRESSED BLOCKS ledger, and the nudge decision. No args = overview. Percentages are shares of the visible content, not the context window. Note: the block refs in COMPRESSED BLOCKS (bN, e.g. b1) are for decompress; compress uses the Surface: seq range, not bN. Drilldown: pass scope:\"compressed\" for a per-block list, or scope:\"uncompressed\" with view:\"messages\" (every visible message) / view:\"ranges\" (merged ranges); tool filters to one tool name, sort reorders (size/time/tool; age for compressed), limit caps rows (default 30). Drilldown row refs are kernel ids (mN) for size awareness only — compress always uses the Surface: seqs, never mN.',\n },\n systemPromptTemplate: `Active Context Pruning — model-driven context management\n\nYOU decide whether and when to compress context. The nudge is an efficiency notification: when you see one, consider which ranges you have genuinely consumed and could summarise to keep working context lean.\n\n{philosophy}\n\nWHEN TO COMPRESS:\n- A sub-agent or delegated task has returned a large result that you have already extracted the key facts from.\n- Verbose command output (build/test logs, git diff, directory listings) where you have already used the information you need.\n- Exploration that led nowhere.\n- Repeated reads of the same file or repeated status checks once the decision is recorded.\n- Resolved discussion threads where a decision has been captured in summary or in code.\n- Intermediate steps of a completed multi-step task, once the final result is recorded.\n- A task phase has ended — bug hunt complete, root cause found, exploration done, research sprint wrapped.\n\nWHEN NOT TO COMPRESS:\n- Content the current step is actively reading or reasoning about.\n- Important user messages — preserve their exact intent, constraints, and acceptance criteria.\n- Protected tool outputs — hard-excluded from compression ranges, survive intact in visible context.\n\n{howToCompressRules}\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. Seq refs must be on the current surface: seqs from older nudges or earlier compresses go stale as the surface moves, so a stale span is auto-remapped to its still-live remainder (the result reports the adjusted span), a fully compressed span is reported as already compressed, and invented/other-session seqs fail with guidance. The block refs (bN, e.g. b1) in acp_status COMPRESSED BLOCKS are for decompress, NOT compress boundaries.\n- decompress: recover a compressed block's original content, read-only. decompress({ blockId }) — accept the bN ref shown by acp_status (e.g. b1) or a compaction id.\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 right before compressing — the only seqs that never go stale are the ones you just read. Drilldown (scope/view/tool/sort/limit) lists per-message or per-block sizes; drilldown rows are kernel ids (mN) for size awareness — compress uses seqs, never mN.\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\n{tier2DistillRules}\n\n{tier3CondenseRules}\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\n/** 模块级默认缓存:默认参/兜底直接引用,避免每次调用重跑校验。 */\nexport const DEFAULT_RESOLVED: ResolvedPrompts = DEFAULT_PROMPTS\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 { resolveTokenCount } from './nudge.ts'\nimport {\n blockIdOfKernelRef,\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 = resolveTokenCount(agent, coreMessages)\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 // Accept the kernel block ref (`bN`) the model tool acp_status shows, as\n // well as the compaction-id prefix (same dual-id resolution as the tool).\n const blockId = blockIdOfKernelRef(session, args[0]!)\n const ledger = rebuildBlockLedger(session.events)\n const block = blockId === null\n ? ledger.find((entry) => entry.blockId.startsWith(args[0]!))\n : ledger.find((entry) => entry.blockId === blockId)\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 * 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 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.\n *\n * The text is DEFAULT_PROMPTS.systemPromptTemplate rendered with the kernel's\n * COMPRESS_PHILOSOPHY and HOW_TO_COMPRESS_RULES; hosts can override the whole\n * section via `config.prompts.systemPrompt` (see docs/configurable-prompts-design.md).\n * @module billion-context-dsh/system-prompt\n */\n\nimport { DEFAULT_PROMPTS, renderSystemPrompt } from './prompts.ts'\n\nexport const ACP_SYSTEM_PROMPT = renderSystemPrompt(DEFAULT_PROMPTS)\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","COMPRESS_PHILOSOPHY","TIER2_DISTILL_RULES","TIER3_CONDENSE_RULES","defaultCountTokens","createUserMessage","defaultCountTokens","createUserMessage","COMPRESS_PHILOSOPHY","TIER2_DISTILL_RULES","TIER3_CONDENSE_RULES","defaultCountTokens","defaultCountTokens","defaultCountTokens"]}
|
package/package.json
CHANGED