billion-context-dsh 0.2.17 → 0.2.19

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.17**) 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.19**) 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 ⭐:
@@ -68,15 +68,24 @@ The command installs the package and automatically layers this package's bundle
68
68
 
69
69
  Restart `dsh` afterwards (bundle layers are composed at startup), open a new session, and verify: ask the model to call `acp_status`, or run `/acp status`. Shipped presets (standard / code / cordis) keep their realm-local `compaction-basic` fallback (automatic pressure compression still runs there; the ACP tools and nudge coexist); minimal and presets without a compaction realm use this engine directly.
70
70
 
71
- > **DSH version compatibility.** The package declares the peer range
72
- > `^0.1.0-rc.6 || ^0.1.1-rc.1` for `@deepseek-ai/dsh-compaction`, covering both the
73
- > `0.1.0-rc.x` and `0.1.1-rc.x` release lines (including the current DSH release;
74
- > the seam's `src/` is unchanged from `0.1.0-rc.6` to `0.1.1-rc.2`, so the public
75
- > API is identical). The range is two `||` clauses **on purpose**: npm
71
+ > **DSH version compatibility.** The package declares all four runtime seam
72
+ > packages (`dsh-compaction` / `dsh-session` / `dsh-llm` / `dsh-tools`) as peer
73
+ > dependencies, sharing the range
74
+ > `^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.4`,
75
+ > covering the `0.1.0-rc.x` and `0.1.1-rc.x` release lines (including the current
76
+ > DSH release; the seam's `src/` is unchanged from `0.1.0-rc.6` to `0.1.1-rc.2`, so the public
77
+ > API is identical) and the `0.1.2-alpha.x` line (which removed the
78
+ > `Session.events` getter in favour of `snapshotEvents()` / `eventAt()`; this
79
+ > engine feature-detects both shapes, so one build runs on either seam). The
80
+ > range is multiple `||` clauses **on purpose**: npm
76
81
  > (node-semver) only lets a prerelease version satisfy a range that carries a
77
82
  > comparator on the SAME `[major, minor, patch]` tuple as the candidate, so a lone
