billion-context-dsh 0.2.12 → 0.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [English](./README.en.md) | [中文](./README.md)
4
4
 
5
5
  > **⚠️ Beta notice — not for production use**
6
- > This project (**v0.2.12**) is a work-in-progress beta. The [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) itself is also in **public beta**. **Do not use either in engineering / production environments** — expect breaking changes and rough edges.
6
+ > This project (**v0.2.14**) 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 ⭐:
@@ -202,9 +202,9 @@ This project reuses `acp-kernel`'s compression core and `billion-context-pi`'s d
202
202
  | `modelContextLimit` | auto-detected (fallback `128000`) | Context window used for the kernel's pressure decisions; an explicit value wins and skips the probe |
203
203
  | `autoModelContextLimit` | `true` | Probe the model's real window from the model API (`agent.ctx.llm.resolveModelInfo`); fall back to the default on failure, the `/acp` command shows the window source (the `acp_status` model tool carries no window info). A failed probe is surfaced in the host log and the `/acp` panel (`restart to re-probe`) — the failure is cached like a success, so fixing the gateway requires a restart or an explicit `modelContextLimit` before the probe retries |
204
204
  | `nudgeMinContextLimitPct` | kernel default `0.45` | Nudge window lower bound (usage fraction) — validation only; the growth-driven trigger has no percentage floor — same default as billion-context-pi |
