billion-context-dsh 0.1.0 → 0.1.2

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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/state.ts","../src/tools.ts","../src/config.ts","../src/region.ts","../src/messages.ts","../src/commands.ts","../src/nudge.ts","../src/system-prompt.ts"],"sourcesContent":["/**\n * billion-context-dsh — Active Context Pruning (ACP) for the DeepSeek Harness,\n * delivered as a `CompactionEngine` backend.\n *\n * The model decides when and what to compress (pure ACP semantics):\n * - the `compress` tool durably shadows a surface range with the model-written\n * summary (no second LLM summarization call — the ACP cost win);\n * - the original events stay in the append-only session log, so `decompress`,\n * `search_context`, and replay always work;\n * - refs are surface seqs carried by the injected nudge's range table (DSH\n * has no in-memory message rewrite hook — see docs/dsh-porting-verification.md);\n * - automatic policy never summarizes by itself: it nudges the model.\n *\n * Mount it wherever a compaction backend is expected:\n *\n * ```yaml\n * - id: compaction-billion-context\n * name: 'billion-context-dsh'\n * config:\n * modelContextLimit: 128000\n * ```\n *\n * The package registers `ctx.compaction` plus the four model tools and the\n * `/acp` command when the hosting composition provides `ctx.tools` /\n * `ctx.commands`.\n * @module billion-context-dsh\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport {\n CompactionEngine,\n ManualCompactionError,\n type CompactionAgentContext,\n type CompactionResult,\n type CompactionTrigger,\n type ManualCompactAgentContext,\n} from '@deepseek-ai/dsh-compaction'\nimport { createCore, type CompressionCore } from 'acp-kernel'\nimport type {} from '@deepseek-ai/dsh-agent'\nimport { AcpStateStore } from './state.ts'\nimport { makeTools, type ToolEnvironment } from './tools.ts'\nimport { acpCommand } from './commands.ts'\nimport { buildNudge } from './nudge.ts'\nimport { ACP_SYSTEM_PROMPT, ACP_SYSTEM_PROMPT_ORDER } from './system-prompt.ts'\n\nexport { AcpStateStore } from './state.ts'\nexport { kernelConfigFor, type KernelConfigInput } from './config.ts'\nexport { ACP_SYSTEM_PROMPT, ACP_SYSTEM_PROMPT_ORDER } from './system-prompt.ts'\nexport { makeTools, type ToolEnvironment } from './tools.ts'\nexport { acpCommand } from './commands.ts'\nexport { buildNudge, type NudgeEnvironment, type NudgeOutcome } from './nudge.ts'\nexport {\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n findOpenTurn,\n assertNoActiveCompaction,\n type AcpBlockLedgerEntry,\n type CompactionTransactionInput,\n} from './region.ts'\nexport { eventsToCoreMessages, projectEvent, surfaceEventsOf, extractEventText } from './messages.ts'\n\nexport interface AcpConfig {\n /** The context window used for pressure decisions. Default 128000. */\n readonly modelContextLimit: number\n /** Nudge window lower bound (usage fraction). Kernel default 0.45 — same as billion-context-pi. */\n readonly nudgeMinContextLimitPct?: number\n /** Nudge window upper bound (over-limit). Kernel default 0.75 — same as billion-context-pi. */\n readonly nudgeMaxContextLimitPct?: number\n /** Emergency nudge threshold (bypasses per-turn dedup). Kernel default 0.95 — same as billion-context-pi. */\n readonly nudgeEmergencyThresholdPct?: number\n /** Any other acp-kernel Config override (billion-context-pi's `coreOverrides` escape hatch). */\n readonly coreOverrides?: Partial<import('acp-kernel').Config>\n /** Register the four model tools on `ctx.tools`. Default true. */\n readonly autoTools: boolean\n /** Register the `/acp` command on `ctx.commands`. Default true. */\n readonly autoCommand: boolean\n /** Inject the nudge into `agent/pre-step` when the kernel recommends it. Default true. */\n readonly autoNudge: boolean\n}\n\nconst DEFAULT_CONFIG: AcpConfig = {\n modelContextLimit: 128000,\n autoTools: true,\n autoCommand: true,\n autoNudge: true,\n}\n\nexport function resolveAcpConfig(config: Partial<AcpConfig> = {}): AcpConfig {\n return { ...DEFAULT_CONFIG, ...config }\n}\n\n/**\n * The ACP compaction backend. Subclasses the seam exactly like\n * `dsh-compaction-basic`; swaps summarization-driven compaction for\n * model-driven block compression without touching the agent loop.\n */\nexport class AcpCompactionEngine extends CompactionEngine {\n /** The framework-agnostic ACP compression core, reused verbatim. */\n readonly kernel: CompressionCore\n /** Per-session kernel state. */\n readonly store: AcpStateStore\n /** Resolved engine configuration. */\n readonly config: AcpConfig\n\n private readonly lastNudgeTurn = new Map<string, number>()\n\n constructor(ctx: Context, config: Partial<AcpConfig> = {}) {\n super(ctx)\n this.config = resolveAcpConfig(config)\n this.kernel = createCore({})\n this.store = new AcpStateStore()\n\n const env: ToolEnvironment = {\n kernel: this.kernel,\n store: this.store,\n modelContextLimit: this.config.modelContextLimit,\n nudgeMinContextLimitPct: this.config.nudgeMinContextLimitPct,\n nudgeMaxContextLimitPct: this.config.nudgeMaxContextLimitPct,\n nudgeEmergencyThresholdPct: this.config.nudgeEmergencyThresholdPct,\n coreOverrides: this.config.coreOverrides,\n }\n\n if (this.config.autoTools) {\n const tools = ctx.get('tools')\n if (tools !== undefined) {\n for (const tool of makeTools(env)) tools.register(tool)\n }\n }\n if (this.config.autoCommand) {\n const commands = ctx.get('commands')\n if (commands !== undefined) commands.register(acpCommand(env))\n }\n if (this.config.autoNudge) {\n ctx.on('agent/pre-step', async (payload, next) => {\n const decision = await next()\n if (decision.kind === 'reject') return decision\n const outcome = buildNudge(payload.agent, env, this.lastNudgeTurn)\n if (outcome === null) return decision\n return { kind: 'enter', messages: [...decision.messages, outcome.message] }\n })\n }\n // The load-bearing ACP guidance lives in the system prompt ONCE; nudges\n // stay short and advisory (model-driven: the model decides).\n const systemPrompt = ctx.get('systemPrompt')\n if (systemPrompt !== undefined) {\n systemPrompt.section({\n name: 'billion-context-dsh',\n order: ACP_SYSTEM_PROMPT_ORDER,\n text: ACP_SYSTEM_PROMPT,\n })\n }\n }\n\n /** ACP is model-driven: automatic pressure policy never summarizes by itself. */\n override async compactIfNeeded(\n _agent: CompactionAgentContext,\n _trigger: CompactionTrigger,\n signal: AbortSignal,\n ): Promise<CompactionResult | null> {\n signal.throwIfAborted()\n return null\n }\n\n /** Explicit idle-session compaction: ACP leaves the decision to the model. */\n override async compactNow(\n _agent: ManualCompactAgentContext,\n signal: AbortSignal,\n ): Promise<CompactionResult | null> {\n signal.throwIfAborted()\n return null\n }\n\n /**\n * The model-driven path lands through the `compress` tool, which runs the\n * full durable transaction directly. This seam method rejects with guidance:\n * automatic summarization is exactly what ACP replaces.\n */\n override async compactRegion(\n _start: number,\n _end: number,\n _agent: CompactionAgentContext,\n signal?: AbortSignal,\n ): Promise<CompactionResult> {\n signal?.throwIfAborted()\n throw new ManualCompactionError(\n 'summary',\n 'billion-context-dsh is model-driven: use the compress tool instead of automatic summarization',\n )\n }\n}\n\nexport default AcpCompactionEngine\n","/**\n * M2 — per-session ACP kernel state.\n *\n * The in-memory map holds the exact acp-kernel `CompressionState` while a\n * session is live. Durability does not rely on a sidecar file: every durable\n * compression writes a `compaction/summary` event whose shadowed range and\n * summary re-derive the block ledger (`rebuildBlockLedger` in region.ts), so a\n * restarted engine can answer decompress/search/status from the session log\n * alone — DSH's \"log is the source of truth\" model.\n * @module billion-context-dsh/state\n */\n\nimport type { Session } from '@deepseek-ai/dsh-session'\nimport { createInitialState, type CompressionState } from 'acp-kernel'\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 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 * 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 { estimateTokensFast, type CompressionCore } from 'acp-kernel'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport type { AcpStateStore } from './state.ts'\nimport { kernelConfigFor, type KernelConfigInput } from './config.ts'\nimport {\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n} from './region.ts'\nimport { eventsToCoreMessages, extractEventText, surfaceEventsOf } from './messages.ts'\n\nexport interface ToolEnvironment extends KernelConfigInput {\n readonly kernel: CompressionCore\n readonly store: AcpStateStore\n}\n\ninterface TextOutput {\n text: string\n}\n\nfunction textOutput(): {\n schema: { type: 'object'; properties: { text: { type: 'string' } }; additionalProperties: boolean }\n render: (args: unknown, value: TextOutput) => import('@deepseek-ai/dsh-llm').ContentBlock[]\n} {\n return {\n schema: {\n type: 'object',\n properties: { text: { type: 'string' } },\n additionalProperties: false,\n },\n render: (_args, value) => [{ type: 'text', text: value.text }],\n }\n}\n\nfunction requireAgent(exec: ToolRunContext): Agent {\n if (exec.agent === undefined) {\n throw new Error('billion-context-dsh: tool requires an agent execution context')\n }\n return exec.agent\n}\n\nconst compressParameters = {\n topic: { type: 'string' as const, description: 'Fallback topic for entries without their own.' },\n content: {\n type: 'array' as const,\n required: true,\n description: 'One or more ranges to compress, each with startSeq/endSeq boundaries (surface seqs) and a dense summary.',\n items: {\n type: 'object' as const,\n properties: {\n startSeq: {\n oneOf: [\n { type: 'integer' as const, description: 'First surface seq of the range.' },\n { type: 'string' as const, description: 'Seq as text; a trailing #callId fragment is ignored.' },\n ],\n },\n endSeq: {\n oneOf: [\n { type: 'integer' as const, description: 'Inclusive last surface seq of the range.' },\n { type: 'string' as const, description: 'Seq as text; a trailing #callId fragment is ignored.' },\n ],\n },\n summary: { type: 'string' as const, description: 'Complete technical summary replacing the range; keep paths, decisions, values verbatim. Minimum 50 characters.' },\n topic: { type: 'string' as const, description: 'Short label (3-5 words) for this range.' },\n },\n additionalProperties: false,\n },\n },\n} as const\n\n/** Normalize a seq arg: number, \"295\", or \"295#call_00_xxx\" → 295. */\nfunction parseSeq(value: number | string): number {\n const text = String(value).split('#')[0]!.trim()\n const seq = Number(text)\n if (!Number.isInteger(seq) || seq < 0) {\n throw new Error(`billion-context-dsh: invalid seq \"${String(value)}\" — use a surface seq like 295`)\n }\n return seq\n}\n\ninterface CompressArgs {\n topic?: string\n content: Array<{ startSeq: number | string; endSeq: number | string; summary: string; topic?: string }>\n}\n\n/** Resolve seq → kernel ref, then applyCompression and land the transaction. */\nasync function handleCompress(env: ToolEnvironment, args: CompressArgs, exec: ToolRunContext): Promise<TextOutput> {\n const agent = requireAgent(exec)\n const session = agent.session\n const state = env.store.stateFor(session)\n const coreMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const tokenCount = coreMessages.reduce((sum, message) => sum + estimateTokensFast(message.text ?? ''), 0)\n const config = kernelConfigFor(env)\n\n // Assign refs / advance state exactly like a turn would.\n const turn = env.kernel.processTurn({ messages: coreMessages, state, config, tokenCount })\n env.store.set(session, turn.state)\n const byRaw = turn.state.messageRefs.byRaw\n\n const ranges = args.content.map((range) => {\n const startSeq = parseSeq(range.startSeq)\n const endSeq = parseSeq(range.endSeq)\n const startRef = byRaw[String(startSeq)]\n const endRef = byRaw[String(endSeq)]\n if (startRef === undefined || endRef === undefined) {\n throw new Error(\n `billion-context-dsh: seq ${startSeq}..${endSeq} has no assigned ref — `\n + 'the range must be on the current surface (run acp_status for the live seq list)',\n )\n }\n return {\n startSeq,\n endSeq,\n startRef,\n endRef,\n summary: range.summary,\n ...(range.topic ?? args.topic) === undefined ? {} : { topic: range.topic ?? args.topic },\n }\n })\n\n const applied = env.kernel.applyCompression({\n ranges: ranges.map(({ startRef, endRef, summary, topic }) => ({ startRef, endRef, summary, topic })),\n messages: coreMessages,\n state: turn.state,\n config,\n })\n if (applied.result.errors.length > 0) {\n return { text: `compress failed: ${applied.result.errors.join('; ')}` }\n }\n env.store.set(session, applied.state)\n\n const lines: string[] = []\n for (let index = 0; index < ranges.length; index += 1) {\n const range = ranges[index]!\n const original = args.content[index]!\n // The edges may have been nudged to the nearest tool-pairing-balanced cut;\n // shadow exactly what resolveSurfaceRange approved.\n const { start, end } = resolveSurfaceRange(session, range.startSeq, range.endSeq)\n const shadowed = shadowedSeqsOf(session, start, end)\n const { compactionId } = runCompactionTransaction(session, {\n start,\n end,\n shadowedSeqs: shadowed,\n summary: [{ type: 'text', text: original.summary }],\n shadowedTokenCount: 0,\n provider: agent.options.provider ?? '',\n model: agent.options.model ?? '',\n })\n const adjusted = start !== range.startSeq || end !== range.endSeq\n lines.push(\n ` block ${compactionId.slice(0, 8)}: seqs ${start}..${end}, ${shadowed.length} messages shadowed`\n + (adjusted ? ` (adjusted from ${range.startSeq}..${range.endSeq} to balanced edges)` : ''),\n )\n }\n\n return {\n text: `Compressed ${applied.result.blocksCreated} block(s), ~${applied.result.tokensCompressed} tokens reclaimed.\\n${lines.join('\\n')}`,\n }\n}\n\nconst decompressParameters = {\n blockId: { type: 'string' as const, required: true, description: 'Block id from acp_status or search_context (the compaction id).' },\n} as const\n\ninterface DecompressArgs {\n blockId: string\n}\n\nfunction handleDecompress(_env: ToolEnvironment, args: DecompressArgs, exec: ToolRunContext): TextOutput {\n const session = requireAgent(exec).session\n const ledger = rebuildBlockLedger(session.events)\n const block = ledger.find((entry) => entry.blockId.startsWith(args.blockId))\n if (block === undefined) {\n return { text: `decompress: block \"${args.blockId}\" not found (see acp_status for the block list)` }\n }\n const parts: string[] = []\n for (const seq of block.shadowedSeqs) {\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 return {\n text: `Block ${block.blockId} — ${block.summary}\\n\\n${parts.join('\\n\\n') || '(no recoverable content)'}`,\n }\n}\n\nconst searchParameters = {\n query: { type: 'string' as const, required: true, description: 'Search terms to find inside compressed blocks.' },\n limit: { type: 'integer' as const, description: 'Maximum results (default 5).' },\n} as const\n\ninterface SearchArgs {\n query: string\n limit?: number\n}\n\nfunction handleSearch(_env: ToolEnvironment, args: SearchArgs, exec: ToolRunContext): TextOutput {\n const session = requireAgent(exec).session\n const ledger = rebuildBlockLedger(session.events)\n const terms = args.query.toLowerCase().split(/\\s+/).filter(Boolean)\n const scored: Array<{ blockId: string; score: number; summary: string }> = []\n for (const block of ledger) {\n const original = block.shadowedSeqs\n .map((seq) => extractEventText(session.events[seq]!))\n .join('\\n')\n const haystack = `${block.summary}\\n${original}`.toLowerCase()\n let score = 0\n for (const term of terms) score += haystack.split(term).length - 1\n if (score > 0) scored.push({ blockId: block.blockId, score, summary: block.summary })\n }\n scored.sort((a, b) => b.score - a.score)\n const top = scored.slice(0, args.limit ?? 5)\n if (top.length === 0) return { text: `search_context: no matches for \"${args.query}\"` }\n return {\n text: `Matches for \"${args.query}\":\\n`\n + top.map((hit) => ` - ${hit.blockId} (score ${hit.score}): ${hit.summary.slice(0, 160)}`).join('\\n')\n + '\\n\\nDecompress with: decompress({ blockId })',\n }\n}\n\nconst statusParameters = {} as const\n\ninterface StatusArgs {\n [key: string]: never\n}\n\nfunction handleStatus(env: ToolEnvironment, _args: StatusArgs, exec: ToolRunContext): TextOutput {\n const session = requireAgent(exec).session\n const ledger = rebuildBlockLedger(session.events)\n const totalTokens = ledger.reduce((sum, block) => sum + block.shadowedTokenCount, 0)\n const coreMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const estimated = coreMessages.reduce((sum, message) => sum + estimateTokensFast(message.text ?? ''), 0)\n const limit = env.modelContextLimit\n const lines = [\n `ACP status — session ${session.id}`,\n ` blocks: ${ledger.length}`,\n ` tokens compressed: ${totalTokens}`,\n ` estimated context: ${estimated} / ${limit} (${Math.round((estimated / limit) * 100)}%)`,\n ]\n for (const block of ledger.slice(0, 10)) {\n lines.push(` - ${block.blockId.slice(0, 8)}: seqs ${block.start}..${block.end} (${block.shadowedSeqs.length} msgs) — ${block.summary.slice(0, 80)}`)\n }\n return { text: lines.join('\\n') }\n}\n\n/** Build the four ACP model tools bound to one engine. */\nexport function makeTools(env: ToolEnvironment): ToolDefinition[] {\n return [\n defineTool({\n name: 'compress',\n description:\n 'Replace older conversation ranges with dense summaries you write. '\n + 'Each message seq is a surface reference. Single range: compress({ content: [{ startSeq, endSeq, summary }] }). '\n + 'Batch multiple unrelated ranges in one call. Never compress content the current step is actively using.',\n parameters: compressParameters,\n output: textOutput(),\n async execute(args, exec) {\n return handleCompress(env, args as CompressArgs, exec)\n },\n }),\n defineTool({\n name: 'decompress',\n description: 'Recover the original content of a compressed block by its blockId (read-only; does not unshadow the range).',\n parameters: decompressParameters,\n output: textOutput(),\n execute(args, exec) {\n return Promise.resolve(handleDecompress(env, args as DecompressArgs, exec))\n },\n }),\n defineTool({\n name: 'search_context',\n description: 'Search inside compressed blocks (summaries and original content) for information the model no longer sees in context.',\n parameters: searchParameters,\n output: textOutput(),\n execute(args, exec) {\n return Promise.resolve(handleSearch(env, args as SearchArgs, exec))\n },\n }),\n defineTool({\n name: 'acp_status',\n description: 'Report the ACP block ledger: compressed blocks, reclaimed tokens, and current context pressure.',\n parameters: statusParameters,\n output: textOutput(),\n execute(args, exec) {\n return Promise.resolve(handleStatus(env, args as StatusArgs, exec))\n },\n }),\n ]\n}\n","/**\n * Kernel configuration assembly — the DSH counterpart of billion-context-pi's\n * `resolveConfig`: build acp-kernel's `Config` from adapter-level knobs.\n *\n * Defaults are deliberately the acp-kernel `defaultConfig` values (the same\n * defaults billion-context-pi ships: nudge window 45%–75%, emergency 95%,\n * growth ratio 5%, protected last messages 5). Every knob is optional — an\n * omitted value keeps the kernel default, so the behavior matches the Pi\n * adapter exactly unless a deployment opts out.\n * @module billion-context-dsh/config\n */\n\nimport { defaultConfig, type Config } from 'acp-kernel'\n\n/** The kernel-facing knobs shared by the nudge path and the compress tool. */\nexport interface KernelConfigInput {\n readonly modelContextLimit: number\n /** Nudge window lower bound (usage fraction). Kernel default: 0.45. */\n readonly nudgeMinContextLimitPct?: number\n /** Nudge window upper bound — over-limit. Kernel default: 0.75. */\n readonly nudgeMaxContextLimitPct?: number\n /** Emergency nudge threshold (bypasses per-turn dedup). Kernel default: 0.95. */\n readonly nudgeEmergencyThresholdPct?: number\n /** Any other acp-kernel Config override (the billion-context-pi escape hatch). */\n readonly coreOverrides?: Partial<Config>\n}\n\n/**\n * Assemble the kernel config: `defaultConfig(limit)` merged with the optional\n * nudge thresholds (merged into the defaults, never replacing them wholesale)\n * and any additional `coreOverrides`.\n */\nexport function kernelConfigFor(input: KernelConfigInput): Config {\n const nudgePatch: Partial<Config['nudge']> = {}\n if (input.nudgeMinContextLimitPct !== undefined) nudgePatch.minContextLimitPct = input.nudgeMinContextLimitPct\n if (input.nudgeMaxContextLimitPct !== undefined) nudgePatch.maxContextLimitPct = input.nudgeMaxContextLimitPct\n if (input.nudgeEmergencyThresholdPct !== undefined) nudgePatch.emergencyThresholdPct = input.nudgeEmergencyThresholdPct\n\n const overrides: Partial<Config> = { ...input.coreOverrides }\n if (Object.keys(nudgePatch).length > 0) {\n overrides.nudge = { ...defaultConfig(input.modelContextLimit).nudge, ...nudgePatch }\n }\n return defaultConfig(input.modelContextLimit, overrides)\n}\n","/**\n * 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 } from '@deepseek-ai/dsh-session'\nimport {\n CompactionId,\n compactCheckpointSource,\n toolPairingBalancedAfter,\n toolPairingBalancedBefore,\n} from '@deepseek-ai/dsh-compaction'\nimport { createUserMessage, type ContentBlock } from '@deepseek-ai/dsh-llm'\nimport { extractText } from './messages.ts'\n\n/** One durable ACP block as rebuilt from the session log. */\nexport interface AcpBlockLedgerEntry {\n /** The compaction transaction id (stable block identity). */\n readonly blockId: string\n readonly summary: string\n readonly shadowedSeqs: readonly number[]\n readonly shadowedTokenCount: number\n readonly start: number\n readonly end: number\n}\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 * Validate one inclusive surface span and adjust its edges to the nearest\n * tool-pairing-balanced cuts. Missing or reversed ranges still throw; an\n * edge that sits inside a tool-call/result pair is nudged outward to the\n * closest clean cut instead (models routinely pick edges from the nudge's\n * range table that straddle a pair). The returned range is what a caller\n * should actually shadow.\n */\nexport function resolveSurfaceRange(\n session: Session,\n start: number,\n end: number,\n): { start: number; end: number } {\n const nodes = session.surface.nodes\n let startIdx = nodes.indexOf(start)\n let endIdx = nodes.indexOf(end)\n if (startIdx < 0 || endIdx < 0) {\n throw new Error(`billion-context-dsh: seq ${start}..${end} not in the current surface`)\n }\n if (startIdx > endIdx) {\n throw new Error(`billion-context-dsh: reversed range ${start}..${end}`)\n }\n // Nudge the start forward and the end backward to the nearest balanced cuts.\n while (startIdx <= endIdx && !toolPairingBalancedBefore(session, nodes[startIdx]!)) {\n startIdx += 1\n }\n while (endIdx >= startIdx && !toolPairingBalancedAfter(session, nodes[endIdx]!)) {\n endIdx -= 1\n }\n if (startIdx > endIdx) {\n throw new Error(\n `billion-context-dsh: no tool-pairing-balanced range inside seq ${start}..${end} — `\n + 'narrow the range or consult acp_status for the current surface',\n )\n }\n return { start: nodes[startIdx]!, end: nodes[endIdx]! }\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}\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 }).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/** 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 = event.data\n ledger.push({\n blockId: data.compactionId,\n summary: extractText(data.summary),\n shadowedSeqs: [...data.shadowedSeqs],\n shadowedTokenCount: data.shadowedTokenCount,\n start: data.shadowedRange.start,\n end: data.shadowedRange.end,\n })\n }\n return ledger\n}\n","/**\n * M1 — session-log projection: DSH surface events → acp-kernel CoreMessage.\n *\n * The ACP kernel is message-array based; DSH is event-log based. This module\n * is the bridge in the direction the engine needs (projectEvent /\n * eventsToCoreMessages). The reverse direction (CoreMessage[] → session\n * appends) is the M5 region transaction's job.\n * Mirrors billion-context-pi's `projectMessage`/`entriesToCoreMessages`\n * against DSH event shapes (see V-verification: SurfaceEventType =\n * 'user/message' | 'assistant/message' | 'tool/result').\n * @module billion-context-pi-dsh/messages\n */\n\nimport type { CoreMessage } from 'acp-kernel'\nimport type { SessionEvent } from '@deepseek-ai/dsh-session'\n\n/**\n * Extract plain text from a DSH content block array or string.\n *\n * Recursive: a real DSH `tool-result` block is `{ type: 'tool-result',\n * toolCallId, content: ContentBlock[] }` — the inner `content` array holds\n * the actual `text` blocks, so a top-level-only walk would drop every tool\n * result from the projection (and with it the seq's ref assignment, breaking\n * compress boundary resolution). Nested arrays are flattened depth-first.\n */\nexport function extractText(content: unknown): string {\n if (typeof content === 'string') return content\n if (!Array.isArray(content)) return ''\n const parts: string[] = []\n for (const block of content) {\n if (block === null || typeof block !== 'object') continue\n const b = block as { type?: unknown; text?: unknown; content?: unknown }\n if (b.type === 'text' && typeof b.text === 'string') {\n parts.push(b.text)\n } else if (Array.isArray(b.content)) {\n parts.push(extractText(b.content))\n }\n }\n return parts.join('\\n')\n}\n\ninterface ToolCallBlock {\n type: 'tool-call'\n id?: string\n name?: string\n arguments?: unknown\n}\n\nfunction toolCallsOf(content: unknown): ToolCallBlock[] {\n if (!Array.isArray(content)) return []\n return content.filter((b): b is ToolCallBlock => (b as { type?: string }).type === 'tool-call')\n}\n\nfunction stringifyArgs(args: unknown): string {\n if (!args) return ''\n if (typeof args === 'string') return args\n try {\n return JSON.stringify(args)\n } catch {\n return String(args)\n }\n}\n\n/**\n * Project one surface message event into CoreMessage(s).\n * - user/message → user text (verbatim content)\n * - assistant/message → assistant text, or one CoreMessage per tool-call\n * - tool/result → tool result (toolName/toolCallId, role 'tool')\n * Non-surface events project to nothing.\n */\nexport function projectEvent(event: SessionEvent): CoreMessage[] {\n switch (event.type) {\n case 'user/message': {\n const text = extractText((event.data as { content?: unknown }).content)\n return text.length > 0 ? [{ id: String(event.seq), role: 'user', contentType: 'text', text }] : []\n }\n case 'assistant/message': {\n const content = (event.data as { message?: { content?: unknown } }).message?.content\n const calls = toolCallsOf(content)\n const text = extractText(content)\n if (calls.length === 0) {\n return text.trim().length > 0\n ? [{ id: String(event.seq), role: 'assistant', contentType: 'text', text }]\n : []\n }\n if (calls.length === 1) {\n const call = calls[0]!\n const argStr = stringifyArgs(call.arguments)\n const body = argStr && text ? `${text}\\n${argStr}` : argStr || text\n return [{\n id: String(event.seq),\n role: 'assistant',\n contentType: 'tool-call',\n toolName: call.name ?? '',\n toolCallId: call.id ?? '',\n text: body,\n }]\n }\n return calls.map((call) => ({\n id: `${event.seq}#${call.id ?? ''}`,\n role: 'assistant' as const,\n contentType: 'tool-call' as const,\n toolName: call.name ?? '',\n toolCallId: call.id ?? '',\n text: stringifyArgs(call.arguments) || text,\n }))\n }\n case 'tool/result': {\n const message = (event.data as {\n message?: { content?: unknown; toolName?: string; toolCallId?: string }\n }).message\n const text = extractText(message?.content)\n if (text.length === 0) return []\n return [{\n id: String(event.seq),\n role: 'tool',\n contentType: 'tool-result',\n toolName: message?.toolName ?? '',\n toolCallId: message?.toolCallId ?? '',\n text,\n }]\n }\n default:\n return []\n }\n}\n\n/** Project a session's message events into CoreMessage[] in log order. */\nexport function eventsToCoreMessages(events: readonly SessionEvent[]): CoreMessage[] {\n const out: CoreMessage[] = []\n for (const event of events) out.push(...projectEvent(event))\n return out\n}\n\n/** The surface-visible message events of a session, in model-visible order. */\nexport function surfaceEventsOf(session: import('@deepseek-ai/dsh-session').Session): SessionEvent[] {\n return session.surface.nodes\n .map((seq) => session.events[seq])\n .filter((event): event is SessionEvent => event !== undefined)\n}\n\n/** 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 * M4 — the `/acp` slash command: a human-friendly window into the same\n * machinery the model tools expose (status, one-shot compress, decompress).\n * @module billion-context-dsh/commands\n */\n\nimport type { CommandDefinition } from '@deepseek-ai/dsh-commands'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport type { ToolEnvironment } from './tools.ts'\nimport {\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n} from './region.ts'\nimport { eventsToCoreMessages, extractEventText, surfaceEventsOf } from './messages.ts'\nimport { defaultConfig, estimateTokensFast } from 'acp-kernel'\n\nfunction statusText(env: ToolEnvironment, agent: Agent): string {\n const session = agent.session\n const ledger = rebuildBlockLedger(session.events)\n const totalTokens = ledger.reduce((sum, block) => sum + block.shadowedTokenCount, 0)\n const coreMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const estimated = coreMessages.reduce((sum, message) => sum + estimateTokensFast(message.text ?? ''), 0)\n const limit = env.modelContextLimit\n const lines = [\n `ACP status — session ${session.id}`,\n ` blocks: ${ledger.length}`,\n ` tokens compressed: ${totalTokens}`,\n ` estimated context: ${estimated} / ${limit} (${Math.round((estimated / limit) * 100)}%)`,\n ]\n for (const block of ledger.slice(0, 10)) {\n lines.push(` - ${block.blockId.slice(0, 8)}: 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 const shadowed = shadowedSeqsOf(session, startSeq, endSeq)\n const { compactionId } = runCompactionTransaction(session, {\n start,\n end,\n shadowedSeqs: shadowed,\n summary: [{ type: 'text', text: summary }],\n shadowedTokenCount: 0,\n provider: agent.options.provider ?? '',\n model: agent.options.model ?? '',\n })\n return `Compressed seqs ${start}..${end} (${shadowed.length} messages) as block ${compactionId.slice(0, 8)}`\n}\n\nfunction decompressText(_env: ToolEnvironment, agent: Agent, args: string[]): string {\n if (args.length < 1) return '/acp decompress <blockId>'\n const session = agent.session\n const ledger = rebuildBlockLedger(session.events)\n const block = ledger.find((entry) => entry.blockId.startsWith(args[0]!))\n if (block === undefined) return `block \"${args[0]}\" not found (see /acp status)`\n const parts = block.shadowedSeqs\n .map((seq) => extractEventText(session.events[seq]!))\n .filter((text) => text.length > 0)\n return `Block ${block.blockId} — ${block.summary}\\n\\n${parts.join('\\n\\n') || '(no recoverable content)'}`\n}\n\n/** Register the /acp command (idempotent per engine). */\nexport function acpCommand(env: ToolEnvironment): CommandDefinition {\n return {\n name: 'acp',\n description:\n 'Active Context Pruning — model-driven context compression. '\n + 'Usage: /acp status | /acp compress <startSeq> <endSeq> <summary> | /acp decompress <blockId>',\n handler: async (invocation) => {\n const raw = invocation.rawInput.trim()\n if (raw === '' || raw === 'status') {\n return { kind: 'success', text: statusText(env, invocation.agent) }\n }\n if (raw.startsWith('compress')) {\n return { kind: 'success', text: compressText(env, invocation.agent, raw.slice('compress'.length).trim().split(/\\s+/) ) }\n }\n if (raw.startsWith('decompress')) {\n return { kind: 'success', text: decompressText(env, invocation.agent, raw.slice('decompress'.length).trim().split(/\\s+/)) }\n }\n return { kind: 'error', text: `unknown /acp subcommand \"${raw.split(/\\s+/)[0]}\" — use status | compress | decompress` }\n },\n }\n}\n","/**\n * M4 — ACP nudge: the kernel's compression recommendation, rendered as an\n * injected user message with a seq-based compressible-range table (D1:\n * \"seq is the ref\" — DSH has no in-memory message rewrite hook, so the model\n * targets ranges by surface seq rather than by <acp> tags).\n * @module billion-context-dsh/nudge\n */\n\nimport {\n estimateTokensFast,\n type CompressionCore,\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 { eventsToCoreMessages, surfaceEventsOf } from './messages.ts'\nimport { findOpenTurn } from './region.ts'\nimport { kernelConfigFor, type KernelConfigInput } from './config.ts'\n\n/** Kernel inputs the nudge path shares with the compress tool. */\nexport interface NudgeEnvironment extends KernelConfigInput {\n readonly kernel: CompressionCore\n readonly store: AcpStateStore\n}\n\nexport interface NudgeOutcome {\n readonly message: UserMessage\n readonly emergency: boolean\n}\n\n/** Render the compressible-range table as seq refs for the model. */\nfunction rangeTable(nudge: NudgeDecision, state: { messageRefs: { byRef: Record<string, string> } }): string {\n const byRef = state.messageRefs.byRef\n const lines = nudge.compressibleRanges.slice(0, 6).map((range) => {\n const startRaw = byRef[range.startRef]\n const endRaw = byRef[range.endRef]\n if (startRaw === undefined || endRaw === undefined) return null\n return ` - seq ${startRaw}..${endRaw} — ${range.count} messages, ~${range.tokens} tokens`\n })\n const visible = lines.filter((line): line is string => line !== null)\n if (visible.length === 0) return ''\n return [\n '',\n 'Compressible ranges (refs are surface seqs):',\n ...visible,\n 'Compress with: compress({ content: [{ startSeq, endSeq, summary }] })',\n ].join('\\n')\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 const coreMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const tokenCount = coreMessages.reduce((sum, message) => sum + estimateTokensFast(message.text ?? ''), 0)\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, turn.state)\n const message = createUserMessage({\n content: [{ type: 'text', text }],\n source: { kind: 'plugin', plugin: 'acp-nudge' },\n })\n return { message, emergency }\n}\n\n/**\n * A short ADVISORY nudge — ACP is model-driven, the model decides whether and\n * when to compress. Full guidance (tools, philosophy, summary rules) lives in\n * the system prompt once, not in every nudge.\n */\nfunction buildNudgeText(\n nudge: NudgeDecision,\n emergency: boolean,\n state: { messageRefs: { byRef: Record<string, string> } },\n): string {\n const pct = Math.round(nudge.contextUsage * 100)\n const frame = emergency\n ? `⚠️ Context usage is at ${pct}% of the window — nearly full. Consider compressing consumed ranges soon so working context stays available; the choice and timing are yours.`\n : `Context usage is at ${pct}%. This is a suggestion, not a requirement — you decide whether and when to compress.`\n const guidance = 'Compress by need, not by percentage: replace only ranges you have genuinely consumed, with dense self-contained summaries.'\n return [frame, '', guidance, rangeTable(nudge, state)].join('\\n')\n}\n","/**\n * M4 — the ACP system-prompt section (DSH counterpart of billion-context-pi's\n * ACP_SYSTEM_PROMPT): the load-bearing compression guidance lives here, ONCE,\n * instead of being re-sent with every nudge. The nudge itself stays a short,\n * advisory notice — ACP is model-driven, the model decides whether and when\n * to compress (never \"compress now\").\n * @module billion-context-dsh/system-prompt\n */\n\nimport { COMPRESS_PHILOSOPHY } from 'acp-kernel'\n\nexport const ACP_SYSTEM_PROMPT = `Active Context Pruning — model-driven context management\n\nYOU decide whether and when to compress context. Nothing forces you: the injected \"nudge\" is a suggestion, not an order, and you may ignore it when compression would not help. Compress only ranges you have genuinely consumed (read tool outputs, finished explorations, superseded steps) that the current work no longer needs verbatim.\n\n${COMPRESS_PHILOSOPHY}\n\nCompression tools (refs are SURFACE SEQS, not ids):\n- compress: replace a seq range with your dense self-contained summary. compress({ content: [{ startSeq, endSeq, summary }] }). Edges are auto-balanced to tool-call/result boundaries; a trailing #callId fragment in a seq is ignored. Ranges must be on the current surface — stale seqs fail with guidance.\n- decompress: recover a compressed block's original content, read-only. decompress({ blockId }).\n- search_context: find information inside compressed blocks BEFORE decompressing. search_context({ query }).\n- acp_status: current context usage and the live compressible-range list. Run it before compressing when in doubt.\n\nWhen you write a summary, it becomes the ONLY record of that range: keep file paths, signatures, exact values, decisions, and error strings verbatim so a later reader (or you, after decompress) can continue without the original. Never reuse historical seqs — the surface moves as messages land and compress; verify with acp_status.`\n\n/** System-prompt section order: tool guidance lives in 100–199. */\nexport const ACP_SYSTEM_PROMPT_ORDER = 150\n"],"mappings":"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","names":["estimateTokensFast","estimateTokensFast","createUserMessage","message","estimateTokensFast","createUserMessage"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/state.ts","../src/tools.ts","../src/config.ts","../src/region.ts","../src/messages.ts","../src/commands.ts","../src/nudge.ts","../src/system-prompt.ts"],"sourcesContent":["/**\n * billion-context-dsh — Active Context Pruning (ACP) for the DeepSeek Harness,\n * delivered as a `CompactionEngine` backend.\n *\n * The model decides when and what to compress (pure ACP semantics):\n * - the `compress` tool durably shadows a surface range with the model-written\n * summary (no second LLM summarization call — the ACP cost win);\n * - the original events stay in the append-only session log, so `decompress`,\n * `search_context`, and replay always work;\n * - refs are surface seqs carried by the injected nudge's range table (DSH\n * has no in-memory message rewrite hook — see docs/dsh-porting-verification.md);\n * - automatic policy never summarizes by itself: it nudges the model.\n *\n * Mount it wherever a compaction backend is expected:\n *\n * ```yaml\n * - id: compaction-billion-context\n * name: 'billion-context-dsh'\n * config:\n * modelContextLimit: 128000\n * ```\n *\n * The package registers `ctx.compaction` plus the four model tools and the\n * `/acp` command when the hosting composition provides `ctx.tools` /\n * `ctx.commands`.\n * @module billion-context-dsh\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport {\n CompactionEngine,\n ManualCompactionError,\n type CompactionAgentContext,\n type CompactionResult,\n type CompactionTrigger,\n type ManualCompactAgentContext,\n} from '@deepseek-ai/dsh-compaction'\nimport { createCore, type CompressionCore } from 'acp-kernel'\nimport type {} from '@deepseek-ai/dsh-agent'\nimport { AcpStateStore } from './state.ts'\nimport { makeTools, type ToolEnvironment } from './tools.ts'\nimport { acpCommand } from './commands.ts'\nimport { buildNudge } from './nudge.ts'\nimport { ACP_SYSTEM_PROMPT, ACP_SYSTEM_PROMPT_ORDER } from './system-prompt.ts'\n\nexport { AcpStateStore } from './state.ts'\nexport { kernelConfigFor, type KernelConfigInput } from './config.ts'\nexport { ACP_SYSTEM_PROMPT, ACP_SYSTEM_PROMPT_ORDER } from './system-prompt.ts'\nexport { makeTools, type ToolEnvironment } from './tools.ts'\nexport { acpCommand } from './commands.ts'\nexport { buildNudge, type NudgeEnvironment, type NudgeOutcome } from './nudge.ts'\nexport {\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n findOpenTurn,\n assertNoActiveCompaction,\n type AcpBlockLedgerEntry,\n type CompactionTransactionInput,\n} from './region.ts'\nexport { eventsToCoreMessages, projectEvent, surfaceEventsOf, extractEventText } from './messages.ts'\n\nexport interface AcpConfig {\n /** The context window used for pressure decisions. Default 128000. */\n readonly modelContextLimit: number\n /** Nudge window lower bound (usage fraction). Kernel default 0.45 — same as billion-context-pi. */\n readonly nudgeMinContextLimitPct?: number\n /** Nudge window upper bound (over-limit). Kernel default 0.75 — same as billion-context-pi. */\n readonly nudgeMaxContextLimitPct?: number\n /** Emergency nudge threshold (bypasses per-turn dedup). Kernel default 0.95 — same as billion-context-pi. */\n readonly nudgeEmergencyThresholdPct?: number\n /** Any other acp-kernel Config override (billion-context-pi's `coreOverrides` escape hatch). */\n readonly coreOverrides?: Partial<import('acp-kernel').Config>\n /** Register the four model tools on `ctx.tools`. Default true. */\n readonly autoTools: boolean\n /** Register the `/acp` command on `ctx.commands`. Default true. */\n readonly autoCommand: boolean\n /** Inject the nudge into `agent/pre-step` when the kernel recommends it. Default true. */\n readonly autoNudge: boolean\n}\n\nconst DEFAULT_CONFIG: AcpConfig = {\n modelContextLimit: 128000,\n autoTools: true,\n autoCommand: true,\n autoNudge: true,\n}\n\nexport function resolveAcpConfig(config: Partial<AcpConfig> = {}): AcpConfig {\n return { ...DEFAULT_CONFIG, ...config }\n}\n\n/**\n * The ACP compaction backend. Subclasses the seam exactly like\n * `dsh-compaction-basic`; swaps summarization-driven compaction for\n * model-driven block compression without touching the agent loop.\n */\nexport class AcpCompactionEngine extends CompactionEngine {\n /** The framework-agnostic ACP compression core, reused verbatim. */\n readonly kernel: CompressionCore\n /** Per-session kernel state. */\n readonly store: AcpStateStore\n /** Resolved engine configuration. */\n readonly config: AcpConfig\n\n private readonly lastNudgeTurn = new Map<string, number>()\n\n constructor(ctx: Context, config: Partial<AcpConfig> = {}) {\n super(ctx)\n this.config = resolveAcpConfig(config)\n this.kernel = createCore({})\n this.store = new AcpStateStore()\n\n const env: ToolEnvironment = {\n kernel: this.kernel,\n store: this.store,\n modelContextLimit: this.config.modelContextLimit,\n nudgeMinContextLimitPct: this.config.nudgeMinContextLimitPct,\n nudgeMaxContextLimitPct: this.config.nudgeMaxContextLimitPct,\n nudgeEmergencyThresholdPct: this.config.nudgeEmergencyThresholdPct,\n coreOverrides: this.config.coreOverrides,\n }\n\n if (this.config.autoTools) {\n const tools = ctx.get('tools')\n if (tools !== undefined) {\n for (const tool of makeTools(env)) tools.register(tool)\n }\n }\n if (this.config.autoCommand) {\n const commands = ctx.get('commands')\n if (commands !== undefined) commands.register(acpCommand(env))\n }\n if (this.config.autoNudge) {\n ctx.on('agent/pre-step', async (payload, next) => {\n const decision = await next()\n if (decision.kind === 'reject') return decision\n const outcome = buildNudge(payload.agent, env, this.lastNudgeTurn)\n if (outcome === null) return decision\n return { kind: 'enter', messages: [...decision.messages, outcome.message] }\n })\n }\n // The load-bearing ACP guidance lives in the system prompt ONCE; nudges\n // stay short and advisory (model-driven: the model decides).\n const systemPrompt = ctx.get('systemPrompt')\n if (systemPrompt !== undefined) {\n systemPrompt.section({\n name: 'billion-context-dsh',\n order: ACP_SYSTEM_PROMPT_ORDER,\n text: ACP_SYSTEM_PROMPT,\n })\n }\n }\n\n /** ACP is model-driven: automatic pressure policy never summarizes by itself. */\n override async compactIfNeeded(\n _agent: CompactionAgentContext,\n _trigger: CompactionTrigger,\n signal: AbortSignal,\n ): Promise<CompactionResult | null> {\n signal.throwIfAborted()\n return null\n }\n\n /** Explicit idle-session compaction: ACP leaves the decision to the model. */\n override async compactNow(\n _agent: ManualCompactAgentContext,\n signal: AbortSignal,\n ): Promise<CompactionResult | null> {\n signal.throwIfAborted()\n return null\n }\n\n /**\n * The model-driven path lands through the `compress` tool, which runs the\n * full durable transaction directly. This seam method rejects with guidance:\n * automatic summarization is exactly what ACP replaces.\n */\n override async compactRegion(\n _start: number,\n _end: number,\n _agent: CompactionAgentContext,\n signal?: AbortSignal,\n ): Promise<CompactionResult> {\n signal?.throwIfAborted()\n throw new ManualCompactionError(\n 'summary',\n 'billion-context-dsh is model-driven: use the compress tool instead of automatic summarization',\n )\n }\n}\n\nexport default AcpCompactionEngine\n","/**\n * M2 — per-session ACP kernel state.\n *\n * The in-memory map holds the exact acp-kernel `CompressionState` while a\n * session is live. Durability does not rely on a sidecar file: every durable\n * compression writes a `compaction/summary` event whose shadowed range and\n * summary re-derive the block ledger (`rebuildBlockLedger` in region.ts), so a\n * restarted engine can answer decompress/search/status from the session log\n * alone — DSH's \"log is the source of truth\" model.\n * @module billion-context-dsh/state\n */\n\nimport type { Session } from '@deepseek-ai/dsh-session'\nimport { createInitialState, type CompressionState } from 'acp-kernel'\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 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 * M3 — the four model tools: compress / decompress / search_context /\n * acp_status, registered through `ctx.tools` (defineTool).\n *\n * compress is the heart of ACP: the model writes the summary and the tool\n * lands it as a durable surface replacement (no second LLM summarization\n * call). decompress recovers shadowed content read-only from the log (DSH\n * keeps the originals — V5). search_context scores blocks rebuilt from the\n * log. acp_status reports the block ledger and pressure.\n * @module billion-context-dsh/tools\n */\n\nimport { defineTool, type ToolDefinition, type ToolRunContext } from '@deepseek-ai/dsh-tools'\nimport { defaultCountTokens, type CompressionCore } from 'acp-kernel'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport type { AcpStateStore } from './state.ts'\nimport { kernelConfigFor, type KernelConfigInput } from './config.ts'\nimport {\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n} from './region.ts'\nimport { eventsToCoreMessages, extractEventText, surfaceEventsOf } from './messages.ts'\n\nexport interface ToolEnvironment extends KernelConfigInput {\n readonly kernel: CompressionCore\n readonly store: AcpStateStore\n}\n\ninterface TextOutput {\n text: string\n}\n\nfunction textOutput(): {\n schema: { type: 'object'; properties: { text: { type: 'string' } }; additionalProperties: boolean }\n render: (args: unknown, value: TextOutput) => import('@deepseek-ai/dsh-llm').ContentBlock[]\n} {\n return {\n schema: {\n type: 'object',\n properties: { text: { type: 'string' } },\n additionalProperties: false,\n },\n render: (_args, value) => [{ type: 'text', text: value.text }],\n }\n}\n\nfunction requireAgent(exec: ToolRunContext): Agent {\n if (exec.agent === undefined) {\n throw new Error('billion-context-dsh: tool requires an agent execution context')\n }\n return exec.agent\n}\n\nconst compressParameters = {\n topic: { type: 'string' as const, description: 'Fallback topic for entries without their own.' },\n content: {\n type: 'array' as const,\n required: true,\n description: 'One or more ranges to compress, each with startSeq/endSeq boundaries (surface seqs) and a dense summary.',\n items: {\n type: 'object' as const,\n properties: {\n startSeq: {\n oneOf: [\n { type: 'integer' as const, description: 'First surface seq of the range.' },\n { type: 'string' as const, description: 'Seq as text; a trailing #callId fragment is ignored.' },\n ],\n },\n endSeq: {\n oneOf: [\n { type: 'integer' as const, description: 'Inclusive last surface seq of the range.' },\n { type: 'string' as const, description: 'Seq as text; a trailing #callId fragment is ignored.' },\n ],\n },\n summary: { type: 'string' as const, description: 'Complete technical summary replacing the range; keep paths, decisions, values verbatim. Minimum 50 characters.' },\n topic: { type: 'string' as const, description: 'Short label (3-5 words) for this range.' },\n },\n additionalProperties: false,\n },\n },\n} as const\n\n/** Normalize a seq arg: number, \"295\", or \"295#call_00_xxx\" → 295. */\nfunction parseSeq(value: number | string): number {\n const text = String(value).split('#')[0]!.trim()\n const seq = Number(text)\n if (!Number.isInteger(seq) || seq < 0) {\n throw new Error(`billion-context-dsh: invalid seq \"${String(value)}\" — use a surface seq like 295`)\n }\n return seq\n}\n\ninterface CompressArgs {\n topic?: string\n content: Array<{ startSeq: number | string; endSeq: number | string; summary: string; topic?: string }>\n}\n\n/** Resolve seq → kernel ref, then applyCompression and land the transaction. */\nasync function handleCompress(env: ToolEnvironment, args: CompressArgs, exec: ToolRunContext): Promise<TextOutput> {\n const agent = requireAgent(exec)\n const session = agent.session\n const state = env.store.stateFor(session)\n const coreMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const tokenCount = coreMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\n const config = kernelConfigFor(env)\n\n // Assign refs / advance state exactly like a turn would.\n const turn = env.kernel.processTurn({ messages: coreMessages, state, config, tokenCount })\n env.store.set(session, turn.state)\n const byRaw = turn.state.messageRefs.byRaw\n\n const ranges = args.content.map((range) => {\n const startSeq = parseSeq(range.startSeq)\n const endSeq = parseSeq(range.endSeq)\n // Balance edges FIRST: the requested edges may sit on multi-tool-call\n // assistant messages, which project to `${seq}#${callId}` CoreMessage ids\n // and therefore have NO bare-`${seq}` ref. resolveSurfaceRange shifts them\n // to clean tool-pairing-balanced cuts that always carry a bare ref, so the\n // resolved refs exist and the shadowed span matches the returned range.\n const { start, end } = resolveSurfaceRange(session, startSeq, endSeq)\n const startRef = byRaw[String(start)]\n const endRef = byRaw[String(end)]\n if (startRef === undefined || endRef === undefined) {\n throw new Error(\n `billion-context-dsh: seq ${start}..${end} has no assigned ref — `\n + 'the range must be on the current surface (run acp_status for the live seq list)',\n )\n }\n return {\n startSeq,\n endSeq,\n start,\n end,\n startRef,\n endRef,\n summary: range.summary,\n ...(range.topic ?? args.topic) === undefined ? {} : { topic: range.topic ?? args.topic },\n }\n })\n\n const applied = env.kernel.applyCompression({\n ranges: ranges.map(({ startRef, endRef, summary, topic }) => ({ startRef, endRef, summary, topic })),\n messages: coreMessages,\n state: turn.state,\n config,\n })\n if (applied.result.errors.length > 0) {\n return { text: `compress failed: ${applied.result.errors.join('; ')}` }\n }\n env.store.set(session, applied.state)\n\n const lines: string[] = []\n for (let index = 0; index < ranges.length; index += 1) {\n const range = ranges[index]!\n const original = args.content[index]!\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 { compactionId } = runCompactionTransaction(session, {\n start,\n end,\n shadowedSeqs: shadowed,\n summary: [{ type: 'text', text: original.summary }],\n shadowedTokenCount: shadowedTokens,\n provider: agent.options.provider ?? '',\n model: agent.options.model ?? '',\n })\n const adjusted = start !== range.startSeq || end !== range.endSeq\n lines.push(\n ` block ${compactionId.slice(0, 8)}: seqs ${start}..${end}, ${shadowed.length} messages shadowed`\n + (adjusted ? ` (adjusted from ${range.startSeq}..${range.endSeq} to balanced edges)` : ''),\n )\n }\n\n return {\n text: `Compressed ${applied.result.blocksCreated} block(s), ~${applied.result.tokensCompressed} tokens reclaimed.\\n${lines.join('\\n')}`,\n }\n}\n\nconst decompressParameters = {\n blockId: { type: 'string' as const, required: true, description: 'Block id from acp_status or search_context (the compaction id).' },\n} as const\n\ninterface DecompressArgs {\n blockId: string\n}\n\nfunction handleDecompress(_env: ToolEnvironment, args: DecompressArgs, exec: ToolRunContext): TextOutput {\n const session = requireAgent(exec).session\n const ledger = rebuildBlockLedger(session.events)\n const block = ledger.find((entry) => entry.blockId.startsWith(args.blockId))\n if (block === undefined) {\n return { text: `decompress: block \"${args.blockId}\" not found (see acp_status for the block list)` }\n }\n const parts: string[] = []\n for (const seq of block.shadowedSeqs) {\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 return {\n text: `Block ${block.blockId} — ${block.summary}\\n\\n${parts.join('\\n\\n') || '(no recoverable content)'}`,\n }\n}\n\nconst searchParameters = {\n query: { type: 'string' as const, required: true, description: 'Search terms to find inside compressed blocks.' },\n limit: { type: 'integer' as const, description: 'Maximum results (default 5).' },\n} as const\n\ninterface SearchArgs {\n query: string\n limit?: number\n}\n\nfunction handleSearch(_env: ToolEnvironment, args: SearchArgs, exec: ToolRunContext): TextOutput {\n const session = requireAgent(exec).session\n const ledger = rebuildBlockLedger(session.events)\n const terms = args.query.toLowerCase().split(/\\s+/).filter(Boolean)\n const scored: Array<{ blockId: string; score: number; summary: string }> = []\n for (const block of ledger) {\n const original = block.shadowedSeqs\n .map((seq) => extractEventText(session.events[seq]!))\n .join('\\n')\n const haystack = `${block.summary}\\n${original}`.toLowerCase()\n let score = 0\n for (const term of terms) score += haystack.split(term).length - 1\n if (score > 0) scored.push({ blockId: block.blockId, score, summary: block.summary })\n }\n scored.sort((a, b) => b.score - a.score)\n const top = scored.slice(0, args.limit ?? 5)\n if (top.length === 0) return { text: `search_context: no matches for \"${args.query}\"` }\n return {\n text: `Matches for \"${args.query}\":\\n`\n + top.map((hit) => ` - ${hit.blockId} (score ${hit.score}): ${hit.summary.slice(0, 160)}`).join('\\n')\n + '\\n\\nDecompress with: decompress({ blockId })',\n }\n}\n\nconst statusParameters = {} as const\n\ninterface StatusArgs {\n [key: string]: never\n}\n\nfunction handleStatus(env: ToolEnvironment, _args: StatusArgs, exec: ToolRunContext): TextOutput {\n const session = requireAgent(exec).session\n const ledger = rebuildBlockLedger(session.events)\n const totalTokens = ledger.reduce((sum, block) => sum + block.shadowedTokenCount, 0)\n const coreMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const estimated = coreMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\n const limit = env.modelContextLimit\n const lines = [\n `ACP status — session ${session.id}`,\n ` blocks: ${ledger.length}`,\n ` tokens compressed: ${totalTokens}`,\n ` estimated context: ${estimated} / ${limit} (${Math.round((estimated / limit) * 100)}%)`,\n ]\n for (const block of ledger.slice(0, 10)) {\n lines.push(` - ${block.blockId.slice(0, 8)}: seqs ${block.start}..${block.end} (${block.shadowedSeqs.length} msgs) — ${block.summary.slice(0, 80)}`)\n }\n return { text: lines.join('\\n') }\n}\n\n/** Build the four ACP model tools bound to one engine. */\nexport function makeTools(env: ToolEnvironment): ToolDefinition[] {\n return [\n defineTool({\n name: 'compress',\n description:\n 'Replace older conversation ranges with dense summaries you write. '\n + 'Each message seq is a surface reference. Single range: compress({ content: [{ startSeq, endSeq, summary }] }). '\n + 'Batch multiple unrelated ranges in one call. Never compress content the current step is actively using.',\n parameters: compressParameters,\n output: textOutput(),\n async execute(args, exec) {\n return handleCompress(env, args as CompressArgs, exec)\n },\n }),\n defineTool({\n name: 'decompress',\n description: 'Recover the original content of a compressed block by its blockId (read-only; does not unshadow the range).',\n parameters: decompressParameters,\n output: textOutput(),\n execute(args, exec) {\n return Promise.resolve(handleDecompress(env, args as DecompressArgs, exec))\n },\n }),\n defineTool({\n name: 'search_context',\n description: 'Search inside compressed blocks (summaries and original content) for information the model no longer sees in context.',\n parameters: searchParameters,\n output: textOutput(),\n execute(args, exec) {\n return Promise.resolve(handleSearch(env, args as SearchArgs, exec))\n },\n }),\n defineTool({\n name: 'acp_status',\n description: 'Report the ACP block ledger: compressed blocks, reclaimed tokens, and current context pressure.',\n parameters: statusParameters,\n output: textOutput(),\n execute(args, exec) {\n return Promise.resolve(handleStatus(env, args as StatusArgs, exec))\n },\n }),\n ]\n}\n","/**\n * Kernel configuration assembly — the DSH counterpart of billion-context-pi's\n * `resolveConfig`: build acp-kernel's `Config` from adapter-level knobs.\n *\n * Defaults are deliberately the acp-kernel `defaultConfig` values (the same\n * defaults billion-context-pi ships: nudge window 45%–75%, emergency 95%,\n * growth ratio 5%, protected last messages 5). Every knob is optional — an\n * omitted value keeps the kernel default, so the behavior matches the Pi\n * adapter exactly unless a deployment opts out.\n * @module billion-context-dsh/config\n */\n\nimport { defaultConfig, type Config } from 'acp-kernel'\n\n/** The kernel-facing knobs shared by the nudge path and the compress tool. */\nexport interface KernelConfigInput {\n readonly modelContextLimit: number\n /** Nudge window lower bound (usage fraction). Kernel default: 0.45. */\n readonly nudgeMinContextLimitPct?: number\n /** Nudge window upper bound — over-limit. Kernel default: 0.75. */\n readonly nudgeMaxContextLimitPct?: number\n /** Emergency nudge threshold (bypasses per-turn dedup). Kernel default: 0.95. */\n readonly nudgeEmergencyThresholdPct?: number\n /** Any other acp-kernel Config override (the billion-context-pi escape hatch). */\n readonly coreOverrides?: Partial<Config>\n}\n\n/**\n * Assemble the kernel config: `defaultConfig(limit)` merged with the optional\n * nudge thresholds (merged into the defaults, never replacing them wholesale)\n * and any additional `coreOverrides`.\n */\nexport function kernelConfigFor(input: KernelConfigInput): Config {\n const nudgePatch: Partial<Config['nudge']> = {}\n if (input.nudgeMinContextLimitPct !== undefined) nudgePatch.minContextLimitPct = input.nudgeMinContextLimitPct\n if (input.nudgeMaxContextLimitPct !== undefined) nudgePatch.maxContextLimitPct = input.nudgeMaxContextLimitPct\n if (input.nudgeEmergencyThresholdPct !== undefined) nudgePatch.emergencyThresholdPct = input.nudgeEmergencyThresholdPct\n\n const overrides: Partial<Config> = { ...input.coreOverrides }\n if (Object.keys(nudgePatch).length > 0) {\n overrides.nudge = { ...defaultConfig(input.modelContextLimit).nudge, ...nudgePatch }\n }\n return defaultConfig(input.modelContextLimit, overrides)\n}\n","/**\n * 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 } from '@deepseek-ai/dsh-session'\nimport {\n CompactionId,\n compactCheckpointSource,\n toolPairingBalancedAfter,\n toolPairingBalancedBefore,\n} from '@deepseek-ai/dsh-compaction'\nimport { createUserMessage, type ContentBlock } from '@deepseek-ai/dsh-llm'\nimport { defaultCountTokens } from 'acp-kernel'\nimport { extractEventText, extractText } from './messages.ts'\n\n/** One durable ACP block as rebuilt from the session log. */\nexport interface AcpBlockLedgerEntry {\n /** The compaction transaction id (stable block identity). */\n readonly blockId: string\n readonly summary: string\n readonly shadowedSeqs: readonly number[]\n readonly shadowedTokenCount: number\n readonly start: number\n readonly end: number\n}\n\n/** The open turn number, or null when the log ends between turns. */\nexport function findOpenTurn(events: readonly SessionEvent[]): number | null {\n let open: number | null = null\n for (const event of events) {\n if (event.type === 'turn/start') open = event.data.turn\n else if (event.type === 'turn/end' && event.data.turn === open) open = null\n }\n return open\n}\n\n/** Reject a second concurrent compaction for the same session. */\nexport function assertNoActiveCompaction(events: readonly SessionEvent[]): void {\n let active = false\n for (const event of events) {\n if (event.type === 'compaction/start') active = true\n else if (event.type === 'compaction/end') active = false\n }\n if (active) {\n throw new Error('billion-context-dsh: another compaction is already active for this session')\n }\n}\n\n/**\n * Whether the surface node at `seq` projects to CoreMessage(s) whose ref key\n * is the bare seq — user messages, tool results, and text-only or SINGLE\n * tool-call assistant messages all do. Multi-tool-call assistant messages\n * project to `${seq}#${callId}` ids (projectEvent) and therefore carry NO\n * bare-`${seq}` ref, so compress's byRaw lookup can never resolve them as\n * range edges. resolveSurfaceRange treats such edges as unbalanced and shifts\n * them to the nearest clean cut.\n */\nfunction hasPlainRef(session: Session, seq: number): boolean {\n const event = session.events[seq]\n if (event === undefined) return false\n switch (event.type) {\n case 'user/message':\n case 'tool/result':\n return extractEventText(event).trim().length > 0\n case 'assistant/message': {\n const content = (event.data as { message?: { content?: unknown } }).message?.content\n const calls = Array.isArray(content)\n ? content.filter(\n (block) => block !== null && typeof block === 'object' && (block as { type?: string }).type === 'tool-call',\n )\n : []\n if (calls.length > 1) return false\n // One tool-call: projectEvent emits a bare-seq CoreMessage unconditionally.\n // Zero: only when the text is non-empty.\n return calls.length === 1 || extractEventText(event).trim().length > 0\n }\n default:\n return false\n }\n}\n\n/**\n * Validate one inclusive surface span and adjust its edges to a\n * tool-pairing-balanced range whose boundaries carry a bare-seq ref. Missing\n * or reversed ranges still throw. An edge that sits inside a tool-call/result\n * pair — or on a multi-tool-call assistant message that has no bare-seq ref —\n * is first nudged inward to the nearest clean cut; if that collapses the range\n * (e.g. the model asked for a SINGLE tool result, which can never be balanced\n * alone), the range EXPANDS outward to the enclosing clean pair instead — a\n * lone tool message is almost always a \"consumed output\" the model genuinely\n * wants to compress. The returned range is what a caller should actually shadow.\n */\nexport function resolveSurfaceRange(\n session: Session,\n start: number,\n end: number,\n): { start: number; end: number } {\n const nodes = session.surface.nodes\n const requestedStartIdx = nodes.indexOf(start)\n const requestedEndIdx = nodes.indexOf(end)\n if (requestedStartIdx < 0 || requestedEndIdx < 0) {\n throw new Error(`billion-context-dsh: seq ${start}..${end} not in the current surface`)\n }\n if (requestedStartIdx > requestedEndIdx) {\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) {\n return { start: nodes[startIdx]!, end: nodes[endIdx]! }\n }\n // Second pass: the inward pass collapsed (a lone tool message) — expand\n // outward from the REQUESTED span to the smallest clean enclosing pair.\n startIdx = requestedStartIdx\n endIdx = requestedEndIdx\n while (startIdx > 0 && !cleanBefore(startIdx)) {\n startIdx -= 1\n }\n while (endIdx < nodes.length - 1 && !cleanAfter(endIdx)) {\n endIdx += 1\n }\n if (cleanBefore(startIdx) && cleanAfter(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}\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 }).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/** 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 = event.data\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 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 })\n }\n return ledger\n}\n\n/** One self-computed compressible span of the current surface. */\nexport interface SeqCompressibleRange {\n readonly start: number\n readonly end: number\n readonly count: number\n readonly tokens: number\n}\n\n/** Whether a surface user message is a compaction checkpoint node (already compressed). */\nfunction isCheckpointNode(event: SessionEvent): boolean {\n if (event.type !== 'user/message') return false\n const source = (event.data as { source?: { plugin?: string } }).source\n return source?.plugin === 'compact'\n}\n\n/**\n * Compute compressible spans directly from the surface — independent of the\n * kernel's ref map, which can drift after surface replacements in long\n * sessions and hide large tool results from the nudge range table. Skips the\n * recent protected tail, the last user message, and compaction checkpoints;\n * edges are then balanced through resolveSurfaceRange. Ranges are ordered by\n * size (largest reclaimed first).\n */\nexport function buildCompressibleSeqRanges(\n session: Session,\n opts: { preserveRecent?: number } = {},\n): SeqCompressibleRange[] {\n const nodes = session.surface.nodes\n const preserve = opts.preserveRecent ?? 5\n const protectedSeqs = new Set<number>()\n for (const seq of nodes.slice(-preserve)) protectedSeqs.add(seq)\n for (let index = nodes.length - 1; index >= 0; index -= 1) {\n const event = session.events[nodes[index]!]\n if (event?.type === 'user/message' && !isCheckpointNode(event)) {\n protectedSeqs.add(nodes[index]!)\n break\n }\n }\n const raw: SeqCompressibleRange[] = []\n let cur: SeqCompressibleRange | null = null\n const flush = (): void => {\n if (cur !== null) raw.push(cur)\n cur = null\n }\n for (const seq of nodes) {\n const event = session.events[seq]\n if (event === undefined || protectedSeqs.has(seq) || isCheckpointNode(event)) {\n flush()\n continue\n }\n 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 * M1 — session-log projection: DSH surface events → acp-kernel CoreMessage.\n *\n * The ACP kernel is message-array based; DSH is event-log based. This module\n * is the bridge in the direction the engine needs (projectEvent /\n * eventsToCoreMessages). The reverse direction (CoreMessage[] → session\n * appends) is the M5 region transaction's job.\n * Mirrors billion-context-pi's `projectMessage`/`entriesToCoreMessages`\n * against DSH event shapes (see V-verification: SurfaceEventType =\n * 'user/message' | 'assistant/message' | 'tool/result').\n * @module billion-context-pi-dsh/messages\n */\n\nimport type { CoreMessage } from 'acp-kernel'\nimport type { SessionEvent } from '@deepseek-ai/dsh-session'\n\n/**\n * Extract plain text from a DSH content block array or string.\n *\n * Recursive: a real DSH `tool-result` block is `{ type: 'tool-result',\n * toolCallId, content: ContentBlock[] }` — the inner `content` array holds\n * the actual `text` blocks, so a top-level-only walk would drop every tool\n * result from the projection (and with it the seq's ref assignment, breaking\n * compress boundary resolution). Nested arrays are flattened depth-first.\n */\nexport function extractText(content: unknown): string {\n if (typeof content === 'string') return content\n if (!Array.isArray(content)) return ''\n const parts: string[] = []\n for (const block of content) {\n if (block === null || typeof block !== 'object') continue\n const b = block as { type?: unknown; text?: unknown; content?: unknown }\n if (b.type === 'text' && typeof b.text === 'string') {\n parts.push(b.text)\n } else if (Array.isArray(b.content)) {\n parts.push(extractText(b.content))\n }\n }\n return parts.join('\\n')\n}\n\ninterface ToolCallBlock {\n type: 'tool-call'\n id?: string\n name?: string\n arguments?: unknown\n}\n\nfunction toolCallsOf(content: unknown): ToolCallBlock[] {\n if (!Array.isArray(content)) return []\n return content.filter((b): b is ToolCallBlock => (b as { type?: string }).type === 'tool-call')\n}\n\nfunction stringifyArgs(args: unknown): string {\n if (!args) return ''\n if (typeof args === 'string') return args\n try {\n return JSON.stringify(args)\n } catch {\n return String(args)\n }\n}\n\n/**\n * Project one surface message event into CoreMessage(s).\n * - user/message → user text (verbatim content)\n * - assistant/message → assistant text, or one CoreMessage per tool-call\n * - tool/result → tool result (toolName/toolCallId, role 'tool')\n * Non-surface events project to nothing.\n */\nexport function projectEvent(event: SessionEvent): CoreMessage[] {\n switch (event.type) {\n case 'user/message': {\n const text = extractText((event.data as { content?: unknown }).content)\n return text.length > 0 ? [{ id: String(event.seq), role: 'user', contentType: 'text', text }] : []\n }\n case 'assistant/message': {\n const content = (event.data as { message?: { content?: unknown } }).message?.content\n const calls = toolCallsOf(content)\n const text = extractText(content)\n if (calls.length === 0) {\n return text.trim().length > 0\n ? [{ id: String(event.seq), role: 'assistant', contentType: 'text', text }]\n : []\n }\n if (calls.length === 1) {\n const call = calls[0]!\n const argStr = stringifyArgs(call.arguments)\n const body = argStr && text ? `${text}\\n${argStr}` : argStr || text\n return [{\n id: String(event.seq),\n role: 'assistant',\n contentType: 'tool-call',\n toolName: call.name ?? '',\n toolCallId: call.id ?? '',\n text: body,\n }]\n }\n return calls.map((call) => ({\n id: `${event.seq}#${call.id ?? ''}`,\n role: 'assistant' as const,\n contentType: 'tool-call' as const,\n toolName: call.name ?? '',\n toolCallId: call.id ?? '',\n text: stringifyArgs(call.arguments) || text,\n }))\n }\n case 'tool/result': {\n const message = (event.data as {\n message?: { content?: unknown; toolName?: string; toolCallId?: string }\n }).message\n const text = extractText(message?.content)\n if (text.length === 0) return []\n return [{\n id: String(event.seq),\n role: 'tool',\n contentType: 'tool-result',\n toolName: message?.toolName ?? '',\n toolCallId: message?.toolCallId ?? '',\n text,\n }]\n }\n default:\n return []\n }\n}\n\n/** Project a session's message events into CoreMessage[] in log order. */\nexport function eventsToCoreMessages(events: readonly SessionEvent[]): CoreMessage[] {\n const out: CoreMessage[] = []\n for (const event of events) out.push(...projectEvent(event))\n return out\n}\n\n/** The surface-visible message events of a session, in model-visible order. */\nexport function surfaceEventsOf(session: import('@deepseek-ai/dsh-session').Session): SessionEvent[] {\n return session.surface.nodes\n .map((seq) => session.events[seq])\n .filter((event): event is SessionEvent => event !== undefined)\n}\n\n/** 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 * M4 — the `/acp` slash command: a human-friendly window into the same\n * machinery the model tools expose (status, one-shot compress, decompress).\n * @module billion-context-dsh/commands\n */\n\nimport type { CommandDefinition } from '@deepseek-ai/dsh-commands'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport type { ToolEnvironment } from './tools.ts'\nimport {\n rebuildBlockLedger,\n resolveSurfaceRange,\n runCompactionTransaction,\n shadowedSeqsOf,\n} from './region.ts'\nimport { eventsToCoreMessages, extractEventText, surfaceEventsOf } from './messages.ts'\nimport { defaultConfig, defaultCountTokens } from 'acp-kernel'\n\nfunction statusText(env: ToolEnvironment, agent: Agent): string {\n const session = agent.session\n const ledger = rebuildBlockLedger(session.events)\n const totalTokens = ledger.reduce((sum, block) => sum + block.shadowedTokenCount, 0)\n const coreMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const estimated = coreMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\n const limit = env.modelContextLimit\n const lines = [\n `ACP status — session ${session.id}`,\n ` blocks: ${ledger.length}`,\n ` tokens compressed: ${totalTokens}`,\n ` estimated context: ${estimated} / ${limit} (${Math.round((estimated / limit) * 100)}%)`,\n ]\n for (const block of ledger.slice(0, 10)) {\n lines.push(` - ${block.blockId.slice(0, 8)}: 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 const shadowed = shadowedSeqsOf(session, startSeq, endSeq)\n let shadowedTokens = 0\n for (const seq of shadowed) {\n const event = session.events[seq]\n if (event !== undefined) shadowedTokens += defaultCountTokens(extractEventText(event))\n }\n const { compactionId } = runCompactionTransaction(session, {\n start,\n end,\n shadowedSeqs: shadowed,\n summary: [{ type: 'text', text: summary }],\n shadowedTokenCount: shadowedTokens,\n provider: agent.options.provider ?? '',\n model: agent.options.model ?? '',\n })\n return `Compressed seqs ${start}..${end} (${shadowed.length} messages) as block ${compactionId.slice(0, 8)}`\n}\n\nfunction decompressText(_env: ToolEnvironment, agent: Agent, args: string[]): string {\n if (args.length < 1) return '/acp decompress <blockId>'\n const session = agent.session\n const ledger = rebuildBlockLedger(session.events)\n const block = ledger.find((entry) => entry.blockId.startsWith(args[0]!))\n if (block === undefined) return `block \"${args[0]}\" not found (see /acp status)`\n const parts = block.shadowedSeqs\n .map((seq) => extractEventText(session.events[seq]!))\n .filter((text) => text.length > 0)\n return `Block ${block.blockId} — ${block.summary}\\n\\n${parts.join('\\n\\n') || '(no recoverable content)'}`\n}\n\n/** Register the /acp command (idempotent per engine). */\nexport function acpCommand(env: ToolEnvironment): CommandDefinition {\n return {\n name: 'acp',\n description:\n 'Active Context Pruning — model-driven context compression. '\n + 'Usage: /acp status | /acp compress <startSeq> <endSeq> <summary> | /acp decompress <blockId>',\n handler: async (invocation) => {\n const raw = invocation.rawInput.trim()\n if (raw === '' || raw === 'status') {\n return { kind: 'success', text: statusText(env, invocation.agent) }\n }\n if (raw.startsWith('compress')) {\n return { kind: 'success', text: compressText(env, invocation.agent, raw.slice('compress'.length).trim().split(/\\s+/) ) }\n }\n if (raw.startsWith('decompress')) {\n return { kind: 'success', text: decompressText(env, invocation.agent, raw.slice('decompress'.length).trim().split(/\\s+/)) }\n }\n return { kind: 'error', text: `unknown /acp subcommand \"${raw.split(/\\s+/)[0]}\" — use status | compress | decompress` }\n },\n }\n}\n","/**\n * M4 — ACP nudge: the kernel's compression recommendation, rendered as an\n * injected user message with a seq-based compressible-range table (D1:\n * \"seq is the ref\" — DSH has no in-memory message rewrite hook, so the model\n * targets ranges by surface seq rather than by <acp> tags).\n * @module billion-context-dsh/nudge\n */\n\nimport {\n defaultCountTokens,\n type CompressionCore,\n type CoreMessage,\n type NudgeDecision,\n} from 'acp-kernel'\nimport { createUserMessage, type UserMessage } from '@deepseek-ai/dsh-llm'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport { AcpStateStore } from './state.ts'\nimport { eventsToCoreMessages, surfaceEventsOf } from './messages.ts'\nimport { buildCompressibleSeqRanges, findOpenTurn } from './region.ts'\nimport { kernelConfigFor, type KernelConfigInput } from './config.ts'\n\n/** Kernel inputs the nudge path shares with the compress tool. */\nexport interface NudgeEnvironment extends KernelConfigInput {\n readonly kernel: CompressionCore\n readonly store: AcpStateStore\n}\n\nexport interface NudgeOutcome {\n readonly message: UserMessage\n readonly emergency: boolean\n}\n\n/**\n * Render the compressible-range table as seq refs for the model.\n * Computed directly from the surface (not the kernel's ref map, which can\n * drift and hide large tool results) — see buildCompressibleSeqRanges.\n */\nexport function rangeTable(session: import('@deepseek-ai/dsh-session').Session): string {\n const ranges = buildCompressibleSeqRanges(session).slice(0, 6)\n if (ranges.length === 0) return ''\n const lines = ranges.map((range) => ` - seq ${range.start}..${range.end} — ${range.count} messages, ~${range.tokens} tokens`)\n return [\n '',\n 'Compressible ranges (refs are surface seqs):',\n ...lines,\n 'Compress with: compress({ content: [{ startSeq, endSeq, summary }] })',\n ].join('\\n')\n}\n\n/**\n * The token count driving pressure decisions. Prefer the host token meter's\n * SURFACE tokens (the input-side heuristic total — matches acp_status and the\n * UI's message-token view). `totalTokens` is request-AND-RESPONSE pressure and\n * can far exceed the window in long sessions (observed 230% against a real\n * ~20% occupancy), so it is not used. Falls back to the fast heuristic when\n * the meter is absent (tests, minimal hosts).\n */\nfunction measuredTokenCount(agent: Agent, coreMessages: CoreMessage[]): number {\n const meter = agent.ctx?.get?.('tokenMeter') as\n | { measure?: (session: unknown) => { surfaceTokens?: number } }\n | undefined\n const surface = meter?.measure?.(agent.session)?.surfaceTokens\n if (typeof surface === 'number' && surface > 0) return surface\n return coreMessages.reduce((sum, message) => sum + defaultCountTokens(message.text ?? ''), 0)\n}\n\n/**\n * Decide and build one nudge message for the agent's next pre-step. Returns\n * null when the kernel recommends no nudge or one was already injected for the\n * current turn (emergency nudges always bypass the dedup). Also advances the\n * in-memory kernel state (ref assignment) so the compress tool can resolve\n * seq → mNNNNN refs.\n */\nexport function buildNudge(\n agent: Agent,\n env: NudgeEnvironment,\n lastNudgeTurn: Map<string, number>,\n): NudgeOutcome | null {\n const session = agent.session\n const state = env.store.stateFor(session)\n const coreMessages = eventsToCoreMessages(surfaceEventsOf(session))\n const tokenCount = measuredTokenCount(agent, coreMessages)\n const config = kernelConfigFor(env)\n const turn = env.kernel.processTurn({ messages: coreMessages, state, config, tokenCount })\n env.store.set(session, turn.state)\n\n const nudge = turn.nudge\n if (nudge === undefined || !nudge.shouldInject) return null\n const emergency = nudge.breakdown?.emergencyOverride === 1\n\n const turnNumber = findOpenTurn(session.events) ?? 0\n const alreadyShown = !emergency && lastNudgeTurn.get(session.id) === turnNumber\n if (alreadyShown) return null\n lastNudgeTurn.set(session.id, turnNumber)\n\n const text = buildNudgeText(nudge, emergency, session)\n const message = createUserMessage({\n content: [{ type: 'text', text }],\n source: { kind: 'plugin', plugin: 'acp-nudge' },\n })\n return { message, emergency }\n}\n\n/**\n * A short ADVISORY nudge — ACP is model-driven, the model decides whether and\n * when to compress. Full guidance (tools, philosophy, summary rules) lives in\n * the system prompt once, not in every nudge.\n */\nfunction buildNudgeText(\n nudge: NudgeDecision,\n emergency: boolean,\n session: import('@deepseek-ai/dsh-session').Session,\n): string {\n // Cap the reported percentage at 100: a broken measurement (e.g. response\n // pressure folded in) must never surface as an absurd \"230%\" to the model.\n const pct = Math.round(Math.min(nudge.contextUsage, 1) * 100)\n const frame = emergency\n ? `⚠️ Context usage is at ${pct}% of the window — nearly full. Consider compressing consumed ranges soon so working context stays available; the choice and timing are yours.`\n : `Context usage is at ${pct}%. This is a suggestion, not a requirement — you decide whether and when to compress.`\n const guidance = 'Compress by need, not by percentage: replace only ranges you have genuinely consumed, with dense self-contained summaries.'\n return [frame, '', guidance, rangeTable(session)].join('\\n')\n}\n","/**\n * M4 — the ACP system-prompt section (DSH counterpart of billion-context-pi's\n * ACP_SYSTEM_PROMPT): the load-bearing compression guidance lives here, ONCE,\n * instead of being re-sent with every nudge. The nudge itself stays a short,\n * advisory notice — ACP is model-driven, the model decides whether and when\n * to compress (never \"compress now\").\n * @module billion-context-dsh/system-prompt\n */\n\nimport { COMPRESS_PHILOSOPHY } from 'acp-kernel'\n\nexport const ACP_SYSTEM_PROMPT = `Active Context Pruning — model-driven context management\n\nYOU decide whether and when to compress context. Nothing forces you: the injected \"nudge\" is a suggestion, not an order, and you may ignore it when compression would not help. Compress only ranges you have genuinely consumed (read tool outputs, finished explorations, superseded steps) that the current work no longer needs verbatim.\n\n${COMPRESS_PHILOSOPHY}\n\nCompression tools (refs are SURFACE SEQS, not ids):\n- compress: replace a seq range with your dense self-contained summary. compress({ content: [{ startSeq, endSeq, summary }] }). Edges are auto-balanced to tool-call/result boundaries; a trailing #callId fragment in a seq is ignored. Ranges must be on the current surface — stale seqs fail with guidance.\n- decompress: recover a compressed block's original content, read-only. decompress({ blockId }).\n- search_context: find information inside compressed blocks BEFORE decompressing. search_context({ query }).\n- acp_status: current context usage and the live compressible-range list. Run it before compressing when in doubt.\n\nWhen you write a summary, it becomes the ONLY record of that range: keep file paths, signatures, exact values, decisions, and error strings verbatim so a later reader (or you, after decompress) can continue without the original. Never reuse historical seqs — the surface moves as messages land and compress; verify with acp_status.`\n\n/** System-prompt section order: tool guidance lives in 100–199. */\nexport const ACP_SYSTEM_PROMPT_ORDER = 150\n"],"mappings":"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","names":["defaultCountTokens","defaultCountTokens","defaultCountTokens","defaultCountTokens","createUserMessage","defaultCountTokens","createUserMessage"]}
package/dist/nudge.d.ts CHANGED
@@ -19,6 +19,12 @@ export interface NudgeOutcome {
19
19
  readonly message: UserMessage;
20
20
  readonly emergency: boolean;
21
21
  }
22
+ /**
23
+ * Render the compressible-range table as seq refs for the model.
24
+ * Computed directly from the surface (not the kernel's ref map, which can
25
+ * drift and hide large tool results) — see buildCompressibleSeqRanges.
26
+ */
27
+ export declare function rangeTable(session: import('@deepseek-ai/dsh-session').Session): string;
22
28
  /**
23
29
  * Decide and build one nudge message for the agent's next pre-step. Returns
24
30
  * null when the kernel recommends no nudge or one was already injected for the
package/dist/region.d.ts CHANGED
@@ -27,12 +27,15 @@ export declare function findOpenTurn(events: readonly SessionEvent[]): number |
27
27
  /** Reject a second concurrent compaction for the same session. */
28
28
  export declare function assertNoActiveCompaction(events: readonly SessionEvent[]): void;
29
29
  /**
30
- * Validate one inclusive surface span and adjust its edges to the nearest
31
- * tool-pairing-balanced cuts. Missing or reversed ranges still throw; an
32
- * edge that sits inside a tool-call/result pair is nudged outward to the
33
- * closest clean cut instead (models routinely pick edges from the nudge's
34
- * range table that straddle a pair). The returned range is what a caller
35
- * should actually shadow.
30
+ * Validate one inclusive surface span and adjust its edges to a
31
+ * tool-pairing-balanced range whose boundaries carry a bare-seq ref. Missing
32
+ * or reversed ranges still throw. An edge that sits inside a tool-call/result
33
+ * pair or on a multi-tool-call assistant message that has no bare-seq ref —
34
+ * is first nudged inward to the nearest clean cut; if that collapses the range
35
+ * (e.g. the model asked for a SINGLE tool result, which can never be balanced
36
+ * alone), the range EXPANDS outward to the enclosing clean pair instead — a
37
+ * lone tool message is almost always a "consumed output" the model genuinely
38
+ * wants to compress. The returned range is what a caller should actually shadow.
36
39
  */
37
40
  export declare function resolveSurfaceRange(session: Session, start: number, end: number): {
38
41
  start: number;
@@ -59,3 +62,21 @@ export declare function runCompactionTransaction(session: Session, input: Compac
59
62
  };
60
63
  /** Rebuild the block ledger from the durable log (no kernel state needed). */
61
64
  export declare function rebuildBlockLedger(events: readonly SessionEvent[]): AcpBlockLedgerEntry[];
65
+ /** One self-computed compressible span of the current surface. */
66
+ export interface SeqCompressibleRange {
67
+ readonly start: number;
68
+ readonly end: number;
69
+ readonly count: number;
70
+ readonly tokens: number;
71
+ }
72
+ /**
73
+ * Compute compressible spans directly from the surface — independent of the
74
+ * kernel's ref map, which can drift after surface replacements in long
75
+ * sessions and hide large tool results from the nudge range table. Skips the
76
+ * recent protected tail, the last user message, and compaction checkpoints;
77
+ * edges are then balanced through resolveSurfaceRange. Ranges are ordered by
78
+ * size (largest reclaimed first).
79
+ */
80
+ export declare function buildCompressibleSeqRanges(session: Session, opts?: {
81
+ preserveRecent?: number;
82
+ }): SeqCompressibleRange[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "billion-context-dsh",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Active Context Pruning (ACP) for the DeepSeek Harness — model-driven context management as a CompactionEngine backend.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -14,7 +14,7 @@
14
14
  "files": [
15
15
  "dist",
16
16
  "README.md",
17
- "README.zh-CN.md",
17
+ "README.en.md",
18
18
  "LICENSE"
19
19
  ],
20
20
  "sideEffects": false,
@@ -41,6 +41,7 @@
41
41
  "@deepseek-ai/dsh-compaction": "0.1.0-rc.6",
42
42
  "@deepseek-ai/dsh-llm": "0.1.0-rc.6",
43
43
  "@deepseek-ai/dsh-session": "0.1.0-rc.6",
44
+ "@deepseek-ai/dsh-token-meter": "^0.1.0-rc.6",
44
45
  "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
45
46
  "@deepseek-ai/schemastery": "3.18.1",
46
47
  "@types/node": "^26.1.2",
package/README.zh-CN.md DELETED
@@ -1,116 +0,0 @@
1
- # billion-context-dsh
2
-
3
- [English](./README.md) | [中文](./README.zh-CN.md)
4
-
5
- **One billion, not one million(十亿,而非百万)。** 面向 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的 [Active Context Pruning(ACP,主动上下文裁剪)](https://github.com/ranxianglei/acp-kernel)——以 `CompactionEngine` 后端形式提供的模型驱动上下文管理。
6
-
7
- 由**模型自己决定何时压缩、压缩什么**——而不是一个硬性上限。长对话保持精简,关键细节(路径、决策、错误信息)以高保真摘要保留,且可搜索、可解压。
8
-
9
- 这是 [billion-context-pi](https://github.com/ranxianglei/billion-context-pi)(Pi 编码代理适配器)在 DSH 上的移植。压缩内核(`acp-kernel`)原样复用;适配层针对 DSH 的 durable-surface(持久化表面)模型重写——验证过的映射关系见 [docs/dsh-porting-verification.md](docs/dsh-porting-verification.md)。
10
-
11
- ## 上游项目与致谢
12
-
13
- 本项目是一个**移植/派生项目**,站在以下上游工作的肩膀上——全部为 MIT 许可。**衷心感谢** [ranxianglei](https://github.com/ranxianglei) 和 DeepSeek Harness 团队创建并开源这些项目:
14
-
15
- | 上游项目 | 作者 | 角色 |
16
- |---|---|---|
17
- | **[billion-context-pi](https://github.com/ranxianglei/billion-context-pi)** | [ranxianglei](https://github.com/ranxianglei) | 本项目移植的 Pi 编码代理适配器;适配器设计、工具语义与本项目默认配置的来源 |
18
- | **[acp-kernel](https://github.com/ranxianglei/acp-kernel)** | [ranxianglei](https://github.com/ranxianglei) | 框架无关的上下文压缩引擎——**原样复用**(refs、blocks、tiers、nudge 决策、search、status) |
19
- | **[opencode-acp](https://github.com/ranxianglei/opencode-acp)** | [ranxianglei](https://github.com/ranxianglei) | ACP("模型决定何时压缩、压缩什么")设计的源头 |
20
- | **[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)** | DeepSeek AI | 本项目所扩展的宿主平台(compaction 能力接缝、agent preset、持久化会话日志) |
21
-
22
- 本项目原样复用 `acp-kernel` 的压缩内核与 `billion-context-pi` 的默认行为;DSH 适配层(会话事件投影、持久化表面事务、模型工具、nudge、配置)为本仓库原创。上游版权与许可归其各自作者所有;本项目的许可条款见 [LICENSE](LICENSE)。
23
-
24
- ## 工作原理
25
-
26
- DSH 的每个模型请求都派生自其 append-only 会话日志(*surface*)。ACP 语义直接映射到这一模型:
27
-
28
- | ACP 概念 | DSH 实现 |
29
- |---|---|
30
- | `compress` 工具遮蔽一段范围 | 持久化 `surfaceOp: { op: 'replace' }`——模型书写的摘要成为 checkpoint 节点;原文保留在日志中 |
31
- | refs(`m00001` 标签) | 表面 seq,由 nudge 的可压缩范围表携带 |
32
- | nudge("你也许该压缩了") | 由内核的压力决策在 `agent/pre-step` 注入 |
33
- | `decompress` | 从日志只读恢复被遮蔽的原文 |
34
- | `search_context` | 对从日志重建的块摘要与原文打分 |
35
- | `acp_status` | 块账本与上下文压力 |
36
- | 块状态 | 内存内核状态 + **日志重建账本**(无旁车文件) |
37
-
38
- 刻意**不做自动摘要**:自动策略只 nudge 模型(`compactIfNeeded` 返回 null)。这正是 ACP 的成本优势——模型书写一份紧凑摘要,而不是为第二次 LLM 摘要调用付费。
39
-
40
- ## 安装 / 挂载
41
-
42
- 本包是即插即用的压缩后端。在宿主组合(或 agent preset 的 compaction realm)中添加一行:
43
-
44
- ```yaml
45
- # 宿主组合(例如 profile 的 cordis.patch.yml)
46
- - insert:
47
- - id: compaction-billion-context
48
- name: 'billion-context-dsh'
49
- config:
50
- modelContextLimit: 128000 # 默认;压力窗口
51
- ```
52
-
53
- 要为某个 agent 替换 `dsh-compaction-basic`,改为挂载在 preset 的 `compaction` isolate realm 中:
54
-
55
- ```yaml
56
- - id: compaction
57
- name: cordis:group
58
- group: true
59
- isolate:
60
- compaction: true
61
- config:
62
- - id: compaction-acp
63
- name: 'billion-context-dsh'
64
- config:
65
- modelContextLimit: 128000
66
- ```
67
-
68
- 当宿主上下文提供 `ctx.tools` / `ctx.commands` 时,引擎还会注册:
69
-
70
- - `compress` — 用你书写的紧凑摘要替换范围
71
- - `decompress` — 恢复某块的原始内容(只读)
72
- - `search_context` — 在压缩块内查找信息
73
- - `acp_status` — 块账本与压力
74
- - `/acp` — 从命令栏执行 status / compress / decompress
75
-
76
- ## 配置
77
-
78
- | 键 | 默认值 | 含义 |
79
- |---|---|---|
80
- | `modelContextLimit` | `128000` | 用于内核压力决策的上下文窗口 |
81
- | `nudgeMinContextLimitPct` | 内核默认 `0.45` | Nudge 窗口下界(用量占比)——与 billion-context-pi 相同的默认值 |
82
- | `nudgeMaxContextLimitPct` | 内核默认 `0.75` | 过限线:超过此值则无论增长与否都触发 nudge |
83
- | `nudgeEmergencyThresholdPct` | 内核默认 `0.95` | 紧急 nudge(绕过每轮去重) |
84
- | `coreOverrides` | — | 任何其他 acp-kernel `Config` 覆盖(billion-context-pi 的 `coreOverrides` 逃生口) |
85
- | `autoTools` | `true` | 在 `ctx.tools` 注册四个模型工具 |
86
- | `autoCommand` | `true` | 在 `ctx.commands` 注册 `/acp` 命令 |
87
- | `autoNudge` | `true` | 当内核建议时向 `agent/pre-step` 注入 nudge |
88
-
89
- ## 开发
90
-
91
- ```bash
92
- npm install
93
- npm run typecheck # 严格 TS
94
- npm test # node --import tsx --test tests/*.test.ts
95
- npm run build # tsup 打包(内联 acp-kernel)+ .d.ts
96
- ```
97
-
98
- `dist/index.js` 自包含,仅外链 `@deepseek-ai/*` 接缝包(由宿主部署提供)。
99
-
100
- ## 架构
101
-
102
- ```
103
- src/
104
- ├── index.ts # AcpCompactionEngine(CompactionEngine 后端)+ 接线
105
- ├── messages.ts # M1: 会话事件 ↔ acp-kernel CoreMessage 投影
106
- ├── state.ts # M2: 每会话内核状态
107
- ├── region.ts # M5: 持久化区域事务 + 日志重建块账本
108
- ├── tools.ts # M3: compress / decompress / search_context / acp_status
109
- ├── nudge.ts # M4: 内核压力决策 → 注入的建议式 nudge 消息
110
- ├── system-prompt.ts # M4: ACP 指引系统提示段(一次性,不进 nudge)
111
- └── commands.ts # M4: /acp 斜杠命令
112
- ```
113
-
114
- ## License
115
-
116
- MIT