billion-context-dsh 0.1.9 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +13 -1
- package/README.md +12 -1
- package/cordis.patch.yml +11 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +153 -38
- package/dist/index.js.map +1 -1
- package/dist/nudge.d.ts +5 -2
- package/dist/prompts.d.ts +85 -0
- package/dist/system-prompt.d.ts +5 -1
- package/dist/tools.d.ts +3 -0
- package/package.json +19 -1
package/README.en.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[English](./README.en.md) | [中文](./README.md)
|
|
4
4
|
|
|
5
5
|
> **⚠️ Beta notice — not for production use**
|
|
6
|
-
> This project (**v0.
|
|
6
|
+
> This project (**v0.2.0**) is a work-in-progress beta. The [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) itself is also in **public beta**. **Do not use either in engineering / production environments** — expect breaking changes and rough edges.
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
9
|
<strong>Built with gratitude on top of these projects</strong> — please give them a ⭐:
|
|
@@ -56,6 +56,18 @@ This is the DeepSeek Harness port of [billion-context-pi](https://github.com/ran
|
|
|
56
56
|
npm install billion-context-dsh
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
> 💡 **One-command install via `dsh plugin` (bundle, v0.2.0+)**. The package declares a `dsh.bundle`
|
|
60
|
+
> manifest, so DSH's plugin command installs it into the profile and applies the patch
|
|
61
|
+
> automatically (equivalent to the composition row below):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
dsh plugin --profile web add billion-context-dsh
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Restart `dsh` afterwards (bundle layers are composed at startup). For custom `config`
|
|
68
|
+
(such as `modelContextLimit` / `prompts`), keep the hand-written composition row — the
|
|
69
|
+
bundle patch ([cordis.patch.yml](cordis.patch.yml)) only inserts the default row without `config`.
|
|
70
|
+
|
|
59
71
|
That's it. Then add a composition row where a compaction backend is expected — two scopes, pick by how wide you want it:
|
|
60
72
|
|
|
61
73
|
**Global — host plane, every mode** (recommended). In your profile patch (e.g. `~/.dsh/profiles/web/cordis.patch.yml`), add:
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[中文](./README.md) | [English](./README.en.md)
|
|
4
4
|
|
|
5
5
|
> **⚠️ 测试版声明——请勿用于生产环境**
|
|
6
|
-
> 本项目(**v0.
|
|
6
|
+
> 本项目(**v0.2.0**)仍处于开发中的测试版。[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 本身也处于**公开测试版**阶段。**请勿将两者用于工程化 / 生产环境**——预期会有破坏性变更与粗糙之处。
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
9
|
<strong>衷心感谢以下项目——请给它们一个 ⭐:</strong>
|
|
@@ -59,6 +59,17 @@
|
|
|
59
59
|
npm install billion-context-dsh
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
> 💡 **v0.2.0 起支持 `dsh plugin` 一键安装(bundle)**。包已声明 `dsh.bundle`
|
|
63
|
+
> manifest,DSH 的插件命令会把它装进 profile 并自动应用补丁(等价于下面的组合行):
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
dsh plugin --profile web add billion-context-dsh
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
装完重启 `dsh`(bundle 层在启动时组合)。需要自定义 `config`(如
|
|
70
|
+
`modelContextLimit` / `prompts`)时仍建议手写组合行——bundle 补丁
|
|
71
|
+
([cordis.patch.yml](cordis.patch.yml))只插入无 `config` 的默认行。
|
|
72
|
+
|
|
62
73
|
就这样。然后在需要压缩后端的位置加组合配置——两种范围,按需选择:
|
|
63
74
|
|
|
64
75
|
**全局生效(host 平面,所有模式)——推荐**。在你的 profile 补丁(如 `~/.dsh/profiles/web/cordis.patch.yml`)中追加:
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# dsh bundle patch: inserts the ACP compaction backend into a profile's layer stack.
|
|
2
|
+
#
|
|
3
|
+
# This is what makes the package installable via `dsh plugin --profile web add
|
|
4
|
+
# billion-context-dsh` (declared in package.json as `dsh.bundle.patch`). The row
|
|
5
|
+
# below is exactly the manual composition row documented in docs/INSTALL.md (§2a),
|
|
6
|
+
# minus config: omitting `modelContextLimit` means auto-detection (fallback 128000),
|
|
7
|
+
# and the built-in prompt copy (config.prompts) is used. Users who want custom
|
|
8
|
+
# config can still write the row by hand in their profile patch.
|
|
9
|
+
- insert:
|
|
10
|
+
- id: compaction-acp
|
|
11
|
+
name: 'billion-context-dsh'
|
package/dist/index.d.ts
CHANGED
|
@@ -30,10 +30,12 @@ import { CompactionEngine, type CompactionAgentContext, type CompactionResult, t
|
|
|
30
30
|
import { type CompressionCore } from 'acp-kernel';
|
|
31
31
|
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
32
32
|
import { AcpStateStore } from './state.ts';
|
|
33
|
+
import { type AcpPrompts, type ResolvedPrompts } from './prompts.ts';
|
|
33
34
|
import { type AcpWindow } from './window.ts';
|
|
34
35
|
export { AcpStateStore } from './state.ts';
|
|
35
36
|
export { kernelConfigFor, type KernelConfigInput } from './config.ts';
|
|
36
37
|
export { ACP_SYSTEM_PROMPT, ACP_SYSTEM_PROMPT_ORDER } from './system-prompt.ts';
|
|
38
|
+
export { DEFAULT_PROMPTS, DEFAULT_RESOLVED, renderSystemPrompt, renderTemplate, resolvePrompts, type AcpPrompts, type NudgePrompts, type PromptInput, type PromptOverride, type RangeTablePrompts, type ResolvedPrompts, type ToolPrompts, } from './prompts.ts';
|
|
37
39
|
export { makeTools, type ToolEnvironment } from './tools.ts';
|
|
38
40
|
export { acpCommand } from './commands.ts';
|
|
39
41
|
export { buildNudge, resolveTokenCount, type NudgeEnvironment, type NudgeOutcome } from './nudge.ts';
|
|
@@ -87,6 +89,8 @@ export interface AcpConfig {
|
|
|
87
89
|
readonly autoCommand: boolean;
|
|
88
90
|
/** Inject the nudge into `agent/pre-step` when the kernel recommends it. Default true. */
|
|
89
91
|
readonly autoNudge: boolean;
|
|
92
|
+
/** Per-stage prompt template overrides (nudge / range table / system prompt / tool descriptions). See docs/configurable-prompts-design.md. */
|
|
93
|
+
readonly prompts?: AcpPrompts;
|
|
90
94
|
}
|
|
91
95
|
export declare function resolveAcpConfig(config?: Partial<AcpConfig>): AcpConfig;
|
|
92
96
|
/**
|
|
@@ -101,6 +105,8 @@ export declare class AcpCompactionEngine extends CompactionEngine {
|
|
|
101
105
|
readonly store: AcpStateStore;
|
|
102
106
|
/** Resolved engine configuration. */
|
|
103
107
|
readonly config: AcpConfig;
|
|
108
|
+
/** Resolved prompt templates (validated at construction — fail-fast on template typos). */
|
|
109
|
+
readonly prompts: ResolvedPrompts;
|
|
104
110
|
private readonly lastNudgeTurn;
|
|
105
111
|
/** Per provider/model route the resolved window (probe failures cached too). */
|
|
106
112
|
private readonly windowCache;
|
package/dist/index.js
CHANGED
|
@@ -585,6 +585,111 @@ import {
|
|
|
585
585
|
defaultCountTokens as defaultCountTokens2
|
|
586
586
|
} from "acp-kernel";
|
|
587
587
|
import { createUserMessage as createUserMessage2 } from "@deepseek-ai/dsh-llm";
|
|
588
|
+
|
|
589
|
+
// src/prompts.ts
|
|
590
|
+
import { COMPRESS_PHILOSOPHY } from "acp-kernel";
|
|
591
|
+
var NUDGE_ALLOWED = {
|
|
592
|
+
normal: /* @__PURE__ */ new Set(["pct"]),
|
|
593
|
+
emergency: /* @__PURE__ */ new Set(["pct"]),
|
|
594
|
+
guidance: /* @__PURE__ */ new Set(),
|
|
595
|
+
tier: /* @__PURE__ */ new Set(["tier", "count", "prevTier", "tokens", "seqs"])
|
|
596
|
+
};
|
|
597
|
+
var RANGE_TABLE_ALLOWED = {
|
|
598
|
+
header: /* @__PURE__ */ new Set(["surface"]),
|
|
599
|
+
title: /* @__PURE__ */ new Set(["count"]),
|
|
600
|
+
line: /* @__PURE__ */ new Set(["start", "end", "count", "tokens"]),
|
|
601
|
+
footer: /* @__PURE__ */ new Set()
|
|
602
|
+
};
|
|
603
|
+
var TOOLS_ALLOWED = {
|
|
604
|
+
compress: /* @__PURE__ */ new Set(),
|
|
605
|
+
decompress: /* @__PURE__ */ new Set(),
|
|
606
|
+
searchContext: /* @__PURE__ */ new Set(),
|
|
607
|
+
acpStatus: /* @__PURE__ */ new Set()
|
|
608
|
+
};
|
|
609
|
+
var SYSTEM_ALLOWED = /* @__PURE__ */ new Set(["philosophy"]);
|
|
610
|
+
function validateTemplate(template, allowed, path) {
|
|
611
|
+
const re = /\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
612
|
+
let match;
|
|
613
|
+
while ((match = re.exec(template)) !== null) {
|
|
614
|
+
const name = match[1];
|
|
615
|
+
if (!allowed.has(name)) {
|
|
616
|
+
throw new Error(
|
|
617
|
+
`${path} contains unknown placeholder {${name}} \u2014 allowed: ${[...allowed].join(", ") || "(none)"}`
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return template;
|
|
622
|
+
}
|
|
623
|
+
function renderTemplate(template, vars) {
|
|
624
|
+
return template.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_match, name) => {
|
|
625
|
+
const value = vars[name];
|
|
626
|
+
if (value === void 0) {
|
|
627
|
+
throw new Error(
|
|
628
|
+
`renderTemplate: missing value for placeholder {${name}} in template "${template.slice(0, 60)}\u2026"`
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
return String(value);
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
function mergeGroup(defaults, override, allowed, path) {
|
|
635
|
+
if (override == null) return defaults;
|
|
636
|
+
const out = {};
|
|
637
|
+
for (const key of Object.keys(defaults)) {
|
|
638
|
+
const value = override[key];
|
|
639
|
+
out[key] = value === null || value === void 0 ? defaults[key] : validateTemplate(value, allowed[key], `${path}.${String(key)}`);
|
|
640
|
+
}
|
|
641
|
+
return out;
|
|
642
|
+
}
|
|
643
|
+
function resolvePrompts(input) {
|
|
644
|
+
if (input === void 0) return DEFAULT_RESOLVED;
|
|
645
|
+
return {
|
|
646
|
+
nudge: mergeGroup(DEFAULT_PROMPTS.nudge, input.nudge, NUDGE_ALLOWED, "prompts.nudge"),
|
|
647
|
+
rangeTable: mergeGroup(DEFAULT_PROMPTS.rangeTable, input.rangeTable, RANGE_TABLE_ALLOWED, "prompts.rangeTable"),
|
|
648
|
+
tools: mergeGroup(DEFAULT_PROMPTS.tools, input.tools, TOOLS_ALLOWED, "prompts.tools"),
|
|
649
|
+
systemPromptTemplate: input.systemPrompt === null || input.systemPrompt === void 0 ? DEFAULT_PROMPTS.systemPromptTemplate : validateTemplate(input.systemPrompt, SYSTEM_ALLOWED, "prompts.systemPrompt")
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
function renderSystemPrompt(prompts) {
|
|
653
|
+
return renderTemplate(prompts.systemPromptTemplate, { philosophy: COMPRESS_PHILOSOPHY });
|
|
654
|
+
}
|
|
655
|
+
var DEFAULT_PROMPTS = {
|
|
656
|
+
nudge: {
|
|
657
|
+
normal: "Context usage is at {pct}%. This is a suggestion, not a requirement \u2014 you decide whether and when to compress.",
|
|
658
|
+
emergency: "\u26A0\uFE0F Context usage is at {pct}% of the window \u2014 nearly full. Consider compressing consumed ranges soon so working context stays available; the choice and timing are yours.",
|
|
659
|
+
guidance: "Compress by need, not by percentage: replace only ranges you have genuinely consumed, with dense self-contained summaries.",
|
|
660
|
+
tier: "Tier {tier}: {count} tier-{prevTier} block(s) distillable ({tokens} tokens) \u2014 compress their summary node(s) [seqs {seqs}] to reclaim the original messages."
|
|
661
|
+
},
|
|
662
|
+
rangeTable: {
|
|
663
|
+
header: "Surface: {surface}",
|
|
664
|
+
title: "Compressible ranges (suggestions only \u2014 compress any consumed span; refs are surface seqs):",
|
|
665
|
+
line: " - seq {start}..{end} \u2014 {count} messages, ~{tokens} tokens",
|
|
666
|
+
footer: "Compress with: compress({ content: [{ startSeq, endSeq, summary }] }) \u2014 content is an array: batch multiple unrelated segments in one call, each entry its own block. Keep ranges disjoint.\nSnapshot taken at nudge time: the seqs go stale once the surface moves (a later compress shadows them), so re-run acp_status for fresh refs before compressing."
|
|
667
|
+
},
|
|
668
|
+
tools: {
|
|
669
|
+
compress: "Replace older conversation ranges with dense summaries you write. Each message seq is a surface reference. Single range: compress({ content: [{ startSeq, endSeq, summary }] }). Batch multiple unrelated ranges in one call (each content entry becomes its own block); keep ranges disjoint. Never compress content the current step is actively using. Seq refs must come from the CURRENT surface (acp_status or the latest nudge): a span whose edges were shadowed by an earlier compress is auto-remapped to its still-live content, a fully compressed span is reported as already compressed, and invented/other-session seqs fail with guidance.",
|
|
670
|
+
decompress: "Recover the original content of a compressed block by its blockId (read-only; does not unshadow the range).",
|
|
671
|
+
searchContext: "Search inside compressed blocks (summaries and original content) for information the model no longer sees in context.",
|
|
672
|
+
acpStatus: "Report the ACP block ledger: compressed blocks, reclaimed tokens, and current context pressure."
|
|
673
|
+
},
|
|
674
|
+
systemPromptTemplate: `Active Context Pruning \u2014 model-driven context management
|
|
675
|
+
|
|
676
|
+
YOU 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.
|
|
677
|
+
|
|
678
|
+
{philosophy}
|
|
679
|
+
|
|
680
|
+
Compression tools (refs are SURFACE SEQS, not ids):
|
|
681
|
+
- compress: replace one or more seq ranges, each with your own dense summary. Single range: compress({ content: [{ startSeq, endSeq, summary }] }). Batch multiple unrelated segments in one call (each entry becomes its own block): compress({ content: [{ startSeq: 1, endSeq: 5, summary: '...' }, { startSeq: 12, endSeq: 18, summary: '...' }] }). Keep ranges disjoint \u2014 overlapping entries in one batch are skipped. Edges are auto-balanced to tool-call/result boundaries; a trailing #callId fragment in a seq is ignored. Seq refs must be on the current surface: seqs from older nudges or earlier compresses go stale as the surface moves, so a stale span is auto-remapped to its still-live remainder (the result reports the adjusted span), a fully compressed span is reported as already compressed, and invented/other-session seqs fail with guidance.
|
|
682
|
+
- decompress: recover a compressed block's original content, read-only. decompress({ blockId }).
|
|
683
|
+
- search_context: find information inside compressed blocks BEFORE decompressing. search_context({ query }).
|
|
684
|
+
- acp_status: current context usage and the live compressible-range list. Run it right before compressing \u2014 the only seqs that never go stale are the ones you just read.
|
|
685
|
+
|
|
686
|
+
Tiered compression: each compressed block appears on the surface as one summary node. Compressing that node again DISTILLS the block (tier 2): the parent summary folds into your new summary and the original messages are freed. Distilling a tier-2 block yields tier 3. Distill when a summary itself is consumed \u2014 decompress on the tier-2 block recovers the full originals.
|
|
687
|
+
|
|
688
|
+
When 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 \u2014 the surface moves as messages land and compress; verify with acp_status.`
|
|
689
|
+
};
|
|
690
|
+
var DEFAULT_RESOLVED = DEFAULT_PROMPTS;
|
|
691
|
+
|
|
692
|
+
// src/nudge.ts
|
|
588
693
|
function resolveTokenCount(agent, coreMessages) {
|
|
589
694
|
const projections = agent.ctx?.get?.("sessionProjections");
|
|
590
695
|
const projected = projections?.snapshot?.(agent.session)?.values?.contextPressure?.projectedTokens;
|
|
@@ -594,17 +699,24 @@ function resolveTokenCount(agent, coreMessages) {
|
|
|
594
699
|
if (typeof surface === "number" && surface > 0) return surface;
|
|
595
700
|
return coreMessages.reduce((sum, message) => sum + defaultCountTokens2(message.text ?? ""), 0);
|
|
596
701
|
}
|
|
597
|
-
function rangeTable(session) {
|
|
702
|
+
function rangeTable(session, prompts = DEFAULT_RESOLVED) {
|
|
598
703
|
const ranges = buildCompressibleSeqRanges(session).slice(0, 6);
|
|
599
704
|
if (ranges.length === 0) return "";
|
|
600
|
-
const lines = ranges.map(
|
|
705
|
+
const lines = ranges.map(
|
|
706
|
+
(range) => renderTemplate(prompts.rangeTable.line, {
|
|
707
|
+
start: range.start,
|
|
708
|
+
end: range.end,
|
|
709
|
+
count: range.count,
|
|
710
|
+
tokens: range.tokens
|
|
711
|
+
})
|
|
712
|
+
);
|
|
601
713
|
return [
|
|
714
|
+
// 前导空串元素产生 nudge 中范围表前的唯一空行(§4:parts 层不再加分隔)。
|
|
602
715
|
"",
|
|
603
|
-
|
|
604
|
-
|
|
716
|
+
renderTemplate(prompts.rangeTable.header, { surface: surfaceSummary(session) }),
|
|
717
|
+
renderTemplate(prompts.rangeTable.title, { count: ranges.length }),
|
|
605
718
|
...lines,
|
|
606
|
-
|
|
607
|
-
"Snapshot taken at nudge time: the seqs go stale once the surface moves (a later compress shadows them), so re-run acp_status for fresh refs before compressing."
|
|
719
|
+
prompts.rangeTable.footer
|
|
608
720
|
].join("\n");
|
|
609
721
|
}
|
|
610
722
|
function measuredTokenCount(agent, coreMessages) {
|
|
@@ -626,28 +738,36 @@ function buildNudge(agent, env, lastNudgeTurn) {
|
|
|
626
738
|
const alreadyShown = !emergency && lastNudgeTurn.get(session.id) === turnNumber;
|
|
627
739
|
if (alreadyShown) return null;
|
|
628
740
|
lastNudgeTurn.set(session.id, turnNumber);
|
|
629
|
-
const text = buildNudgeText(nudge, emergency, session);
|
|
741
|
+
const text = buildNudgeText(nudge, emergency, session, env.prompts);
|
|
630
742
|
const message = createUserMessage2({
|
|
631
743
|
content: [{ type: "text", text }],
|
|
632
744
|
source: { kind: "plugin", plugin: "acp-nudge" }
|
|
633
745
|
});
|
|
634
746
|
return { message, emergency };
|
|
635
747
|
}
|
|
636
|
-
function buildNudgeText(nudge, emergency, session) {
|
|
748
|
+
function buildNudgeText(nudge, emergency, session, prompts = DEFAULT_RESOLVED) {
|
|
637
749
|
const pct = Math.round(Math.min(nudge.contextUsage, 1) * 100);
|
|
638
|
-
const frame =
|
|
639
|
-
|
|
640
|
-
|
|
750
|
+
const frame = renderTemplate(
|
|
751
|
+
emergency ? prompts.nudge.emergency : prompts.nudge.normal,
|
|
752
|
+
{ pct }
|
|
753
|
+
);
|
|
754
|
+
const parts = [frame];
|
|
755
|
+
if (prompts.nudge.guidance !== "") parts.push("", prompts.nudge.guidance);
|
|
641
756
|
if ((nudge.tier === 2 || nudge.tier === 3) && (nudge.tierTargetBlocks?.length ?? 0) > 0) {
|
|
642
757
|
const targets = nudge.tierTargetBlocks;
|
|
643
758
|
const summarySeqs = targets.map((block) => summarySeqOfKernelBlock(session, block.blockId)).filter((seq) => seq !== null);
|
|
644
759
|
const pending = nudge.tier === 2 ? nudge.breakdown?.pendingT2 : nudge.breakdown?.pendingT3;
|
|
645
760
|
const tokens = typeof pending === "number" ? pending : 0;
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
761
|
+
const tierLine = renderTemplate(prompts.nudge.tier, {
|
|
762
|
+
tier: nudge.tier,
|
|
763
|
+
count: targets.length,
|
|
764
|
+
prevTier: nudge.tier - 1,
|
|
765
|
+
tokens,
|
|
766
|
+
seqs: summarySeqs.join(", ")
|
|
767
|
+
});
|
|
768
|
+
if (tierLine !== "") parts.push(tierLine);
|
|
649
769
|
}
|
|
650
|
-
parts.push(rangeTable(session));
|
|
770
|
+
parts.push(rangeTable(session, prompts));
|
|
651
771
|
return parts.join("\n");
|
|
652
772
|
}
|
|
653
773
|
|
|
@@ -935,10 +1055,11 @@ async function handleStatus(env, _args, exec) {
|
|
|
935
1055
|
return { text: lines.join("\n") };
|
|
936
1056
|
}
|
|
937
1057
|
function makeTools(env) {
|
|
1058
|
+
const prompts = env.prompts ?? DEFAULT_RESOLVED;
|
|
938
1059
|
return [
|
|
939
1060
|
defineTool({
|
|
940
1061
|
name: "compress",
|
|
941
|
-
description:
|
|
1062
|
+
description: prompts.tools.compress,
|
|
942
1063
|
parameters: compressParameters,
|
|
943
1064
|
output: textOutput(),
|
|
944
1065
|
async execute(args, exec) {
|
|
@@ -947,7 +1068,7 @@ function makeTools(env) {
|
|
|
947
1068
|
}),
|
|
948
1069
|
defineTool({
|
|
949
1070
|
name: "decompress",
|
|
950
|
-
description:
|
|
1071
|
+
description: prompts.tools.decompress,
|
|
951
1072
|
parameters: decompressParameters,
|
|
952
1073
|
output: textOutput(),
|
|
953
1074
|
execute(args, exec) {
|
|
@@ -956,7 +1077,7 @@ function makeTools(env) {
|
|
|
956
1077
|
}),
|
|
957
1078
|
defineTool({
|
|
958
1079
|
name: "search_context",
|
|
959
|
-
description:
|
|
1080
|
+
description: prompts.tools.searchContext,
|
|
960
1081
|
parameters: searchParameters,
|
|
961
1082
|
output: textOutput(),
|
|
962
1083
|
execute(args, exec) {
|
|
@@ -965,7 +1086,7 @@ function makeTools(env) {
|
|
|
965
1086
|
}),
|
|
966
1087
|
defineTool({
|
|
967
1088
|
name: "acp_status",
|
|
968
|
-
description:
|
|
1089
|
+
description: prompts.tools.acpStatus,
|
|
969
1090
|
parameters: statusParameters,
|
|
970
1091
|
output: textOutput(),
|
|
971
1092
|
execute(args, exec) {
|
|
@@ -1062,22 +1183,7 @@ function acpCommand(env) {
|
|
|
1062
1183
|
}
|
|
1063
1184
|
|
|
1064
1185
|
// src/system-prompt.ts
|
|
1065
|
-
|
|
1066
|
-
var ACP_SYSTEM_PROMPT = `Active Context Pruning \u2014 model-driven context management
|
|
1067
|
-
|
|
1068
|
-
YOU 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.
|
|
1069
|
-
|
|
1070
|
-
${COMPRESS_PHILOSOPHY}
|
|
1071
|
-
|
|
1072
|
-
Compression tools (refs are SURFACE SEQS, not ids):
|
|
1073
|
-
- compress: replace one or more seq ranges, each with your own dense summary. Single range: compress({ content: [{ startSeq, endSeq, summary }] }). Batch multiple unrelated segments in one call (each entry becomes its own block): compress({ content: [{ startSeq: 1, endSeq: 5, summary: '...' }, { startSeq: 12, endSeq: 18, summary: '...' }] }). Keep ranges disjoint \u2014 overlapping entries in one batch are skipped. Edges are auto-balanced to tool-call/result boundaries; a trailing #callId fragment in a seq is ignored. Seq refs must be on the current surface: seqs from older nudges or earlier compresses go stale as the surface moves, so a stale span is auto-remapped to its still-live remainder (the result reports the adjusted span), a fully compressed span is reported as already compressed, and invented/other-session seqs fail with guidance.
|
|
1074
|
-
- decompress: recover a compressed block's original content, read-only. decompress({ blockId }).
|
|
1075
|
-
- search_context: find information inside compressed blocks BEFORE decompressing. search_context({ query }).
|
|
1076
|
-
- acp_status: current context usage and the live compressible-range list. Run it right before compressing \u2014 the only seqs that never go stale are the ones you just read.
|
|
1077
|
-
|
|
1078
|
-
Tiered compression: each compressed block appears on the surface as one summary node. Compressing that node again DISTILLS the block (tier 2): the parent summary folds into your new summary and the original messages are freed. Distilling a tier-2 block yields tier 3. Distill when a summary itself is consumed \u2014 decompress on the tier-2 block recovers the full originals.
|
|
1079
|
-
|
|
1080
|
-
When 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 \u2014 the surface moves as messages land and compress; verify with acp_status.`;
|
|
1186
|
+
var ACP_SYSTEM_PROMPT = renderSystemPrompt(DEFAULT_PROMPTS);
|
|
1081
1187
|
var ACP_SYSTEM_PROMPT_ORDER = 150;
|
|
1082
1188
|
|
|
1083
1189
|
// src/index.ts
|
|
@@ -1104,12 +1210,15 @@ var AcpCompactionEngine = class extends CompactionEngine {
|
|
|
1104
1210
|
store;
|
|
1105
1211
|
/** Resolved engine configuration. */
|
|
1106
1212
|
config;
|
|
1213
|
+
/** Resolved prompt templates (validated at construction — fail-fast on template typos). */
|
|
1214
|
+
prompts;
|
|
1107
1215
|
lastNudgeTurn = /* @__PURE__ */ new Map();
|
|
1108
1216
|
/** Per provider/model route the resolved window (probe failures cached too). */
|
|
1109
1217
|
windowCache = /* @__PURE__ */ new Map();
|
|
1110
1218
|
constructor(ctx, config = {}) {
|
|
1111
1219
|
super(ctx);
|
|
1112
1220
|
this.config = resolveAcpConfig(config);
|
|
1221
|
+
this.prompts = resolvePrompts(config.prompts);
|
|
1113
1222
|
const ports = this.config.countTokens !== void 0 ? { countTokens: this.config.countTokens } : {};
|
|
1114
1223
|
this.kernel = createCore(ports);
|
|
1115
1224
|
this.store = new AcpStateStore();
|
|
@@ -1122,7 +1231,8 @@ var AcpCompactionEngine = class extends CompactionEngine {
|
|
|
1122
1231
|
nudgeMaxContextLimitPct: this.config.nudgeMaxContextLimitPct,
|
|
1123
1232
|
nudgeEmergencyThresholdPct: this.config.nudgeEmergencyThresholdPct,
|
|
1124
1233
|
coreOverrides: this.config.coreOverrides,
|
|
1125
|
-
windowFor: (agent) => this.windowFor(agent)
|
|
1234
|
+
windowFor: (agent) => this.windowFor(agent),
|
|
1235
|
+
prompts: this.prompts
|
|
1126
1236
|
};
|
|
1127
1237
|
const tools = ctx.get("tools");
|
|
1128
1238
|
if (tools !== void 0) {
|
|
@@ -1171,7 +1281,7 @@ var AcpCompactionEngine = class extends CompactionEngine {
|
|
|
1171
1281
|
systemPrompt.section({
|
|
1172
1282
|
name: "billion-context-dsh",
|
|
1173
1283
|
order: ACP_SYSTEM_PROMPT_ORDER,
|
|
1174
|
-
text:
|
|
1284
|
+
text: renderSystemPrompt(this.prompts)
|
|
1175
1285
|
});
|
|
1176
1286
|
} else {
|
|
1177
1287
|
let done = false;
|
|
@@ -1183,7 +1293,7 @@ var AcpCompactionEngine = class extends CompactionEngine {
|
|
|
1183
1293
|
registry.section({
|
|
1184
1294
|
name: "billion-context-dsh",
|
|
1185
1295
|
order: ACP_SYSTEM_PROMPT_ORDER,
|
|
1186
|
-
text:
|
|
1296
|
+
text: renderSystemPrompt(this.prompts)
|
|
1187
1297
|
});
|
|
1188
1298
|
};
|
|
1189
1299
|
ctx.on("internal/service", (name) => {
|
|
@@ -1248,6 +1358,8 @@ export {
|
|
|
1248
1358
|
AcpStateStore,
|
|
1249
1359
|
AlreadyCompressedRangeError,
|
|
1250
1360
|
DEFAULT_CONTEXT_WINDOW,
|
|
1361
|
+
DEFAULT_PROMPTS,
|
|
1362
|
+
DEFAULT_RESOLVED,
|
|
1251
1363
|
acpCommand,
|
|
1252
1364
|
assertNoActiveCompaction,
|
|
1253
1365
|
blockRefForSummarySeq,
|
|
@@ -1264,7 +1376,10 @@ export {
|
|
|
1264
1376
|
makeTools,
|
|
1265
1377
|
projectEvent,
|
|
1266
1378
|
rebuildBlockLedger,
|
|
1379
|
+
renderSystemPrompt,
|
|
1380
|
+
renderTemplate,
|
|
1267
1381
|
resolveAcpConfig,
|
|
1382
|
+
resolvePrompts,
|
|
1268
1383
|
resolveSurfaceRange,
|
|
1269
1384
|
resolveTokenCount,
|
|
1270
1385
|
runCompactionTransaction,
|