78
- > `^0.1.0-rc.6` can never match `0.1.1-rc.x` (issue #68) older releases fail to
79
- > install on DSH 0.1.1-rc.x; upgrade to a release containing this fix.
83
+ > `^0.1.0-rc.6` can never match `0.1.1-rc.x` (issue #68) or `0.1.2-alpha.x`
84
+ > older releases fail to install on DSH 0.1.1-rc.x / 0.1.2-alpha.x; upgrade to a
85
+ > release containing this fix. Declaring all four seam packages as peers (not
86
+ > just `dsh-compaction`) ensures that, even under pnpm's
87
+ > hoisted/linked layout, installations resolve them to the **host's own** copy
88
+ > rather than a stale nested copy inconsistent with the host.
80
89
 
81
90
  **Path B: plain `npm install` (package only — a composition row is required).**
82
91
 
@@ -89,7 +98,7 @@ This only installs the package into your project/global store; it does **not** t
89
98
  **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:
90
99
 
91
100
  ```bash
92
- dsh plugin --profile web add github:Tyan66666/billion-context-dsh#v0.2.17
101
+ dsh plugin --profile web add github:Tyan66666/billion-context-dsh#v0.2.19
93
102
  ```
94
103
 
95
104
  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).
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [中文](./README.md) | [English](./README.en.md)
4
4
 
5
5
  > **⚠️ 测试版声明——请勿用于生产环境**
6
- > 本项目(**v0.2.17**)仍处于开发中的测试版。[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 本身也处于**公开测试版**阶段。**请勿将两者用于工程化 / 生产环境**——预期会有破坏性变更与粗糙之处。
6
+ > 本项目(**v0.2.19**)仍处于开发中的测试版。[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 本身也处于**公开测试版**阶段。**请勿将两者用于工程化 / 生产环境**——预期会有破坏性变更与粗糙之处。
7
7
 
8
8
  <p align="center">
9
9
  <strong>衷心感谢以下项目——请给它们一个 ⭐:</strong>
@@ -71,14 +71,20 @@ dsh plugin --profile web add billion-context-dsh
71
71
 
72
72
  装完**重启 `dsh`**(bundle 层在启动时组合),新开会话即可用——让模型调用 `acp_status` 或执行 `/acp status` 自证。shipped 预设(standard / code / cordis)内部的 realm 级 `compaction-basic` 自动压缩兜底仍然保留(这些模式里"自动摘要"照旧,ACP 工具与 nudge 并存);minimal 等不带 compaction realm 的预设直接使用本引擎。
73
73
 
74
- > **与 DSH 版本的兼容性。** 包声明 peer 依赖 `@deepseek-ai/dsh-compaction`
75
- > `^0.1.0-rc.6 || ^0.1.1-rc.1`,同时覆盖 `0.1.0-rc.x` `0.1.1-rc.x` 两条 rc 线
76
- > (含当前最新 DSH release;从 `0.1.0-rc.6` `0.1.1-rc.2`,seam `src/` 源码
77
- > 零改动,公开 API 完全一致)。范围写成两个并集子句是**有意为之**:npm
78
- > (node-semver)的预发布匹配规则要求 range 里存在与候选版本**相同
74
+ > **与 DSH 版本的兼容性。** 包把四个运行期 seam 包(`dsh-compaction` /
75
+ > `dsh-session` / `dsh-llm` / `dsh-tools`)都声明为 peer 依赖,共享同一个
76
+ > 范围 `^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.4`,同时覆盖 `0.1.0-rc.x`、
77
+ > `0.1.1-rc.x` 两条 rc 线与 `0.1.2-alpha.x` 线(从 `0.1.0-rc.6` 到 `0.1.1-rc.2`,
78
+ > seam 的 `src/` 源码零改动,公开 API 完全一致;`0.1.2-alpha` 移除了
79
+ > `Session.events` getter,改为 `snapshotEvents()` / `eventAt()`,本引擎对两种
80
+ > 形态做了特性探测,单一构建两端通用)。范围写成多个并集子句是**有意为之**:
81
+ > npm (node-semver)的预发布匹配规则要求 range 里存在与候选版本**相同
79
82
  > `[major, minor, patch]` 元组**的比较器,单一 `^0.1.0-rc.6` 永远匹配不了
80
- > `0.1.1-rc.x`(issue #68)——因此旧发布的包在 DSH 0.1.1-rc.x 上装不上,
81
- > 升级到含本次修复的新版本即可。
83
+ > `0.1.1-rc.x`(issue #68),也匹配不了 `0.1.2-alpha.x`——因此旧发布的包在
84
+ > DSH 0.1.1-rc.x / 0.1.2-alpha.x 上装不上,升级到含本次修复的新版本即可。
85
+ > 把这四个 seam 包一并声明为 peer(而不只是 `dsh-compaction`),是为了让
86
+ > 安装在 pnpm 的集成/封存布局下仍能把它们解析到**宿主自己的副本**,而不是
87
+ > 某个与宿主不一致的陈旧嵌套副本。
82
88
 
83
89
  **方式二:纯 `npm install`(只装包,需要手写组合行)。**
84
90
 
@@ -91,7 +97,7 @@ npm install billion-context-dsh
91
97
  **git 源安装(`github:` 规格,插件商店展示的形态)。** 预构建产物 `dist/` 已提交到仓库,从 git 源安装同样开箱即用——**无需任何构建步骤**,pnpm 11 默认拦截构建脚本(`allowBuilds`)的机制对这个包不构成障碍:
92
98
 
93
99
  ```bash
94
- dsh plugin --profile web add github:Tyan66666/billion-context-dsh#v0.2.17
100
+ dsh plugin --profile web add github:Tyan66666/billion-context-dsh#v0.2.19
95
101
  ```
96
102
 
97
103
  建议带 `#<tag>` 安装,拿到与对应 npm 版本完全一致的产物;不带 ref 则装默认分支的最新构建。只有 clone 仓库自行从源码构建(`npm run build`)才需要放行构建。背景与方案取舍见 [docs/git-source-install-design.md](docs/git-source-install-design.md)(issue #92)。
package/dist/index.js CHANGED
@@ -2645,6 +2645,18 @@ import {
2645
2645
  } from "@deepseek-ai/dsh-compaction";
2646
2646
  import { createAssistantMessage, createUserMessage } from "@deepseek-ai/dsh-llm";
2647
2647
 
2648
+ // src/session-events.ts
2649
+ function sessionEventsOf(session) {
2650
+ const snapshot = session.snapshotEvents?.();
2651
+ if (snapshot !== void 0) return snapshot;
2652
+ return session.events;
2653
+ }
2654
+ function eventAtOf(session, seq) {
2655
+ const eventAt = session.eventAt;
2656
+ if (typeof eventAt === "function") return eventAt.call(session, seq);
2657
+ return session.events[seq];
2658
+ }
2659
+
2648
2660
  // src/messages.ts
2649
2661
  function extractText(content) {
2650
2662
  if (typeof content === "string") return content;
@@ -2757,10 +2769,10 @@ function eventsToCoreMessages(events, toolNames) {
2757
2769
  return out;
2758
2770
  }
2759
2771
  function surfaceEventsOf(session) {
2760
- return session.surface.nodes.map((seq) => session.events[seq]).filter((event) => event !== void 0);
2772
+ return session.surface.nodes.map((seq) => eventAtOf(session, seq)).filter((event) => event !== void 0);
2761
2773
  }
2762
2774
  function allLogMessages(session) {
2763
- return eventsToCoreMessages(session.events);
2775
+ return eventsToCoreMessages(sessionEventsOf(session));
2764
2776
  }
2765
2777
  function extractEventText(event) {
2766
2778
  switch (event.type) {
@@ -2826,7 +2838,7 @@ function hostPriceEvent(event) {
2826
2838
  function shadowedHostTokens(session, seqs) {
2827
2839
  let total = 0;
2828
2840
  for (const seq of seqs) {
2829
- const event = session.events[seq];
2841
+ const event = eventAtOf(session, seq);
2830
2842
  if (event !== void 0) total += hostPriceEvent(event);
2831
2843
  }
2832
2844
  return total;
@@ -2869,11 +2881,11 @@ function assertNoActiveCompaction(events) {
2869
2881
  else if (event.type === "compaction/end") active = false;
2870
2882
  }
2871
2883
  if (active) {
2872
- throw new Error("billion-context-dsh: another compaction is already active for this session");
2884
+ console.warn("billion-context-dsh: clearing stale compaction flag \u2014 found a compaction/start with no matching compaction/end");
2873
2885
  }
2874
2886
  }
2875
2887
  function hasPlainRef(session, seq) {
2876
- const event = session.events[seq];
2888
+ const event = eventAtOf(session, seq);
2877
2889
  if (event === void 0) return false;
2878
2890
  switch (event.type) {
2879
2891
  case "user/message":
@@ -2906,14 +2918,14 @@ var AlreadyCompressedRangeError = class extends Error {
2906
2918
  coveringBlockIds;
2907
2919
  };
2908
2920
  function recoverStaleRange(session, start, end) {
2909
- if (session.events[start] === void 0 || session.events[end] === void 0) {
2910
- const failedEdge = session.events[start] === void 0 ? start : end;
2921
+ if (eventAtOf(session, start) === void 0 || eventAtOf(session, end) === void 0) {
2922
+ const failedEdge = eventAtOf(session, start) === void 0 ? start : end;
2911
2923
  return { kind: "unresolvable", failedEdge };
2912
2924
  }
2913
2925
  const liveInside = session.surface.nodes.filter((seq) => seq >= start && seq <= end).sort((a, b) => a - b);
2914
- const plain = liveInside.filter((seq) => !isCheckpointNode(session.events[seq]));
2926
+ const plain = liveInside.filter((seq) => !isCheckpointNode(eventAtOf(session, seq)));
2915
2927
  if (plain.length === 0) {
2916
- const coveringBlockIds = rebuildBlockLedger(session.events).filter((entry) => entry.shadowedSeqs.some((seq) => seq >= start && seq <= end)).map((entry) => entry.blockId);
2928
+ const coveringBlockIds = rebuildBlockLedger(sessionEventsOf(session)).filter((entry) => entry.shadowedSeqs.some((seq) => seq >= start && seq <= end)).map((entry) => entry.blockId);
2917
2929
  return { kind: "already-compressed", coveringBlockIds };
2918
2930
  }
2919
2931
  return { kind: "ok", start: plain[0], end: plain[plain.length - 1] };
@@ -2996,35 +3008,53 @@ function readCompactionSummary(event) {
2996
3008
  return event.data;
2997
3009
  }
2998
3010
  function runCompactionTransaction(session, input) {
2999
- assertNoActiveCompaction(session.events);
3000
- const turn = findOpenTurn(session.events);
3011
+ assertNoActiveCompaction(sessionEventsOf(session));
3012
+ const turn = findOpenTurn(sessionEventsOf(session));
3001
3013
  const compactionId = CompactionId(randomUUID());
3002
3014
  const seqs = [];
3003
- seqs.push(session.append("compaction/start", { compactionId, turn }).seq);
3004
- seqs.push(session.append("compaction/summary", {
3005
- compactionId,
3006
- summary: input.summary,
3007
- shadowedRange: { start: input.start, end: input.end },
3008
- shadowedSeqs: [...input.shadowedSeqs],
3009
- shadowedTokenCount: input.shadowedTokenCount,
3010
- provider: input.provider,
3011
- model: input.model,
3012
- tier: input.tier ?? 1,
3013
- ...input.kernelBlockId === void 0 ? {} : { kernelBlockId: input.kernelBlockId },
3014
- ...input.topic === void 0 ? {} : { topic: input.topic },
3015
- ...input.parentBlockIds === void 0 || input.parentBlockIds.length === 0 ? {} : { parentBlockIds: [...input.parentBlockIds] },
3016
- ...input.directMessageIds === void 0 ? {} : { directMessageIds: [...input.directMessageIds] },
3017
- ...input.effectiveMessageIds === void 0 ? {} : { effectiveMessageIds: [...input.effectiveMessageIds] }
3018
- }).seq);
3019
- const message = createUserMessage({
3020
- content: input.summary,
3021
- source: compactCheckpointSource(compactionId)
3022
- });
3023
- seqs.push(session.append("user/message", message, {
3024
- surfaceOp: { op: "replace", start: input.start, end: input.end },
3025
- sourceEventSeqs: [...input.shadowedSeqs]
3026
- }).seq);
3027
- seqs.push(session.append("compaction/end", { compactionId, turn }).seq);
3015
+ if (input.start > input.end) {
3016
+ throw new Error(`billion-context-dsh: reversed range ${input.start}..${input.end}`);
3017
+ }
3018
+ if (eventAtOf(session, input.start) === void 0 || eventAtOf(session, input.end) === void 0) {
3019
+ const failedEdge = eventAtOf(session, input.start) === void 0 ? input.start : input.end;
3020
+ throw new Error(
3021
+ `billion-context-dsh: seq ${input.start}..${input.end} not in the current surface \u2014 edge seq ${failedEdge} is not in this session's log. Surface seqs are sparse message nodes (only user/message, assistant/message, tool/result events); consult acp_status for the current surface range`
3022
+ );
3023
+ }
3024
+ try {
3025
+ seqs.push(session.append("compaction/start", { compactionId, turn }).seq);
3026
+ seqs.push(session.append("compaction/summary", {
3027
+ compactionId,
3028
+ summary: input.summary,
3029
+ shadowedRange: { start: input.start, end: input.end },
3030
+ shadowedSeqs: [...input.shadowedSeqs],
3031
+ shadowedTokenCount: input.shadowedTokenCount,
3032
+ provider: input.provider,
3033
+ model: input.model,
3034
+ tier: input.tier ?? 1,
3035
+ ...input.kernelBlockId === void 0 ? {} : { kernelBlockId: input.kernelBlockId },
3036
+ ...input.topic === void 0 ? {} : { topic: input.topic },
3037
+ ...input.parentBlockIds === void 0 || input.parentBlockIds.length === 0 ? {} : { parentBlockIds: [...input.parentBlockIds] },
3038
+ ...input.directMessageIds === void 0 ? {} : { directMessageIds: [...input.directMessageIds] },
3039
+ ...input.effectiveMessageIds === void 0 ? {} : { effectiveMessageIds: [...input.effectiveMessageIds] }
3040
+ }).seq);
3041
+ const message = createUserMessage({
3042
+ content: input.summary,
3043
+ source: compactCheckpointSource(compactionId)
3044
+ });
3045
+ seqs.push(session.append("user/message", message, {
3046
+ surfaceOp: { op: "replace", start: input.start, end: input.end },
3047
+ sourceEventSeqs: [...input.shadowedSeqs]
3048
+ }).seq);
3049
+ seqs.push(session.append("compaction/end", { compactionId, turn }).seq);
3050
+ } catch (error) {
3051
+ try {
3052
+ session.append("compaction/end", { compactionId, turn });
3053
+ } catch (compensateError) {
3054
+ console.warn("billion-context-dsh: failed to write a compensating compaction/end", compensateError);
3055
+ }
3056
+ throw error;
3057
+ }
3028
3058
  return { compactionId, seqs };
3029
3059
  }
3030
3060
  function summarySeqOfCompaction(events, compactionId) {
@@ -3111,7 +3141,7 @@ function hideSurfaceSeqs(session, seqs, provider, model, text, priceEvent = host
3111
3141
  const end = seqs[seqs.length - 1];
3112
3142
  let shadowedTokenCount = 0;
3113
3143
  for (const seq of seqs) {
3114
- const event = session.events[seq];
3144
+ const event = eventAtOf(session, seq);
3115
3145
  if (event !== void 0) shadowedTokenCount += priceEvent(event);
3116
3146
  }
3117
3147
  session.append("compaction/prune", {
@@ -3130,7 +3160,7 @@ function hideSurfaceSeqs(session, seqs, provider, model, text, priceEvent = host
3130
3160
  return;
3131
3161
  }
3132
3162
  session.append("assistant/message", {
3133
- turn: findOpenTurn(session.events) ?? 0,
3163
+ turn: findOpenTurn(sessionEventsOf(session)) ?? 0,
3134
3164
  step: 0,
3135
3165
  message: createAssistantMessage({ content: [], source: { provider, model } })
3136
3166
  }, {
@@ -3140,7 +3170,8 @@ function hideSurfaceSeqs(session, seqs, provider, model, text, priceEvent = host
3140
3170
  }
3141
3171
  function hideCompressToolPair(session, callId, resultSeq) {
3142
3172
  let callSeq = null;
3143
- for (const event of session.events) {
3173
+ const events = sessionEventsOf(session);
3174
+ for (const event of events) {
3144
3175
  if (event.type !== "assistant/message") continue;
3145
3176
  if (toolCallIdsOfEvent(event).includes(callId)) {
3146
3177
  callSeq = event.seq;
@@ -3148,11 +3179,11 @@ function hideCompressToolPair(session, callId, resultSeq) {
3148
3179
  }
3149
3180
  }
3150
3181
  if (callSeq === null) return false;
3151
- const callNodeIds = toolCallIdsOfEvent(session.events[callSeq]);
3182
+ const callNodeIds = toolCallIdsOfEvent(events[callSeq]);
3152
3183
  if (callNodeIds.length !== 1 || callNodeIds[0] !== callId) return false;
3153
3184
  let resolvedResultSeq = resultSeq ?? null;
3154
3185
  if (resolvedResultSeq === null) {
3155
- for (const event of session.events) {
3186
+ for (const event of events) {
3156
3187
  if (event.type === "tool/result" && toolCallIdOfResultEvent(event) === callId) {
3157
3188
  resolvedResultSeq = event.seq;
3158
3189
  break;
@@ -3164,8 +3195,8 @@ function hideCompressToolPair(session, callId, resultSeq) {
3164
3195
  const startIdx = nodes.indexOf(callSeq);
3165
3196
  const endIdx = nodes.indexOf(resolvedResultSeq);
3166
3197
  if (startIdx < 0 || endIdx < 0 || endIdx - startIdx !== 1) return false;
3167
- const { provider, model } = assistantProviderModel(session.events[callSeq]);
3168
- const resultEvent = session.events[resolvedResultSeq];
3198
+ const { provider, model } = assistantProviderModel(events[callSeq]);
3199
+ const resultEvent = events[resolvedResultSeq];
3169
3200
  const resultText = resultEvent === void 0 ? "" : extractEventText(resultEvent);
3170
3201
  hideSurfaceSeqs(session, [callSeq, resolvedResultSeq], provider, model, resultText.trim().length > 0 ? resultText : void 0);
3171
3202
  return true;
@@ -3178,7 +3209,7 @@ function stripOrphanedSurfaceToolMessages(session, inFlightCallIds = /* @__PURE_
3178
3209
  const brokenResults = /* @__PURE__ */ new Map();
3179
3210
  for (let index = 0; index < nodes.length; index += 1) {
3180
3211
  const seq = nodes[index];
3181
- const event = session.events[seq];
3212
+ const event = eventAtOf(session, seq);
3182
3213
  if (event === void 0) continue;
3183
3214
  if (event.type === "assistant/message") {
3184
3215
  const ids = toolCallIdsOfEvent(event);
@@ -3200,7 +3231,7 @@ function stripOrphanedSurfaceToolMessages(session, inFlightCallIds = /* @__PURE_
3200
3231
  if (callNodeIds !== void 0) {
3201
3232
  adjacent = true;
3202
3233
  for (let mid = call.index + 1; mid < index; mid += 1) {
3203
- const midEvent = session.events[nodes[mid]];
3234
+ const midEvent = eventAtOf(session, nodes[mid]);
3204
3235
  if (midEvent === void 0 || midEvent.type !== "tool/result") {
3205
3236
  adjacent = false;
3206
3237
  break;
@@ -3218,7 +3249,7 @@ function stripOrphanedSurfaceToolMessages(session, inFlightCallIds = /* @__PURE_
3218
3249
  }
3219
3250
  const brokenIdsByCallSeq = /* @__PURE__ */ new Map();
3220
3251
  for (const [resultSeq, callSeq] of brokenResults) {
3221
- const id = toolCallIdOfResultEvent(session.events[resultSeq]);
3252
+ const id = toolCallIdOfResultEvent(eventAtOf(session, resultSeq));
3222
3253
  if (id !== null) {
3223
3254
  const list = brokenIdsByCallSeq.get(callSeq) ?? [];
3224
3255
  list.push(id);
@@ -3235,7 +3266,7 @@ function stripOrphanedSurfaceToolMessages(session, inFlightCallIds = /* @__PURE_
3235
3266
  const hidden = [...hiddenSet].sort((a, b) => a - b);
3236
3267
  let count = 0;
3237
3268
  for (const seq of hidden) {
3238
- const event = session.events[seq];
3269
+ const event = eventAtOf(session, seq);
3239
3270
  if (event === void 0) continue;
3240
3271
  const { provider, model } = assistantProviderModel(event);
3241
3272
  hideSurfaceSeqs(session, [seq], provider, model);
@@ -3246,7 +3277,7 @@ function stripOrphanedSurfaceToolMessages(session, inFlightCallIds = /* @__PURE_
3246
3277
  function openToolCallIds(session) {
3247
3278
  const open = /* @__PURE__ */ new Set();
3248
3279
  for (const seq of session.surface.nodes) {
3249
- const event = session.events[seq];
3280
+ const event = eventAtOf(session, seq);
3250
3281
  if (event === void 0) continue;
3251
3282
  if (event.type === "assistant/message") {
3252
3283
  for (const id of toolCallIdsOfEvent(event)) open.add(id);
@@ -3275,7 +3306,7 @@ function buildCompressibleSeqRanges(session, opts = {}) {
3275
3306
  for (const seq of nodes.slice(-preserve)) protectedSeqs.add(seq);
3276
3307
  }
3277
3308
  for (let index = nodes.length - 1; index >= 0; index -= 1) {
3278
- const event = session.events[nodes[index]];
3309
+ const event = eventAtOf(session, nodes[index]);
3279
3310
  if (event?.type === "user/message" && !isCheckpointNode(event)) {
3280
3311
  protectedSeqs.add(nodes[index]);
3281
3312
  break;
@@ -3288,7 +3319,7 @@ function buildCompressibleSeqRanges(session, opts = {}) {
3288
3319
  cur = null;
3289
3320
  };
3290
3321
  for (const seq of nodes) {
3291
- const event = session.events[seq];
3322
+ const event = eventAtOf(session, seq);
3292
3323
  if (event === void 0 || protectedSeqs.has(seq) || isCheckpointNode(event)) {
3293
3324
  flush();
3294
3325
  continue;
@@ -3335,7 +3366,7 @@ function surfaceSummary(session) {
3335
3366
  return `${nodes.length} nodes, seqs ${first}..${last}`;
3336
3367
  }
3337
3368
  function blockRegistry(session) {
3338
- const ledger = rebuildBlockLedger(session.events);
3369
+ const ledger = rebuildBlockLedger(sessionEventsOf(session));
3339
3370
  const kernelIdOf = /* @__PURE__ */ new Map();
3340
3371
  const raw = [];
3341
3372
  let next = 1;
@@ -3369,7 +3400,7 @@ function blockRegistry(session) {
3369
3400
  }));
3370
3401
  }
3371
3402
  function blockRefForSummarySeq(session, seq) {
3372
- const event = session.events[seq];
3403
+ const event = eventAtOf(session, seq);
3373
3404
  if (event?.type !== "user/message") return null;
3374
3405
  const source = event.data.source;
3375
3406
  if (source?.plugin !== "compact" || source.compactionId === void 0) return null;
@@ -3399,7 +3430,7 @@ function checkpointBlockIdOf(events, seq) {
3399
3430
  return source.compactionId;
3400
3431
  }
3401
3432
  function expandShadowedSeqs(session, blockId) {
3402
- const ledger = rebuildBlockLedger(session.events);
3433
+ const ledger = rebuildBlockLedger(sessionEventsOf(session));
3403
3434
  const byId = new Map(ledger.map((entry) => [entry.blockId, entry]));
3404
3435
  const root = byId.get(blockId);
3405
3436
  if (root === void 0) return [];
@@ -3409,7 +3440,7 @@ function expandShadowedSeqs(session, blockId) {
3409
3440
  if (seen.has(entry.blockId)) return;
3410
3441
  seen.add(entry.blockId);
3411
3442
  for (const seq of entry.shadowedSeqs) {
3412
- const childId = checkpointBlockIdOf(session.events, seq);
3443
+ const childId = checkpointBlockIdOf(sessionEventsOf(session), seq);
3413
3444
  const child = childId === null ? void 0 : byId.get(childId);
3414
3445
  if (child !== void 0) visit(child);
3415
3446
  else out.push(seq);
@@ -3486,9 +3517,10 @@ var AcpStateStore = class {
3486
3517
  const existing = this.states.get(id);
3487
3518
  if (existing !== void 0) return existing;
3488
3519
  const state = createInitialState();
3489
- if (session.events.some((event) => event.type === "compaction/summary")) {
3490
- state.blocks = rebuildKernelBlocks(session.events);
3491
- state.nextBlockId = nextBlockIdAfter(session.events);
3520
+ const events = sessionEventsOf(session);
3521
+ if (events.some((event) => event.type === "compaction/summary")) {
3522
+ state.blocks = rebuildKernelBlocks(events);
3523
+ state.nextBlockId = nextBlockIdAfter(events);
3492
3524
  }
3493
3525
  this.states.set(id, state);
3494
3526
  return state;
@@ -3707,7 +3739,7 @@ function buildNudge(agent, env, lastNudgeTurn) {
3707
3739
  const nudge = turn.nudge;
3708
3740
  if (nudge === void 0 || !nudge.shouldInject) return null;
3709
3741
  const emergency = nudge.breakdown?.emergencyOverride === 1;
3710
- const turnNumber = findOpenTurn(session.events) ?? 0;
3742
+ const turnNumber = findOpenTurn(sessionEventsOf(session)) ?? 0;
3711
3743
  const alreadyShown = !emergency && lastNudgeTurn.get(session.id) === turnNumber;
3712
3744
  if (alreadyShown) return null;
3713
3745
  lastNudgeTurn.set(session.id, turnNumber);
@@ -4126,7 +4158,7 @@ var decompressParameters = {
4126
4158
  function resolveBlockId(session, arg) {
4127
4159
  const byKernelRef = blockIdOfKernelRef(session, arg);
4128
4160
  if (byKernelRef !== null) return byKernelRef;
4129
- const ledger = rebuildBlockLedger(session.events);
4161
+ const ledger = rebuildBlockLedger(sessionEventsOf(session));
4130
4162
  const byPrefix = ledger.find((entry) => entry.blockId.startsWith(arg));
4131
4163
  return byPrefix?.blockId ?? null;
4132
4164
  }
@@ -4137,14 +4169,14 @@ function handleDecompress(_env, rawArgs, exec) {
4137
4169
  if (blockId === null) {
4138
4170
  return { text: `decompress: block "${args.blockId}" not found (see acp_status for the block list)` };
4139
4171
  }
4140
- const ledger = rebuildBlockLedger(session.events);
4172
+ const ledger = rebuildBlockLedger(sessionEventsOf(session));
4141
4173
  const block = ledger.find((entry) => entry.blockId === blockId);
4142
4174
  if (block === void 0) {
4143
4175
  return { text: `decompress: block "${args.blockId}" not found (see acp_status for the block list)` };
4144
4176
  }
4145
4177
  const parts = [];
4146
4178
  for (const seq of expandShadowedSeqs(session, block.blockId)) {
4147
- const event = session.events[seq];
4179
+ const event = eventAtOf(session, seq);
4148
4180
  const text = event === void 0 ? "" : extractEventText(event);
4149
4181
  if (text.length > 0) parts.push(`[seq ${seq}] ${text}`);
4150
4182
  }
@@ -4172,7 +4204,7 @@ function roleOfEvent(event) {
4172
4204
  }
4173
4205
  }
4174
4206
  function buildSearchDocs(session) {
4175
- const ledger = rebuildBlockLedger(session.events);
4207
+ const ledger = rebuildBlockLedger(sessionEventsOf(session));
4176
4208
  const docs = [];
4177
4209
  const claimed = /* @__PURE__ */ new Set();
4178
4210
  for (const block of ledger) {
@@ -4188,7 +4220,7 @@ function buildSearchDocs(session) {
4188
4220
  for (const seq of expandShadowedSeqs(session, block.blockId)) {
4189
4221
  if (claimed.has(seq)) continue;
4190
4222
  claimed.add(seq);
4191
- const event = session.events[seq];
4223
+ const event = eventAtOf(session, seq);
4192
4224
  if (event === void 0) continue;
4193
4225
  const role = roleOfEvent(event);
4194
4226
  const text = extractEventText(event);
@@ -4367,7 +4399,7 @@ async function detectContextWindow(agent, provider, model) {
4367
4399
  // src/commands.ts
4368
4400
  async function statusText(env, agent) {
4369
4401
  const session = agent.session;
4370
- const ledger = rebuildBlockLedger(session.events);
4402
+ const ledger = rebuildBlockLedger(sessionEventsOf(session));
4371
4403
  const totalTokens = ledger.reduce((sum, block) => sum + block.shadowedTokenCount, 0);
4372
4404
  const coreMessages = allLogMessages(session);
4373
4405
  const surfaceMessages = eventsToCoreMessages(surfaceEventsOf(session));
@@ -4435,10 +4467,10 @@ function decompressText(_env, agent, args) {
4435
4467
  if (args.length < 1) return "/acp decompress <blockId>";
4436
4468
  const session = agent.session;
4437
4469
  const blockId = blockIdOfKernelRef(session, args[0]);
4438
- const ledger = rebuildBlockLedger(session.events);
4470
+ const ledger = rebuildBlockLedger(sessionEventsOf(session));
4439
4471
  const block = blockId === null ? ledger.find((entry) => entry.blockId.startsWith(args[0])) : ledger.find((entry) => entry.blockId === blockId);
4440
4472
  if (block === void 0) return `block "${args[0]}" not found (see /acp status)`;
4441
- const parts = expandShadowedSeqs(session, block.blockId).map((seq) => extractEventText(session.events[seq])).filter((text) => text.length > 0);
4473
+ const parts = expandShadowedSeqs(session, block.blockId).map((seq) => extractEventText(eventAtOf(session, seq))).filter((text) => text.length > 0);
4442
4474
  return `Block ${block.blockId} \u2014 ${block.summary}
4443
4475
 
4444
4476
  ${parts.join("\n\n") || "(no recoverable content)"}`;