billion-context-dsh 0.2.25 → 0.2.26
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 +6 -4
- package/README.md +6 -4
- package/dist/index.d.ts +25 -0
- package/dist/index.js +235 -26
- package/dist/index.js.map +1 -1
- package/dist/messages.d.ts +85 -15
- package/dist/nudge.d.ts +8 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[English](./README.en.md) | [中文](./README.md)
|
|
4
4
|
|
|
5
5
|
> **⚠️ Beta notice — not for production use**
|
|
6
|
-
> This project (**v0.2.
|
|
6
|
+
> This project (**v0.2.26**) 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 ⭐:
|
|
@@ -42,7 +42,7 @@ When conversations get long, the model runs out of context. Most tools hard-trun
|
|
|
42
42
|
Unlike DSH's built-in auto-compaction (which replaces a range with an automatically generated summary), billion-context-dsh:
|
|
43
43
|
|
|
44
44
|
- **Model-driven** — the model writes the summary itself; there is no second LLM summarization call
|
|
45
|
-
- **Advisory, never imperative** — automatic policy only *nudges*; the model decides whether and when to compress
|
|
45
|
+
- **Advisory, never imperative** — automatic policy only *nudges*; the model decides whether and when to compress (one exception: a provider-confirmed context overflow lets the engine recover once by itself — see `maxOverflowRetries` in the configuration table)
|
|
46
46
|
- **Durable & recoverable** — a compressed range becomes a checkpoint node, the originals stay in the append-only session log; `decompress` restores them, `search_context` finds information inside blocks
|
|
47
47
|
- **Long tasks hold steady** — every step builds on the results before it; key conclusions stay usable and compound, so very long tasks actually finish
|
|
48
48
|
- **Context stays lean** — every request rides on a small, distilled slice of context with only the key information; no bulk compression of large ranges, so details don't decay with it — and tokens stay low
|
|
@@ -100,7 +100,7 @@ This only installs the package into your project/global store; it does **not** t
|
|
|
100
100
|
**Install from the git source (`github:` spec — the form the plugin store shows).** The prebuilt `dist/` artifacts are committed to this repository, so a git-source install also works out of the box — **no build step needed**, and pnpm 11's default build-script blocking (`allowBuilds`) never applies to this package:
|
|
101
101
|
|
|
102
102
|
```bash
|
|
103
|
-
dsh plugin --profile web add github:Tyan66666/billion-context-dsh#v0.2.
|
|
103
|
+
dsh plugin --profile web add github:Tyan66666/billion-context-dsh#v0.2.26
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
Prefer a `#<tag>` ref to get artifacts identical to that npm release; without a ref you get the latest default-branch build. Only building the repo yourself (`npm run build`) requires approving build scripts. Background and trade-offs: [docs/git-source-install-design.md](docs/git-source-install-design.md) (issue #92).
|
|
@@ -167,7 +167,7 @@ compaction-acp:
|
|
|
167
167
|
/acp-prune config reset all
|
|
168
168
|
```
|
|
169
169
|
|
|
170
|
-
Changing a window key (`modelContextLimit` / `autoModelContextLimit`) clears the window-probe cache — the next pre-step re-probes under the new values (probe failures are cached too, so this is also how a fixed gateway gets re-probed). In provider-less plain-npm compositions `/acp-prune config` degrades to advice text; `settingsEnabled: false` disables the integration entirely (composition-row-only — the switch is deliberately NOT part of the settings layer: it cannot turn itself off). Design details: [docs/settings-integration-design.md](docs/settings-integration-design.md).
|
|
170
|
+
Changing a window key (`modelContextLimit` / `autoModelContextLimit`) clears the window-probe cache — the next pre-step re-probes under the new values (probe failures are cached too, so this is also how a fixed gateway gets re-probed). In provider-less plain-npm compositions `/acp-prune config` degrades to advice text; on DSH lines ≥0.1.7, where the host settings service no longer provides `installSection` (outside this plugin's declared peer range), the engine degrades cleanly too — one startup warning, the six keys stay adjustable through the composition-row `config:`, engine and tools unaffected (issue #173); `settingsEnabled: false` disables the integration entirely (composition-row-only — the switch is deliberately NOT part of the settings layer: it cannot turn itself off). Design details: [docs/settings-integration-design.md](docs/settings-integration-design.md).
|
|
171
171
|
|
|
172
172
|
**Per-mode — an agent preset's `compaction` realm.** First *disable (or delete) the realm's existing `dsh-compaction-basic` row*, then mount this engine — two backends cannot coexist in the same realm:
|
|
173
173
|
|
|
@@ -251,6 +251,7 @@ This project reuses `acp-kernel`'s compression core and `billion-context-pi`'s d
|
|
|
251
251
|
| `autoTools` | `true` | Register the four model tools on `ctx.tools` |
|
|
252
252
|
| `autoCommand` | `true` | Register the `/acp-prune` command on `ctx.commands` |
|
|
253
253
|
| `autoNudge` | `true` | Inject the nudge into `agent/pre-step` (runtime-adjustable: `/acp-prune config`) |
|
|
254
|
+
| `maxOverflowRetries` | `1` | Budget for automatic recovery from a provider-confirmed context overflow (`CONTEXT_WINDOW_EXCEEDED`): the engine hides the largest eligible range with a marker summary it writes itself (no model call — the request was just rejected, so there is no model turn to write one; every original stays in the session log and `search_context`/`decompress` find it again), then answers the host's `agent/request-error` so the request is retried. The budget counts per request and resets once the model makes progress (an assistant message lands) or the agent goes idle; `0` disables the automatic action (the error surfaces unchanged). This is ACP's ONLY automatic compaction action — the pressure side stays nudge-only, the model decides (composition-only, not in the settings layer; rationale and trade-offs in [docs/overflow-recovery-design.md](docs/overflow-recovery-design.md)) |
|
|
254
255
|
| `settingsEnabled` | `true` (enabled when unset) | (optional) Disable the runtime-settings integration entirely (composition-row-only, deliberately NOT in the settings layer — the switch cannot turn itself off; with it off the composition-row `config:` stays the only effective channel) |
|
|
255
256
|
| `prompts` | — | (optional) Custom prompt copy: per-slot overrides for nudge / range table / system prompt / tool descriptions (template + named placeholders, validated at construction; see “Custom prompt copy” above and [docs/configurable-prompts-design.md](docs/configurable-prompts-design.md)) |
|
|
256
257
|
|
|
@@ -267,6 +268,7 @@ If you do not want to tune three percentages by hand, pick the nudge aggressiven
|
|
|
267
268
|
| `aggressive` | 0.30 | 0.50 | 0.70 | Lean context — compresses early and frequently |
|
|
268
269
|
|
|
269
270
|
- **Fills only what you left unset**: `preset` fills ONLY the `nudge*ContextLimitPct` values you did not set explicitly; if you set both a `preset` and one of those thresholds, your explicit value wins (precedence: explicit > preset > default).
|
|
271
|
+
- **Presets reach the runtime settings layer too**: the threshold values a composed preset fills are registered as the settings layer's `base` — `/acp-prune config list` attributes them to `source: base`, and `/acp-prune config reset <key>` returns them to the **preset value**, not the engine default (a runtime reset restores what the composition chose). Without a preset, nothing changes.
|
|
270
272
|
- **No other knob is touched**: `modelContextLimit`, `autoNudge`, `prompts`, and `coreOverrides` are unaffected; `coreOverrides.nudge` still lands last and its same-name keys outrank everything.
|
|
271
273
|
- **See the active tier**: `/acp-prune status` prints the effective `preset` and the three thresholds **actually in force** — the line mirrors `kernelConfigFor`'s merge order, so any explicit override you made on top of the preset AND any same-name key in `coreOverrides.nudge` are shown as they really apply.
|
|
272
274
|
- **A typo fails loudly**: an unknown name throws at engine construction and lists the valid tiers (the same fail-fast contract as custom prompt templates) — it never silently falls back to the defaults. The bundle row itself carries no `config`, so a `preset` can only come from your own same-id `compaction-acp` row; if that row fails to construct, the profile stays down until you fix it (intended fail-fast, not a defect).
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[中文](./README.md) | [English](./README.en.md)
|
|
4
4
|
|
|
5
5
|
> **⚠️ 测试版声明——请勿用于生产环境**
|
|
6
|
-
> 本项目(**v0.2.
|
|
6
|
+
> 本项目(**v0.2.26**)仍处于开发中的测试版。[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 本身也处于**公开测试版**阶段。**请勿将两者用于工程化 / 生产环境**——预期会有破坏性变更与粗糙之处。
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
9
|
<strong>衷心感谢以下项目——请给它们一个 ⭐:</strong>
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
与 DSH 内置的自动压缩(用自动生成的摘要替换一段范围)不同,billion-context-dsh:
|
|
43
43
|
|
|
44
44
|
- **模型驱动** —— 摘要由模型自己书写,没有第二次 LLM 摘要调用
|
|
45
|
-
- **只建议、不强令** —— 自动策略只 *nudge
|
|
45
|
+
- **只建议、不强令** —— 自动策略只 *nudge*(提醒),是否压缩、何时压缩由模型决定(唯一的例外:provider 确认的上下文超窗会让引擎自己抢救一次——见配置表的 `maxOverflowRetries`)
|
|
46
46
|
- **持久且可恢复** —— 压缩范围成为 checkpoint 节点,原文保留在 append-only 会话日志中;`decompress` 可恢复,`search_context` 可在块内查找
|
|
47
47
|
- **长任务稳得住** —— 每一步都接着前面的成果走,关键结论持续可用、不断叠加,超长任务更容易跑完
|
|
48
48
|
- **上下文始终精简** —— 每次请求都只用少量、精炼的上下文,只保留关键信息;不做大段统一压缩,细节不随之衰失,token 消耗自然更低
|
|
@@ -99,7 +99,7 @@ npm install billion-context-dsh
|
|
|
99
99
|
**git 源安装(`github:` 规格,插件商店展示的形态)。** 预构建产物 `dist/` 已提交到仓库,从 git 源安装同样开箱即用——**无需任何构建步骤**,pnpm 11 默认拦截构建脚本(`allowBuilds`)的机制对这个包不构成障碍:
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
|
-
dsh plugin --profile web add github:Tyan66666/billion-context-dsh#v0.2.
|
|
102
|
+
dsh plugin --profile web add github:Tyan66666/billion-context-dsh#v0.2.26
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
建议带 `#<tag>` 安装,拿到与对应 npm 版本完全一致的产物;不带 ref 则装默认分支的最新构建。只有 clone 仓库自行从源码构建(`npm run build`)才需要放行构建。背景与方案取舍见 [docs/git-source-install-design.md](docs/git-source-install-design.md)(issue #92)。
|
|
@@ -164,7 +164,7 @@ compaction-acp:
|
|
|
164
164
|
/acp-prune config reset all
|
|
165
165
|
```
|
|
166
166
|
|
|
167
|
-
窗口相关键(`modelContextLimit` / `autoModelContextLimit`)改动会清空窗口探测缓存——下一次 pre-step 按新值重新探测(探测失败也会被缓存,正是靠这个机制在修复网关后重新探测)。无 settings provider 的纯 npm 安装组合下 `/acp-prune config`
|
|
167
|
+
窗口相关键(`modelContextLimit` / `autoModelContextLimit`)改动会清空窗口探测缓存——下一次 pre-step 按新值重新探测(探测失败也会被缓存,正是靠这个机制在修复网关后重新探测)。无 settings provider 的纯 npm 安装组合下 `/acp-prune config` 降级为指引文案;DSH 0.1.7+ 宿主线(settings 服务已移除 `installSection`,在本插件声明的 peer 区间之外)同样干净降级:启动时记一条 warn,六个键改由组合行 `config:` 调整,引擎与工具不受影响(issue #173);`settingsEnabled: false` 可整体关闭该集成(组合行专用,不进 settings 层——开关不能关掉自己)。设计细节见 [docs/settings-integration-design.md](docs/settings-integration-design.md)。
|
|
168
168
|
|
|
169
169
|
**单模式生效(agent preset 的 `compaction` realm)**。先在该 realm 内*禁用(或删除)原有的 `dsh-compaction-basic` 行*,再插入本引擎——同一 realm 内两个后端不能并存:
|
|
170
170
|
|
|
@@ -248,6 +248,7 @@ DSH 的每个模型请求都派生自其 append-only 会话日志(*surface*)
|
|
|
248
248
|
| `autoTools` | `true` | 在 `ctx.tools` 注册四个模型工具 |
|
|
249
249
|
| `autoCommand` | `true` | 在 `ctx.commands` 注册 `/acp-prune` 命令 |
|
|
250
250
|
| `autoNudge` | `true` | 当内核建议时向 `agent/pre-step` 注入 nudge(运行时热调:`/acp-prune config`) |
|
|
251
|
+
| `maxOverflowRetries` | `1` | 宿主确认的上下文超窗(`CONTEXT_WINDOW_EXCEEDED`)自动抢救预算:超窗时引擎挑一段**最大且合规**的范围做一次紧急压缩(摘要是引擎自己写的标记,不调模型——请求刚被拒,本就没有模型回合可写摘要;原文都留在日志里,`search_context`/`decompress` 找得回来),然后应答宿主的 `agent/request-error` 让该请求重试。预算按请求计数,模型有进展(落一条 assistant 消息)或 agent 回到 idle 即重置;`0` 关闭这个自动动作(错误原样抛给用户)。这是 ACP 唯一的自动压缩动作——压力侧始终只 nudge、由模型决定(组合行专用,不进 settings 层;原理与取舍见 [docs/overflow-recovery-design.md](docs/overflow-recovery-design.md)) |
|
|
251
252
|
| `settingsEnabled` | `true`(未配置即启用) | (可选)整体关闭运行时设置集成(组合行专用,不进 settings 层——开关不能关掉自己;关闭后组合行 `config:` 仍是唯一生效通道) |
|
|
252
253
|
| `prompts` | — | (可选)自定义提示词文案:nudge / 范围表 / system prompt / 工具描述按槽位覆盖(模板 + 命名占位符,构造期校验;见上文「自定义提示词文案」与 [docs/configurable-prompts-design.md](docs/configurable-prompts-design.md)) |
|
|
253
254
|
|
|
@@ -264,6 +265,7 @@ DSH 的每个模型请求都派生自其 append-only 会话日志(*surface*)
|
|
|
264
265
|
| `aggressive` | 0.30 | 0.50 | 0.70 | 精简上下文,更早更频繁地压缩 |
|
|
265
266
|
|
|
266
267
|
- **只填未设的阈值**:`preset` 仅填充你没有显式设置的 `nudge*ContextLimitPct`;同时写了 `preset` 和某个阈值时,该阈值以你的显式值为准(优先级 显式 > preset > 默认)。
|
|
268
|
+
- **preset 同样落到运行时设置层**:组合 preset 填出的三个阈值注册为 settings 层的 `base` 层——`/acp-prune config list` 把它们归因为 `source: base`,`/acp-prune config reset <key>` 退回的是 **preset 值**而非引擎默认(运行时 reset 恢复的是组合的选择);未配 preset 时行为完全不变。
|
|
267
269
|
- **不碰其他旋钮**:`modelContextLimit`、`autoNudge`、`prompts`、`coreOverrides` 完全不受影响;`coreOverrides.nudge` 仍最后落地、同名键最高优先。
|
|
268
270
|
- **查看当前档位**:`/acp-prune status` 会打印生效的 `preset` 及其**真实生效的**三个阈值——这一行镜像 `kernelConfigFor` 的合并顺序,所以你在其上做的显式覆盖、以及 `coreOverrides.nudge` 里的同名键都会如实显示。
|
|
269
271
|
- **拼错即报错**:未知名称在引擎构造期直接抛错并列出合法值(与自定义提示词模板同一约定),不会静默回退默认。注意 bundle 行本身不带 `config`,`preset` 只能由你自己的同 id `compaction-acp` 行提供;该行构造失败即挂载失败,profile 会在你修好配置前一直起不来(fail-fast 的既定行为)。
|
package/dist/index.d.ts
CHANGED
|
@@ -115,6 +115,15 @@ export interface AcpConfig {
|
|
|
115
115
|
readonly autoCommand: boolean;
|
|
116
116
|
/** Inject the nudge into `agent/pre-step` when the kernel recommends it. Default true. */
|
|
117
117
|
readonly autoNudge: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* How many consecutive provider-confirmed context-overflow failures one
|
|
120
|
+
* agent may answer with an emergency compaction + retry before the original
|
|
121
|
+
* error is preserved (mirrors compaction-basic's `maxOverflowRetries`;
|
|
122
|
+
* default 1, the host's own default). The budget resets when the agent
|
|
123
|
+
* makes progress (an assistant message lands) or returns to idle, so a
|
|
124
|
+
* request that cannot be repaired cannot retry forever.
|
|
125
|
+
*/
|
|
126
|
+
readonly maxOverflowRetries?: number;
|
|
118
127
|
/**
|
|
119
128
|
* Escape hatch: disable the runtime-settings integration entirely
|
|
120
129
|
* (composition-layer ONLY — deliberately not exposed through the settings
|
|
@@ -163,6 +172,10 @@ export declare class AcpCompactionEngine extends CompactionEngine {
|
|
|
163
172
|
readonly settingsCommand: SettingsCommandSurface;
|
|
164
173
|
/** Per route the adapter's per-request output cap (the output reservation); null = undisclosed. */
|
|
165
174
|
private readonly outputReservationCache;
|
|
175
|
+
/** Per-agent context-overflow recovery budget (mirrors the host's `overflowRetries`). */
|
|
176
|
+
private readonly overflowRetries;
|
|
177
|
+
/** Per-session overflow agents, so session progress can reset the recovery budget. */
|
|
178
|
+
private readonly overflowSessions;
|
|
166
179
|
constructor(ctx: Context, config?: Partial<AcpConfig>);
|
|
167
180
|
/**
|
|
168
181
|
* Resolve the effective context window for an agent. An explicitly
|
|
@@ -208,6 +221,18 @@ export declare class AcpCompactionEngine extends CompactionEngine {
|
|
|
208
221
|
* (degenerate config) — the raw-window behavior is preserved.
|
|
209
222
|
*/
|
|
210
223
|
private applyReservation;
|
|
224
|
+
/**
|
|
225
|
+
* Best-effort emergency compaction for one provider-confirmed context
|
|
226
|
+
* overflow: pick the largest eligible (guarded, tool-pairing-balanced)
|
|
227
|
+
* surface range and land the normal durable transaction with a fixed
|
|
228
|
+
* engine-written marker summary. No LLM call — the provider just rejected
|
|
229
|
+
* the request for being too large, so there is no model turn available to
|
|
230
|
+
* write a summary; the originals stay in the append-only log, so
|
|
231
|
+
* search_context still indexes them, decompress restores them, and the
|
|
232
|
+
* model can re-run the compress tool over the marker later to write a real
|
|
233
|
+
* summary. Returns null when nothing eligible exists (nothing to reclaim).
|
|
234
|
+
*/
|
|
235
|
+
private compactForOverflow;
|
|
211
236
|
/** ACP is model-driven: automatic pressure policy never summarizes by itself. */
|
|
212
237
|
compactIfNeeded(_agent: CompactionAgentContext, _trigger: CompactionTrigger, signal: AbortSignal): Promise<CompactionResult | null>;
|
|
213
238
|
/** Explicit idle-session compaction: ACP leaves the decision to the model. */
|
package/dist/index.js
CHANGED
|
@@ -3363,6 +3363,9 @@ function makePreview(text, query, len) {
|
|
|
3363
3363
|
return prefix + text.slice(start, end).trim() + suffix;
|
|
3364
3364
|
}
|
|
3365
3365
|
|
|
3366
|
+
// src/index.ts
|
|
3367
|
+
import { CONTEXT_WINDOW_EXCEEDED_CODE } from "@deepseek-ai/dsh-llm";
|
|
3368
|
+
|
|
3366
3369
|
// src/lru.ts
|
|
3367
3370
|
var DEFAULT_SESSION_CACHE_LIMIT = 512;
|
|
3368
3371
|
var LruMap = class extends Map {
|
|
@@ -3478,9 +3481,18 @@ function buildToolCallIndex(events) {
|
|
|
3478
3481
|
}
|
|
3479
3482
|
var SUMMARY_FRAME_PREFIX = "[Model-written summary \u2014 not user words; re-verify any obligations before relying on them]";
|
|
3480
3483
|
function withSummaryFramePrefix(text) {
|
|
3481
|
-
|
|
3484
|
+
if (text.startsWith(SUMMARY_FRAME_PREFIX)) return text;
|
|
3485
|
+
if (isEngineWrittenSummary(text)) return text;
|
|
3486
|
+
return `${SUMMARY_FRAME_PREFIX}
|
|
3482
3487
|
${text}`;
|
|
3483
3488
|
}
|
|
3489
|
+
var ENGINE_SUMMARY_LEAD = "[engine-written summary \u2014 context-overflow emergency compaction";
|
|
3490
|
+
function isEngineWrittenSummary(text) {
|
|
3491
|
+
return text.startsWith(ENGINE_SUMMARY_LEAD);
|
|
3492
|
+
}
|
|
3493
|
+
function overflowMarkerSummary(hiddenCount) {
|
|
3494
|
+
return `${ENGINE_SUMMARY_LEAD}: ${hiddenCount} surface message(s) hidden because the provider rejected the request as exceeding the context window. The originals are intact in the session log \u2014 use search_context or decompress (see acp_status) to read them, or re-run the compress tool over this range to write a proper summary.]`;
|
|
3495
|
+
}
|
|
3484
3496
|
function projectEvent(event, toolNames) {
|
|
3485
3497
|
switch (event.type) {
|
|
3486
3498
|
case "user/message": {
|
|
@@ -3606,7 +3618,12 @@ function contentBlocksOfEvent(event) {
|
|
|
3606
3618
|
function isCheckpointNode(event) {
|
|
3607
3619
|
if (event.type !== "user/message") return false;
|
|
3608
3620
|
const source = event.data.source;
|
|
3609
|
-
return source?.plugin === "compact";
|
|
3621
|
+
return source?.plugin === "compact" || source?.kind === "compact-checkpoint";
|
|
3622
|
+
}
|
|
3623
|
+
function checkpointCompactionIdOf(event) {
|
|
3624
|
+
if (!isCheckpointNode(event)) return null;
|
|
3625
|
+
const source = event.data.source;
|
|
3626
|
+
return typeof source?.compactionId === "string" ? source.compactionId : null;
|
|
3610
3627
|
}
|
|
3611
3628
|
var METADATA_PLUGINS = /* @__PURE__ */ new Set([
|
|
3612
3629
|
"acp-nudge",
|
|
@@ -3619,17 +3636,44 @@ var REAL_CONTENT_PLUGINS = /* @__PURE__ */ new Set([
|
|
|
3619
3636
|
"user-approval",
|
|
3620
3637
|
"tools-ptc"
|
|
3621
3638
|
]);
|
|
3639
|
+
var REAL_CONTENT_KINDS = /* @__PURE__ */ new Set([
|
|
3640
|
+
"runtime-context",
|
|
3641
|
+
// dynamic-context snapshot (was '@deepseek-ai/dsh-system-prompt')
|
|
3642
|
+
"ptc-mode"
|
|
3643
|
+
// deferred tool context (was 'tools-ptc' / 'tools-code-mode')
|
|
3644
|
+
]);
|
|
3645
|
+
var AUDITED_RELAY_KINDS = /* @__PURE__ */ new Set([
|
|
3646
|
+
"subagent-report",
|
|
3647
|
+
"subagent-settled"
|
|
3648
|
+
]);
|
|
3622
3649
|
var HOST_INSTRUCTION_KINDS = /* @__PURE__ */ new Set([
|
|
3623
3650
|
"agent-instructions",
|
|
3624
3651
|
// AGENTS.md injection (hook shape: {kind:'agent-instructions', form:'instructions'})
|
|
3625
|
-
"skill-catalog"
|
|
3652
|
+
"skill-catalog",
|
|
3626
3653
|
// skill catalog (form:'catalog')
|
|
3654
|
+
// Host compaction summary row (DSH >= 0.1.7; was plugin 'dsh-compaction-basic').
|
|
3655
|
+
// That legacy name was never whitelisted, so its rows were barriers already —
|
|
3656
|
+
// the renamed spelling keeps exactly that treatment instead of silently
|
|
3657
|
+
// becoming foldable content (issue #169).
|
|
3658
|
+
"compact-basic"
|
|
3627
3659
|
]);
|
|
3660
|
+
function sourcePluginOf(source) {
|
|
3661
|
+
if (source === void 0 || typeof source !== "object") return void 0;
|
|
3662
|
+
const kind = source.kind;
|
|
3663
|
+
if (kind === "plugin") {
|
|
3664
|
+
return typeof source.plugin === "string" && source.plugin.length > 0 ? source.plugin : void 0;
|
|
3665
|
+
}
|
|
3666
|
+
if (typeof kind === "string" && kind.startsWith("plugin:")) {
|
|
3667
|
+
const name = kind.slice("plugin:".length);
|
|
3668
|
+
return name.length > 0 ? name : void 0;
|
|
3669
|
+
}
|
|
3670
|
+
return void 0;
|
|
3671
|
+
}
|
|
3628
3672
|
function isAgentInstructionsRow(event) {
|
|
3629
3673
|
if (event.type !== "user/message") return false;
|
|
3630
3674
|
const source = event.data.source;
|
|
3631
3675
|
if (!source) return false;
|
|
3632
|
-
return source.kind === "agent-instructions" || source
|
|
3676
|
+
return source.kind === "agent-instructions" || sourcePluginOf(source) === "agent-instructions";
|
|
3633
3677
|
}
|
|
3634
3678
|
function classifySurfaceEvent(event) {
|
|
3635
3679
|
if (isCheckpointNode(event)) return "checkpoint";
|
|
@@ -3638,20 +3682,26 @@ function classifySurfaceEvent(event) {
|
|
|
3638
3682
|
if (!source) return "real";
|
|
3639
3683
|
const kind = source.kind;
|
|
3640
3684
|
if (kind === "user") return "real";
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
if (
|
|
3685
|
+
const plugin = sourcePluginOf(source);
|
|
3686
|
+
if (kind === "plugin" || plugin !== void 0) {
|
|
3687
|
+
if (plugin !== void 0 && METADATA_PLUGINS.has(plugin)) return "metadata";
|
|
3688
|
+
if (plugin !== void 0 && REAL_CONTENT_PLUGINS.has(plugin)) return "real";
|
|
3644
3689
|
return "instruction";
|
|
3645
3690
|
}
|
|
3646
|
-
if (kind !==
|
|
3647
|
-
return "
|
|
3691
|
+
if (typeof kind !== "string") return "real";
|
|
3692
|
+
if (HOST_INSTRUCTION_KINDS.has(kind)) return "instruction";
|
|
3693
|
+
if (REAL_CONTENT_KINDS.has(kind) || AUDITED_RELAY_KINDS.has(kind)) return "real";
|
|
3694
|
+
return "instruction";
|
|
3648
3695
|
}
|
|
3649
3696
|
function isRealUserTurn(event) {
|
|
3650
3697
|
if (event.type !== "user/message") return false;
|
|
3651
3698
|
if (classifySurfaceEvent(event) !== "real") return false;
|
|
3652
3699
|
const source = event.data.source;
|
|
3653
|
-
|
|
3654
|
-
|
|
3700
|
+
const plugin = sourcePluginOf(source);
|
|
3701
|
+
if (plugin !== void 0 && REAL_CONTENT_PLUGINS.has(plugin)) return false;
|
|
3702
|
+
const kind = source?.kind;
|
|
3703
|
+
if (typeof kind === "string" && REAL_CONTENT_KINDS.has(kind)) return false;
|
|
3704
|
+
return kind !== "subagent-report" && kind !== "subagent-settled";
|
|
3655
3705
|
}
|
|
3656
3706
|
|
|
3657
3707
|
// src/host-tokens.ts
|
|
@@ -4036,9 +4086,8 @@ function summarySeqIndex(events) {
|
|
|
4036
4086
|
const index = /* @__PURE__ */ new Map();
|
|
4037
4087
|
for (const event of events) {
|
|
4038
4088
|
if (event.type !== "user/message") continue;
|
|
4039
|
-
const
|
|
4040
|
-
|
|
4041
|
-
if (compactionId !== void 0 && !index.has(compactionId)) index.set(compactionId, event.seq);
|
|
4089
|
+
const compactionId = checkpointCompactionIdOf(event);
|
|
4090
|
+
if (compactionId !== null && !index.has(compactionId)) index.set(compactionId, event.seq);
|
|
4042
4091
|
}
|
|
4043
4092
|
return index;
|
|
4044
4093
|
}
|
|
@@ -4128,7 +4177,10 @@ function hideSurfaceSeqs(session, seqs, text, priceEvent = hostPriceEvent) {
|
|
|
4128
4177
|
const body = text !== void 0 && text.trim().length > 0 ? text : PRUNE_NOTE;
|
|
4129
4178
|
session.append("user/message", createUserMessage({
|
|
4130
4179
|
content: [{ type: "text", text: body }],
|
|
4131
|
-
|
|
4180
|
+
// V4 producer kind (issue #163): DSH ≥0.1.7's V4 admission rejects the
|
|
4181
|
+
// legacy wrapper `{ kind: 'plugin', plugin: … }`; `plugin:<name>` is what
|
|
4182
|
+
// the host's own V3→V4 migration emits and is accepted by 0.1.5 too.
|
|
4183
|
+
source: { kind: "plugin:billion-context-dsh" }
|
|
4132
4184
|
}), {
|
|
4133
4185
|
surfaceOp: { op: "replace", startSeq: start, endSeq: end },
|
|
4134
4186
|
sourceEventSeqs: [...seqs]
|
|
@@ -4436,10 +4488,10 @@ function blockRegistry(session) {
|
|
|
4436
4488
|
}
|
|
4437
4489
|
function blockRefForSummarySeq(session, seq) {
|
|
4438
4490
|
const event = eventAtOf(session, seq);
|
|
4439
|
-
if (event
|
|
4440
|
-
const
|
|
4441
|
-
if (
|
|
4442
|
-
const entry = blockRegistry(session).find((r) => r.blockId ===
|
|
4491
|
+
if (event === void 0) return null;
|
|
4492
|
+
const compactionId = checkpointCompactionIdOf(event);
|
|
4493
|
+
if (compactionId === null) return null;
|
|
4494
|
+
const entry = blockRegistry(session).find((r) => r.blockId === compactionId);
|
|
4443
4495
|
if (entry === void 0) return null;
|
|
4444
4496
|
return entry.kernelBlockId;
|
|
4445
4497
|
}
|
|
@@ -4459,10 +4511,8 @@ function summarySeqOfKernelBlock(session, kernelBlockId) {
|
|
|
4459
4511
|
}
|
|
4460
4512
|
function checkpointBlockIdOf(events, seq) {
|
|
4461
4513
|
const event = events[seq];
|
|
4462
|
-
if (event
|
|
4463
|
-
|
|
4464
|
-
if (source?.plugin !== "compact" || source.compactionId === void 0) return null;
|
|
4465
|
-
return source.compactionId;
|
|
4514
|
+
if (event === void 0) return null;
|
|
4515
|
+
return checkpointCompactionIdOf(event);
|
|
4466
4516
|
}
|
|
4467
4517
|
function expandShadowedSeqs(session, blockId) {
|
|
4468
4518
|
const ledger = rebuildBlockLedger(sessionEventsOf(session));
|
|
@@ -4902,7 +4952,13 @@ function buildNudge(agent, env, lastNudgeTurn, emergencyNudges, onEmergencyCapHi
|
|
|
4902
4952
|
);
|
|
4903
4953
|
const message = createUserMessage2({
|
|
4904
4954
|
content: [{ type: "text", text }],
|
|
4905
|
-
|
|
4955
|
+
// V4 producer kind (issue #163): DSH ≥0.1.7's V4 admission rejects the
|
|
4956
|
+
// legacy wrapper shape `{ kind: 'plugin', plugin: … }` outright (a wedged
|
|
4957
|
+
// batch fails the NEXT turn with "format v4 message requires a
|
|
4958
|
+
// producer-owned source kind"). `plugin:<name>` is exactly what the host's
|
|
4959
|
+
// own V3→V4 migration emits for unregistered plugins, and DSH 0.1.5
|
|
4960
|
+
// sessions accept it too (probe-verified), so no version gate is needed.
|
|
4961
|
+
source: { kind: "plugin:acp-nudge" }
|
|
4906
4962
|
});
|
|
4907
4963
|
return { message, emergency };
|
|
4908
4964
|
}
|
|
@@ -6107,6 +6163,9 @@ var DEFAULT_CONFIG = {
|
|
|
6107
6163
|
autoTools: true,
|
|
6108
6164
|
autoCommand: true,
|
|
6109
6165
|
autoNudge: true,
|
|
6166
|
+
// Same default as the host's compaction-basic policy: one owned retry per
|
|
6167
|
+
// unrelieved overflow, then the original error is preserved.
|
|
6168
|
+
maxOverflowRetries: 1,
|
|
6110
6169
|
// Nudge thresholds: engine defaults 0.70/0.85 — deliberately below the
|
|
6111
6170
|
// kernel/billion-context-pi 0.75/0.95. 0.95 leaves no room to act before
|
|
6112
6171
|
// the API rejects, and the host's compaction-basic line (thresholdRatio
|
|
@@ -6120,7 +6179,11 @@ var DEFAULT_CONFIG = {
|
|
|
6120
6179
|
function resolveAcpConfig(config = {}) {
|
|
6121
6180
|
const resolved = resolvePresetThresholds({ ...DEFAULT_CONFIG, ...config }, config);
|
|
6122
6181
|
assertNudgeThresholdOrder(resolved);
|
|
6123
|
-
|
|
6182
|
+
const maxOverflowRetries = resolved.maxOverflowRetries ?? 1;
|
|
6183
|
+
if (!Number.isInteger(maxOverflowRetries) || maxOverflowRetries < 0) {
|
|
6184
|
+
throw new Error(`maxOverflowRetries must be a non-negative integer (got ${maxOverflowRetries})`);
|
|
6185
|
+
}
|
|
6186
|
+
return { ...resolved, maxOverflowRetries };
|
|
6124
6187
|
}
|
|
6125
6188
|
function resolvePresetThresholds(base, config) {
|
|
6126
6189
|
if (base.preset === void 0) return base;
|
|
@@ -6132,6 +6195,17 @@ function resolvePresetThresholds(base, config) {
|
|
|
6132
6195
|
nudgeEmergencyThresholdPct: config.nudgeEmergencyThresholdPct ?? preset.nudgeEmergencyThresholdPct
|
|
6133
6196
|
};
|
|
6134
6197
|
}
|
|
6198
|
+
function presetFilledSettingsEntry(config) {
|
|
6199
|
+
const entry = filterSettingsEntry(config);
|
|
6200
|
+
if (config.preset === void 0) return entry;
|
|
6201
|
+
const preset = resolvePreset(config.preset);
|
|
6202
|
+
return {
|
|
6203
|
+
...entry,
|
|
6204
|
+
nudgeMinContextLimitPct: config.nudgeMinContextLimitPct ?? preset.nudgeMinContextLimitPct,
|
|
6205
|
+
nudgeMaxContextLimitPct: config.nudgeMaxContextLimitPct ?? preset.nudgeMaxContextLimitPct,
|
|
6206
|
+
nudgeEmergencyThresholdPct: config.nudgeEmergencyThresholdPct ?? preset.nudgeEmergencyThresholdPct
|
|
6207
|
+
};
|
|
6208
|
+
}
|
|
6135
6209
|
function assertNudgeThresholdOrder(config) {
|
|
6136
6210
|
const { nudgeMinContextLimitPct: min, nudgeMaxContextLimitPct: max, nudgeEmergencyThresholdPct: emergency } = config;
|
|
6137
6211
|
const describe = `min ${min ?? "kernel default"} / max ${max ?? "kernel default"} / emergency ${emergency ?? "kernel default"}`;
|
|
@@ -6177,6 +6251,10 @@ var AcpCompactionEngine = class extends CompactionEngine {
|
|
|
6177
6251
|
settingsCommand;
|
|
6178
6252
|
/** Per route the adapter's per-request output cap (the output reservation); null = undisclosed. */
|
|
6179
6253
|
outputReservationCache = /* @__PURE__ */ new Map();
|
|
6254
|
+
/** Per-agent context-overflow recovery budget (mirrors the host's `overflowRetries`). */
|
|
6255
|
+
overflowRetries = /* @__PURE__ */ new Map();
|
|
6256
|
+
/** Per-session overflow agents, so session progress can reset the recovery budget. */
|
|
6257
|
+
overflowSessions = /* @__PURE__ */ new Map();
|
|
6180
6258
|
constructor(ctx, config = {}) {
|
|
6181
6259
|
super(ctx);
|
|
6182
6260
|
this.config = resolveAcpConfig(config);
|
|
@@ -6185,7 +6263,7 @@ var AcpCompactionEngine = class extends CompactionEngine {
|
|
|
6185
6263
|
this.kernel = createCore(ports);
|
|
6186
6264
|
setDocCacheCap(128 * 1024 * 1024);
|
|
6187
6265
|
this.store = new AcpStateStore();
|
|
6188
|
-
const compositionEntry =
|
|
6266
|
+
const compositionEntry = presetFilledSettingsEntry(config);
|
|
6189
6267
|
let current = resolveAcpSettings(compositionEntry);
|
|
6190
6268
|
this.readSettingsSource = () => current;
|
|
6191
6269
|
const engine = this;
|
|
@@ -6202,6 +6280,12 @@ var AcpCompactionEngine = class extends CompactionEngine {
|
|
|
6202
6280
|
this.settingsCommand = makeSettingsCommandSurface(() => this.settingsService, () => current);
|
|
6203
6281
|
if (this.config.settingsEnabled !== false) {
|
|
6204
6282
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
6283
|
+
if (typeof settingsCtx.settings?.installSection !== "function") {
|
|
6284
|
+
this.ctx.logger.warn(
|
|
6285
|
+
"billion-context-dsh: host settings service has no installSection (removed in dsh-settings >= 0.1.7) \u2014 the compaction-acp settings section is not registered; the six knobs keep their composition values and /acp-prune config reports the section unavailable"
|
|
6286
|
+
);
|
|
6287
|
+
return void 0;
|
|
6288
|
+
}
|
|
6205
6289
|
settingsCtx.settings.installSection(ctx, ACP_SETTINGS_NAMESPACE, AcpSettingsSchema, compositionEntry, {
|
|
6206
6290
|
// The seam's source type follows the entry it registered, so `source`
|
|
6207
6291
|
// is a partial view of the settings; re-resolve it into a
|
|
@@ -6282,6 +6366,13 @@ var AcpCompactionEngine = class extends CompactionEngine {
|
|
|
6282
6366
|
});
|
|
6283
6367
|
}
|
|
6284
6368
|
ctx.on("session/event", (session, event) => {
|
|
6369
|
+
if (event.type === "assistant/message") {
|
|
6370
|
+
const overflowAgent = this.overflowSessions.get(session);
|
|
6371
|
+
if (overflowAgent !== void 0) {
|
|
6372
|
+
this.overflowRetries.delete(overflowAgent);
|
|
6373
|
+
this.overflowSessions.delete(session);
|
|
6374
|
+
}
|
|
6375
|
+
}
|
|
6285
6376
|
if (event.type !== "tool/result") return;
|
|
6286
6377
|
const message = event.data.message;
|
|
6287
6378
|
const block = message.content[0];
|
|
@@ -6312,6 +6403,49 @@ var AcpCompactionEngine = class extends CompactionEngine {
|
|
|
6312
6403
|
if (outcome === null) return decision;
|
|
6313
6404
|
return { kind: "enter", messages: [...decision.messages, outcome.message] };
|
|
6314
6405
|
});
|
|
6406
|
+
ctx.on("agent/status", ({ agent, status }) => {
|
|
6407
|
+
if (status !== "idle") return;
|
|
6408
|
+
this.overflowRetries.delete(agent);
|
|
6409
|
+
this.overflowSessions.delete(agent.session);
|
|
6410
|
+
});
|
|
6411
|
+
ctx.on("agent/request-error", async ({ agent, failure, signal }, next) => {
|
|
6412
|
+
if (failure.code !== CONTEXT_WINDOW_EXCEEDED_CODE || signal.aborted) return next();
|
|
6413
|
+
this.overflowSessions.set(agent.session, agent);
|
|
6414
|
+
const retries = this.overflowRetries.get(agent) ?? 0;
|
|
6415
|
+
const max = this.config.maxOverflowRetries ?? 1;
|
|
6416
|
+
if (retries >= max) {
|
|
6417
|
+
this.ctx.logger.warn(
|
|
6418
|
+
`billion-context-dsh: context-overflow recovery budget spent (${max} retries) for session ${agent.session.id}; preserving the original request error`
|
|
6419
|
+
);
|
|
6420
|
+
return next();
|
|
6421
|
+
}
|
|
6422
|
+
const generation = agent.session.surface.replaceGeneration;
|
|
6423
|
+
let result;
|
|
6424
|
+
try {
|
|
6425
|
+
result = await this.compactForOverflow(agent, signal);
|
|
6426
|
+
} catch (error) {
|
|
6427
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6428
|
+
if (!signal.aborted && agent.session.surface.replaceGeneration > generation) {
|
|
6429
|
+
this.ctx.logger.warn(
|
|
6430
|
+
`billion-context-dsh: context-overflow compaction failed after durable surface progress: ${message}; retrying from the replacement surface`
|
|
6431
|
+
);
|
|
6432
|
+
this.overflowRetries.set(agent, retries + 1);
|
|
6433
|
+
return { kind: "retry" };
|
|
6434
|
+
}
|
|
6435
|
+
this.ctx.logger.warn(
|
|
6436
|
+
`billion-context-dsh: context-overflow compaction failed: ${message}; ${signal.aborted ? "cancellation prevents retry" : "preserving the original request error"}`
|
|
6437
|
+
);
|
|
6438
|
+
return next();
|
|
6439
|
+
}
|
|
6440
|
+
if (signal.aborted || agent.session.surface.replaceGeneration <= generation) return next();
|
|
6441
|
+
if (result !== null) {
|
|
6442
|
+
this.ctx.logger.info(
|
|
6443
|
+
`compaction (context overflow recovery): shadowed ${result.shadowedSeqs.length} surface nodes (seqs ${result.start}-${result.end}, ~${result.shadowedTokenCount} tokens)`
|
|
6444
|
+
);
|
|
6445
|
+
}
|
|
6446
|
+
this.overflowRetries.set(agent, retries + 1);
|
|
6447
|
+
return { kind: "retry" };
|
|
6448
|
+
});
|
|
6315
6449
|
const systemPrompt = ctx.get("systemPrompt");
|
|
6316
6450
|
if (systemPrompt !== void 0) {
|
|
6317
6451
|
systemPrompt.section({
|
|
@@ -6435,6 +6569,81 @@ var AcpCompactionEngine = class extends CompactionEngine {
|
|
|
6435
6569
|
if (cap === null || cap >= window.limit) return window;
|
|
6436
6570
|
return { ...window, rawLimit: window.limit, outputReserved: cap, limit: window.limit - cap };
|
|
6437
6571
|
}
|
|
6572
|
+
/**
|
|
6573
|
+
* Best-effort emergency compaction for one provider-confirmed context
|
|
6574
|
+
* overflow: pick the largest eligible (guarded, tool-pairing-balanced)
|
|
6575
|
+
* surface range and land the normal durable transaction with a fixed
|
|
6576
|
+
* engine-written marker summary. No LLM call — the provider just rejected
|
|
6577
|
+
* the request for being too large, so there is no model turn available to
|
|
6578
|
+
* write a summary; the originals stay in the append-only log, so
|
|
6579
|
+
* search_context still indexes them, decompress restores them, and the
|
|
6580
|
+
* model can re-run the compress tool over the marker later to write a real
|
|
6581
|
+
* summary. Returns null when nothing eligible exists (nothing to reclaim).
|
|
6582
|
+
*/
|
|
6583
|
+
async compactForOverflow(agent, signal) {
|
|
6584
|
+
signal.throwIfAborted();
|
|
6585
|
+
const session = agent.session;
|
|
6586
|
+
const state = this.store.stateFor(session);
|
|
6587
|
+
const coreMessages = allLogMessages(session);
|
|
6588
|
+
const surfaceMessages = eventsToCoreMessages(surfaceEventsOf(session));
|
|
6589
|
+
const tokenCount = resolveTokenCount(agent, surfaceMessages);
|
|
6590
|
+
const window = await this.windowFor(agent);
|
|
6591
|
+
const config = kernelConfigFor({ ...this.env, modelContextLimit: window.limit });
|
|
6592
|
+
const turn = this.kernel.processTurn({ messages: coreMessages, state, config, tokenCount, renderTags: "none" });
|
|
6593
|
+
this.store.set(session, turn.state);
|
|
6594
|
+
const byRaw = turn.state.messageRefs.byRaw;
|
|
6595
|
+
let firstSeq;
|
|
6596
|
+
let lastSeq;
|
|
6597
|
+
for (const seq of session.surface.nodes) {
|
|
6598
|
+
if (byRaw[String(seq)] === void 0) continue;
|
|
6599
|
+
firstSeq ??= seq;
|
|
6600
|
+
lastSeq = seq;
|
|
6601
|
+
}
|
|
6602
|
+
if (firstSeq === void 0 || lastSeq === void 0) return null;
|
|
6603
|
+
const view = {
|
|
6604
|
+
ranges: [{ startRef: byRaw[String(firstSeq)], endRef: byRaw[String(lastSeq)] }],
|
|
6605
|
+
refs: { byRef: turn.state.messageRefs.byRef }
|
|
6606
|
+
};
|
|
6607
|
+
const ranges = buildCompressibleSeqRanges(session, view, {
|
|
6608
|
+
preserveRecent: 5,
|
|
6609
|
+
mediaPriceOf: meterMediaPriceResolver(agent, session)
|
|
6610
|
+
});
|
|
6611
|
+
if (ranges.length === 0) return null;
|
|
6612
|
+
const best = ranges.reduce((largest, range) => range.tokens > largest.tokens ? range : largest);
|
|
6613
|
+
const startRef = byRaw[String(best.start)];
|
|
6614
|
+
const endRef = byRaw[String(best.end)];
|
|
6615
|
+
if (startRef === void 0 || endRef === void 0) return null;
|
|
6616
|
+
const summary = overflowMarkerSummary(best.count);
|
|
6617
|
+
const applied = this.kernel.applyCompression({
|
|
6618
|
+
ranges: [{ startRef, endRef, summary, topic: "context-overflow recovery" }],
|
|
6619
|
+
messages: coreMessages,
|
|
6620
|
+
state: turn.state,
|
|
6621
|
+
config
|
|
6622
|
+
});
|
|
6623
|
+
if (applied.result.blocksCreated === 0) return null;
|
|
6624
|
+
this.store.set(session, applied.state);
|
|
6625
|
+
const previousIds = new Set(turn.state.blocks.map((block) => block.blockId));
|
|
6626
|
+
const created = applied.state.blocks.find((block) => !previousIds.has(block.blockId));
|
|
6627
|
+
if (created === void 0) return null;
|
|
6628
|
+
const shadowed = shadowedSeqsOf(session, best.start, best.end);
|
|
6629
|
+
if (shadowed.length === 0) return null;
|
|
6630
|
+
const shadowedTokenCount = shadowedTokensViaMeter(session, shadowed, agent.ctx);
|
|
6631
|
+
const { provider, model } = routeFor(agent);
|
|
6632
|
+
runCompactionTransaction(session, {
|
|
6633
|
+
start: best.start,
|
|
6634
|
+
end: best.end,
|
|
6635
|
+
shadowedSeqs: shadowed,
|
|
6636
|
+
summary: [{ type: "text", text: summary }],
|
|
6637
|
+
shadowedTokenCount,
|
|
6638
|
+
provider,
|
|
6639
|
+
model,
|
|
6640
|
+
topic: "context-overflow recovery",
|
|
6641
|
+
kernelBlockId: created.blockId,
|
|
6642
|
+
directMessageIds: created.directMessageIds,
|
|
6643
|
+
effectiveMessageIds: created.effectiveMessageIds
|
|
6644
|
+
});
|
|
6645
|
+
return { start: best.start, end: best.end, shadowedSeqs: shadowed, shadowedTokenCount };
|
|
6646
|
+
}
|
|
6438
6647
|
/** ACP is model-driven: automatic pressure policy never summarizes by itself. */
|
|
6439
6648
|
async compactIfNeeded(_agent, _trigger, signal) {
|
|
6440
6649
|
signal.throwIfAborted();
|