205
- | `nudgeMaxContextLimitPct` | engine default `0.70` (kernel/pi default `0.75`) | Over-limit line: above this the nudge fires regardless of growth — deliberately below the host compaction-basic 80% auto-compaction line so the forced nudge fires first; an explicit value wins |
206
- | `nudgeEmergencyThresholdPct` | engine default `0.85` (kernel/pi default `0.95`) | Emergency nudge (bypasses the per-turn dedup) — lowered from `0.95`: at 95% the model has no room to act and the 80% auto-compaction line shadows it; an explicit value wins |
207
- | `coreOverrides` | — | Any other acp-kernel `Config` override (billion-context-pi's `coreOverrides` escape hatch) |
205
+ | `nudgeMaxContextLimitPct` | engine default `0.70` (kernel/pi default `0.75`) | Over-limit line: above this the nudge fires regardless of growth — deliberately below the host compaction-basic 80% auto-compaction line so the forced nudge fires first; an explicit value wins (a same-name key in `coreOverrides.nudge` outranks it — see below) |
206
+ | `nudgeEmergencyThresholdPct` | engine default `0.85` (kernel/pi default `0.95`) | Emergency nudge (bypasses the per-turn dedup) — lowered from `0.95`: at 95% the model has no room to act and the 80% auto-compaction line shadows it; an explicit value wins (a same-name key in `coreOverrides.nudge` outranks it — see below) |
207
+ | `coreOverrides` | — | Any other acp-kernel `Config` override (billion-context-pi's `coreOverrides` escape hatch). Merge order: kernel defaults → top-level pct knobs → `coreOverrides.nudge` lands last — same-name keys take its value |
208
208
  | `autoTools` | `true` | Register the four model tools on `ctx.tools` |
209
209
  | `autoCommand` | `true` | Register the `/acp` command on `ctx.commands` |
210
210
  | `autoNudge` | `true` | Inject the nudge into `agent/pre-step` |
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [中文](./README.md) | [English](./README.en.md)
4
4
 
5
5
  > **⚠️ 测试版声明——请勿用于生产环境**
6
- > 本项目(**v0.2.12**)仍处于开发中的测试版。[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 本身也处于**公开测试版**阶段。**请勿将两者用于工程化 / 生产环境**——预期会有破坏性变更与粗糙之处。
6
+ > 本项目(**v0.2.14**)仍处于开发中的测试版。[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 本身也处于**公开测试版**阶段。**请勿将两者用于工程化 / 生产环境**——预期会有破坏性变更与粗糙之处。
7
7
 
8
8
  <p align="center">
9
9
  <strong>衷心感谢以下项目——请给它们一个 ⭐:</strong>
@@ -202,9 +202,9 @@ DSH 的每个模型请求都派生自其 append-only 会话日志(*surface*)
202
202
  | `modelContextLimit` | 自动探测(回退 `128000`) | 用于内核压力决策的上下文窗口;显式配置时优先且跳过探测 |
203
203
  | `autoModelContextLimit` | `true` | 从模型 API 自动探测真实窗口(`agent.ctx.llm.resolveModelInfo`);探测失败回退默认值,`/acp` 命令展示窗口来源(模型工具 `acp_status` 不含窗口信息)。探测失败会在宿主日志与 `/acp` 面板提示(`restart to re-probe`)——失败结果同样被缓存,修复网关后需重启或显式设置 `modelContextLimit` 才会重新探测 |
204
204
  | `nudgeMinContextLimitPct` | 内核默认 `0.45` | Nudge 窗口下界(用量占比)——仅作配置校验,增长路径的触发没有百分比下限——与 billion-context-pi 相同的默认值 |
205
- | `nudgeMaxContextLimitPct` | engine 默认 `0.70`(内核/pi 默认 `0.75`) | 过限线:超过此值则无论增长与否都触发 nudge——刻意低于宿主 compaction-basic 的 80% 自动压缩线,保证强制 nudge 先触发;显式配置优先 |
206
- | `nudgeEmergencyThresholdPct` | engine 默认 `0.85`(内核/pi 默认 `0.95`) | 紧急 nudge(绕过每轮去重)——从 `0.95` 下调:95% 时模型已无操作空间且会被 80% 自动压缩线遮蔽;显式配置优先 |
207
- | `coreOverrides` | — | 任何其他 acp-kernel `Config` 覆盖(billion-context-pi 的 `coreOverrides` 逃生口) |
205
+ | `nudgeMaxContextLimitPct` | engine 默认 `0.70`(内核/pi 默认 `0.75`) | 过限线:超过此值则无论增长与否都触发 nudge——刻意低于宿主 compaction-basic 的 80% 自动压缩线,保证强制 nudge 先触发;显式配置优先(`coreOverrides.nudge` 同名键优先级更高,见下) |
206
+ | `nudgeEmergencyThresholdPct` | engine 默认 `0.85`(内核/pi 默认 `0.95`) | 紧急 nudge(绕过每轮去重)——从 `0.95` 下调:95% 时模型已无操作空间且会被 80% 自动压缩线遮蔽;显式配置优先(`coreOverrides.nudge` 同名键优先级更高,见下) |
207
+ | `coreOverrides` | — | 任何其他 acp-kernel `Config` 覆盖(billion-context-pi 的 `coreOverrides` 逃生口)。合并顺序:内核默认 → 顶层 pct 配置 → `coreOverrides.nudge` 最后落地——同名键以它为准 |
208
208
  | `autoTools` | `true` | 在 `ctx.tools` 注册四个模型工具 |
209
209
  | `autoCommand` | `true` | 在 `ctx.commands` 注册 `/acp` 命令 |
210
210
  | `autoNudge` | `true` | 当内核建议时向 `agent/pre-step` 注入 nudge |
package/dist/index.d.ts CHANGED
@@ -30,6 +30,7 @@ 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 ToolEnvironment } from './tools.ts';
33
34
  import { type AcpPrompts, type ResolvedPrompts } from './prompts.ts';
34
35
  import { type AcpWindow } from './window.ts';
35
36
  export { AcpStateStore } from './state.ts';
@@ -59,7 +60,8 @@ export interface AcpConfig {
59
60
  * kernel injects a nudge regardless of growth or cadence. Engine default
60
61
  * 0.70 (deliberately BELOW the kernel/billion-context-pi default 0.75 and
61
62
  * the host compaction-basic auto-compaction line 0.80, so the forced nudge
62
- * always fires first); an explicit value wins.
63
+ * always fires first); an explicit value wins over this default — a
64
+ * same-name key in `coreOverrides.nudge` wins over both (it merges last).
63
65
  */
64
66
  readonly nudgeMaxContextLimitPct?: number;
65
67
  /**
@@ -69,7 +71,11 @@ export interface AcpConfig {
69
71
  * compaction-basic line shadows it in standard/code/cordis modes).
70
72
  */
71
73
  readonly nudgeEmergencyThresholdPct?: number;
72
- /** Any other acp-kernel Config override (billion-context-pi's `coreOverrides` escape hatch). */
74
+ /**
75
+ * Any other acp-kernel Config override (billion-context-pi's `coreOverrides`
76
+ * escape hatch). Merge order per section: kernel defaults → the engine pct
77
+ * knobs above → these keys land LAST, so a same-name key here wins.
78
+ */
73
79
  readonly coreOverrides?: Partial<import('acp-kernel').Config>;
74
80
  /**
75
81
  * Custom token-count function for the kernel's internal estimation.
@@ -107,6 +113,14 @@ export declare class AcpCompactionEngine extends CompactionEngine {
107
113
  readonly config: AcpConfig;
108
114
  /** Resolved prompt templates (validated at construction — fail-fast on template typos). */
109
115
  readonly prompts: ResolvedPrompts;
116
+ /**
117
+ * The environment wired into tools / command / nudge. Exposed so tests (and
118
+ * introspection) can assert the forwarding actually happened: the config
119
+ * chain user config → this.config → env → kernelConfigFor is all OPTIONAL
120
+ * fields, so a dropped forwarding line fails typecheck silently and would
121
+ * revive lost-config bugs with every unit test green.
122
+ */
123
+ readonly env: ToolEnvironment;
110
124
  private readonly lastNudgeTurn;
111
125
  /** Successful compress call ids awaiting their tool/result so the pair can be hidden. */
112
126
  private readonly compressCallIdsToHide;
package/dist/index.js CHANGED
@@ -3502,7 +3502,7 @@ var AcpStateStore = class {
3502
3502
  };
3503
3503
 
3504
3504
  // src/tools.ts
3505
- import { defineTool } from "@deepseek-ai/dsh-tools";
3505
+ import { defineTool, ToolArgsError } from "@deepseek-ai/dsh-tools";
3506
3506
 
3507
3507
  // src/config.ts
3508
3508
  function kernelConfigFor(input) {
@@ -3511,8 +3511,12 @@ function kernelConfigFor(input) {
3511
3511
  if (input.nudgeMaxContextLimitPct !== void 0) nudgePatch.maxContextLimitPct = input.nudgeMaxContextLimitPct;
3512
3512
  if (input.nudgeEmergencyThresholdPct !== void 0) nudgePatch.emergencyThresholdPct = input.nudgeEmergencyThresholdPct;
3513
3513
  const overrides = { ...input.coreOverrides };
3514
- if (Object.keys(nudgePatch).length > 0) {
3515
- overrides.nudge = { ...defaultConfig(input.modelContextLimit).nudge, ...nudgePatch };
3514
+ if (Object.keys(nudgePatch).length > 0 || input.coreOverrides?.nudge) {
3515
+ overrides.nudge = {
3516
+ ...defaultConfig(input.modelContextLimit).nudge,
3517
+ ...nudgePatch,
3518
+ ...input.coreOverrides?.nudge
3519
+ };
3516
3520
  }
3517
3521
  return defaultConfig(input.modelContextLimit, overrides);
3518
3522
  }
@@ -3851,6 +3855,16 @@ var compressParameters = {
3851
3855
  // intentionally NOT `required: true` — a required property would reject the
3852
3856
  // wrapped shape before `handleCompress` can see it. The tool description
3853
3857
  // still tells the model content is mandatory.
3858
+ //
3859
+ // The items fields are the opposite case: startSeq/endSeq/summary MUST be
3860
+ // `required: true`. Without that, a model call that omits `summary` (only
3861
+ // startSeq/endSeq/topic present) passed schema validation and failed late
3862
+ // inside the kernel with "Summary is empty" — and live sessions showed the
3863
+ // model retrying the identical broken call in a loop. With the fields
3864
+ // required, the same call is rejected at the schema gate with
3865
+ // `missing required property "content[0].summary"`, which tells the model
3866
+ // exactly which field to add (same pattern as decompress's required
3867
+ // blockId / search_context's required query).
3854
3868
  arguments: { type: "json", description: "Tolerated wrapped-arguments form (model-generated); unwrapped in handleCompress. Prefer passing content directly." },
3855
3869
  topic: { type: "string", description: "Fallback topic for entries without their own." },
3856
3870
  content: {
@@ -3860,18 +3874,20 @@ var compressParameters = {
3860
3874
  type: "object",
3861
3875
  properties: {
3862
3876
  startSeq: {
3877
+ required: true,
3863
3878
  oneOf: [
3864
3879
  { type: "integer", description: "First surface seq of the range." },
3865
3880
  { type: "string", description: "Seq as text; a trailing #callId fragment is ignored." }
3866
3881
  ]
3867
3882
  },
3868
3883
  endSeq: {
3884
+ required: true,
3869
3885
  oneOf: [
3870
3886
  { type: "integer", description: "Inclusive last surface seq of the range." },
3871
3887
  { type: "string", description: "Seq as text; a trailing #callId fragment is ignored." }
3872
3888
  ]
3873
3889
  },
3874
- summary: { type: "string", description: "Complete technical summary replacing the range; keep paths, decisions, values verbatim. Minimum 50 characters." },
3890
+ summary: { type: "string", required: true, description: "Complete technical summary replacing the range; keep paths, decisions, values verbatim. Minimum 50 characters." },
3875
3891
  topic: { type: "string", description: "Short label (3-5 words) for this range." }
3876
3892
  },
3877
3893
  additionalProperties: false
@@ -3942,6 +3958,18 @@ function unwrapEnvelope(args) {
3942
3958
  if (typeof inner !== "object" || inner === null || Array.isArray(inner)) return args;
3943
3959
  return { ...args, ...inner };
3944
3960
  }
3961
+ function validateContentItems(content) {
3962
+ const violations = [];
3963
+ content.forEach((item, index) => {
3964
+ const path = `content[${index}]`;
3965
+ if (item.startSeq === void 0) violations.push(`missing required property "${path}.startSeq"`);
3966
+ if (item.endSeq === void 0) violations.push(`missing required property "${path}.endSeq"`);
3967
+ if (typeof item.summary !== "string" || item.summary.trim().length === 0) {
3968
+ violations.push(`missing required property "${path}.summary"`);
3969
+ }
3970
+ });
3971
+ if (violations.length > 0) throw new ToolArgsError(violations);
3972
+ }
3945
3973
  async function handleCompress(env, args, exec) {
3946
3974
  const agent = requireAgent(exec);
3947
3975
  const session = agent.session;
@@ -3963,6 +3991,7 @@ async function handleCompress(env, args, exec) {
3963
3991
  };
3964
3992
  }
3965
3993
  args = unwrapped;
3994
+ validateContentItems(args.content);
3966
3995
  const ranges = [];
3967
3996
  const alreadyCompressedNotes = [];
3968
3997
  for (const range of args.content) {
@@ -4438,7 +4467,9 @@ var DEFAULT_CONFIG = {
4438
4467
  // kernel/billion-context-pi 0.75/0.95. 0.95 leaves no room to act before
4439
4468
  // the API rejects, and the host's compaction-basic line (thresholdRatio
4440
4469
  // 0.80) shadows it in standard/code/cordis modes; 0.70 keeps the forced
4441
- // over-limit nudge ahead of that 80% line. Explicit values always win.
4470
+ // over-limit nudge ahead of that 80% line. Explicit values always win
4471
+ // against these defaults — `coreOverrides` merges last and beats them on
4472
+ // same-name keys.
4442
4473
  nudgeMaxContextLimitPct: 0.7,
4443
4474
  nudgeEmergencyThresholdPct: 0.85
4444
4475
  };
@@ -4454,6 +4485,14 @@ var AcpCompactionEngine = class extends CompactionEngine {
4454
4485
  config;
4455
4486
  /** Resolved prompt templates (validated at construction — fail-fast on template typos). */
4456
4487
  prompts;
4488
+ /**
4489
+ * The environment wired into tools / command / nudge. Exposed so tests (and
4490
+ * introspection) can assert the forwarding actually happened: the config
4491
+ * chain user config → this.config → env → kernelConfigFor is all OPTIONAL
4492
+ * fields, so a dropped forwarding line fails typecheck silently and would
4493
+ * revive lost-config bugs with every unit test green.
4494
+ */
4495
+ env;
4457
4496
  lastNudgeTurn = /* @__PURE__ */ new Map();
4458
4497
  /** Successful compress call ids awaiting their tool/result so the pair can be hidden. */
4459
4498
  compressCallIdsToHide = /* @__PURE__ */ new Set();
@@ -4479,6 +4518,7 @@ var AcpCompactionEngine = class extends CompactionEngine {
4479
4518
  prompts: this.prompts,
4480
4519
  compressCallIdsToHide: this.compressCallIdsToHide
4481
4520
  };
4521
+ this.env = env;
4482
4522
  const tools = ctx.get("tools");
4483
4523
  if (tools !== void 0) {
4484
4524
  for (const tool of makeTools(env)) tools.register(tool);