acp-kernel 0.0.26 → 0.0.28

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.md CHANGED
@@ -85,6 +85,14 @@ processTurn({ messages, state, config, tokenCount, renderTags: "none" });
85
85
  `renderTags` is optional and defaults to `"all"`, so existing call sites keep
86
86
  working unchanged.
87
87
 
88
+ **Token counts in rendered tags are snapshots.** Each message's
89
+ `<acp tokens="N">` attribute is frozen at the tag's first render (the
90
+ per-message `tokenSnapshot` in state) and is not recomputed when the message
91
+ text is later filtered, truncated, or edited by the host — the number always
92
+ describes what the model originally saw. Nudge/pressure *decisions* are
93
+ unaffected: they recount live text every turn. If you need the legacy
94
+ live-recomputed tags, use `renderVisibleRefs` directly.
95
+
88
96
  ### Standalone modules
89
97
 
90
98
  | Module | Purpose |
@@ -95,6 +103,7 @@ working unchanged.
95
103
  | `mergeMarkedBlocks` / `collectOldGenBlocks` | Batch merge old-gen blocks into one summary |
96
104
  | `rebuildCompressionState` | Fork-recovery: replay historical compress calls |
97
105
  | `applyMessageFilters` | Pluggable message-filter framework |
106
+ | `resolveTransformChannel` | Channel-selection policy: an explicit preference wins; the default is the wire channel only when the caller reports it viable |
98
107
 
99
108
  ### Nudge system
100
109
 
@@ -106,9 +115,22 @@ The nudge system tells the model *when* to compress. It implements:
106
115
  - **Compressible-range computation**: reports the actual compressible ranges (excluding covered + preserved-recent messages) so the model knows what to target.
107
116
  - **Baseline reset on compress**: `applyCompression` clears the growth baseline on success, preventing the feedback-loop bug where the nudge re-fires post-compress.
108
117
 
118
+ ## Wire codec (`acp-kernel/wire`)
119
+
120
+ The `acp-kernel/wire` subpath ships the provider wire-body codecs (lossless
121
+ round-trip via the `BiliMessage` sidecar): `anthropicToCore`/`coreToAnthropic`,
122
+ `openaiToCore`/`coreToOpenai`, `responsesToCore`/`coreToResponses`, plus
123
+ `deriveMessageId` (content-hash message identity), conversation signals and the
124
+ subagent-namespace helpers. `WIRE_FORMATS` / `detectWireFormat` classify a
125
+ request body by the codec that can parse it — `undefined` means the body is
126
+ unparseable and must be passed through untransformed. Adapters map their
127
+ host's model/API ids to formats and use `resolveTransformChannel` (with
128
+ `wireViable` = body parseable AND the host applies the payload replacement)
129
+ to decide between message-level and wire-level surgery.
130
+
109
131
  ## Status
110
132
 
111
- ✅ **Engine complete** — 23 source modules, 167 tests, typecheck + build clean. 3-tier compression, growth-gated nudges, emergency truncation,, fork-recovery, batch merge, composable node pipeline. Ready for adapter authoring.
133
+ ✅ **Engine complete** — 23 source modules, full suite green (`npm test`), typecheck + build clean. 3-tier compression, growth-gated nudges, emergency truncation, fork-recovery, batch merge, composable node pipeline. Ready for adapter authoring.
112
134
 
113
135
  > **Protected tool messages:** protected tool calls (per `config.protectedTools`) and their paired tool-results are hard-excluded from compression — they are dropped from the compressible set and from the new block's `effectiveMessageIds`, so they stay fully visible and are never folded into a summary. This matches opencode-acp's Bug 39 fix. The soft-protected recent zone (`preserveRecentMessages` / last user message) is handled separately: messages there are excluded from the range but do not fail it (an entirely-protected range fails with a clear error).
114
136
 
@@ -1 +1 @@
1
- {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAapB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AASvD,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAEhB,MAAM,EAEN,WAAW,EAGX,iBAAiB,EAEjB,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,KAAK;IACpB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;IACxD,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAAC;IACvE,YAAY,IAAI,YAAY,EAAE,CAAC;IAC/B,UAAU,CACR,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,gBAAgB,GACtB,gBAAgB,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAAC;IACnE,MAAM,CACJ,KAAK,EAAE,gBAAgB,EACvB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,YAAY,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,EAAE,CAAC;IACJ,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnC;AAsBD,wBAAgB,UAAU,CAAC,KAAK,GAAE,KAAU,GAAG,eAAe,CAwQ7D;AAixBD,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAapB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AASvD,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAEhB,MAAM,EAEN,WAAW,EAGX,iBAAiB,EAEjB,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,KAAK;IACpB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;IACxD,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAAC;IACvE,YAAY,IAAI,YAAY,EAAE,CAAC;IAC/B,UAAU,CACR,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,gBAAgB,GACtB,gBAAgB,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAAC;IACnE,MAAM,CACJ,KAAK,EAAE,gBAAgB,EACvB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,YAAY,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,EAAE,CAAC;IACJ,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnC;AAsBD,wBAAgB,UAAU,CAAC,KAAK,GAAE,KAAU,GAAG,eAAe,CAwQ7D;AAqxBD,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -26,6 +26,8 @@ export { rebuildCompressionState } from "./rebuild.js";
26
26
  export type { RebuildResult, RebuildPorts } from "./rebuild.js";
27
27
  export { renderVisibleRefs, renderRefsNode, createRenderRefsNode } from "./render-refs.js";
28
28
  export type { RenderStrategy } from "./render-refs.js";
29
+ export { resolveTransformChannel } from "./transform-channel.js";
30
+ export type { TransformChannel } from "./transform-channel.js";
29
31
  export { searchBlocks, searchBlocksAsync, blockDocs, messageDocs } from "./search.js";
30
32
  export type { SearchResult, SearchOptions, SearchAlgorithm, AsyncSearchAlgorithm, AnySearchAlgorithm, SearchDoc, SearchDocKind, ScoredBlock, MessageRole, RoleWeights, MessageInput } from "./search.js";
31
33
  export { DEFAULT_ALGORITHM, DEFAULT_ROLE_WEIGHTS, registerSearchAlgorithm, getSearchAlgorithm, listSearchAlgorithms } from "./search.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -77,6 +77,7 @@ function createInitialState() {
77
77
  return {
78
78
  blocks: [],
79
79
  messageRefs: { byRaw: {}, byRef: {} },
80
+ tokenSnapshot: {},
80
81
  nudge: {
81
82
  lastPerMessageNudgeTokens: 0,
82
83
  lastNudgeShownTokens: 0,
@@ -257,11 +258,23 @@ function syncBlocks(messages, state) {
257
258
  byRaw: { ...state.messageRefs.byRaw },
258
259
  byRef: { ...state.messageRefs.byRef }
259
260
  },
261
+ // Snapshot is keyed by ref with primitive values — shallow copy suffices.
262
+ tokenSnapshot: { ...state.tokenSnapshot ?? {} },
260
263
  nudge: { ...state.nudge, anchors: { ...state.nudge.anchors } },
261
264
  stats: { ...state.stats },
262
265
  nextBlockId: state.nextBlockId,
263
266
  nextRunId: state.nextRunId
264
267
  };
268
+ const liveRefs = new Set(
269
+ messages.map((m) => result.messageRefs.byRaw[m.id]).filter((r) => typeof r === "string")
270
+ );
271
+ if (Object.keys(result.tokenSnapshot).length !== liveRefs.size) {
272
+ const pruned = {};
273
+ for (const [ref, n] of Object.entries(result.tokenSnapshot)) {
274
+ if (liveRefs.has(ref)) pruned[ref] = n;
275
+ }
276
+ result.tokenSnapshot = pruned;
277
+ }
265
278
  const consumedBlockIds = /* @__PURE__ */ new Set();
266
279
  for (const block of result.blocks) {
267
280
  for (const consumedId of block.directBlockIds) {
@@ -298,12 +311,14 @@ function estimateTokensFast(text) {
298
311
  if (!text) return 0;
299
312
  return Math.ceil(text.length / 4);
300
313
  }
314
+ var BPE_SIZE_GUARD = 1e5;
301
315
  function createBpeTokenizer() {
302
316
  try {
303
317
  const mod = require2("@anthropic-ai/tokenizer");
304
318
  const bpeCount = mod.countTokens ?? mod.default?.countTokens;
305
319
  if (typeof bpeCount !== "function") return defaultCountTokens;
306
320
  return (text) => {
321
+ if (text.length > BPE_SIZE_GUARD) return defaultCountTokens(text);
307
322
  try {
308
323
  return bpeCount(text);
309
324
  } catch {
@@ -788,7 +803,7 @@ var TAG_CLOSE = LT + "/acp" + GT;
788
803
  function acpTag(ref, tokens, type) {
789
804
  return TAG_OPEN + 'tokens="' + formatTokens(tokens) + '" type="' + type + '"' + GT + ref + TAG_CLOSE;
790
805
  }
791
- function renderMessage(message, map, countTokens, strategy) {
806
+ function renderMessage(message, map, countTokens, strategy, snapshot = null) {
792
807
  const ref = refForRaw(map, message.id);
793
808
  if (!ref || ref === BLOCKED_REF) return message;
794
809
  if (strategy === "none") return message;
@@ -799,7 +814,7 @@ function renderMessage(message, map, countTokens, strategy) {
799
814
  "^" + escapeRegex(TAG_OPEN) + "[^>]*" + GT + escapeRegex(ref) + escapeRegex(TAG_CLOSE) + "\\n?"
800
815
  );
801
816
  const cleanText = (message.text || "").replace(ownTagRe, "");
802
- const tokens = countTokens(cleanText);
817
+ const tokens = snapshot ? snapshot[ref] ?? (snapshot[ref] = countTokens(cleanText)) : countTokens(cleanText);
803
818
  const type = classifyType(message);
804
819
  const prefix = acpTag(ref, tokens, type) + "\n";
805
820
  if (!cleanText) return { ...message, text: prefix };
@@ -811,14 +826,27 @@ function renderVisibleRefs(messages, state, countTokens = (text) => Math.ceil(te
811
826
  (message) => renderMessage(message, map, countTokens, strategy)
812
827
  );
813
828
  }
829
+ function renderWithSnapshot(messages, state, countTokens = (text) => Math.ceil(text.length / 4), strategy = "all") {
830
+ const map = state.messageRefs;
831
+ const snapshot = { ...state.tokenSnapshot ?? {} };
832
+ const rendered = messages.map(
833
+ (message) => renderMessage(message, map, countTokens, strategy, snapshot)
834
+ );
835
+ return { messages: rendered, tokenSnapshot: snapshot };
836
+ }
814
837
  function createRenderRefsNode(strategy) {
815
838
  return {
816
839
  name: "render-refs",
817
840
  run(io, ctx) {
818
- return {
819
- ...io,
820
- messages: renderVisibleRefs(io.messages, io.state, ctx.countTokens, strategy)
821
- };
841
+ const { messages, tokenSnapshot } = renderWithSnapshot(
842
+ io.messages,
843
+ io.state,
844
+ ctx.countTokens,
845
+ strategy
846
+ );
847
+ const prev = io.state.tokenSnapshot;
848
+ const changed = !prev || Object.keys(tokenSnapshot).length !== Object.keys(prev).length;
849
+ return changed ? { ...io, messages, state: { ...io.state, tokenSnapshot } } : { ...io, messages };
822
850
  }
823
851
  };
824
852
  }
@@ -1022,6 +1050,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
1022
1050
  ref,
1023
1051
  refNum: rn,
1024
1052
  tokens: countTokens(msg.text ?? ""),
1053
+ chars: (msg.text ?? "").length,
1025
1054
  isTool: isToolMessage(msg),
1026
1055
  isUser: msg.role === "user"
1027
1056
  });
@@ -1042,6 +1071,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
1042
1071
  endRef: info.ref,
1043
1072
  count: 1,
1044
1073
  tokens: info.tokens,
1074
+ chars: info.chars,
1045
1075
  toolPct: info.isTool ? 100 : 0,
1046
1076
  textPct: info.isTool ? 0 : 100
1047
1077
  };
@@ -1049,6 +1079,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
1049
1079
  cur.endRef = info.ref;
1050
1080
  cur.count++;
1051
1081
  cur.tokens += info.tokens;
1082
+ cur.chars = (cur.chars ?? 0) + info.chars;
1052
1083
  if (info.isTool) {
1053
1084
  cur.toolPct = Math.round((cur.toolPct * (cur.count - 1) + 100) / cur.count);
1054
1085
  } else {
@@ -1096,6 +1127,7 @@ function mergeBatch(batch) {
1096
1127
  const last = batch[batch.length - 1];
1097
1128
  const count = batch.reduce((s, r) => s + r.count, 0);
1098
1129
  const tokens = batch.reduce((s, r) => s + r.tokens, 0);
1130
+ const chars = batch.reduce((s, r) => s + rangeChars(r), 0);
1099
1131
  const toolPct = Math.round(
1100
1132
  batch.reduce((s, r) => s + r.toolPct * r.count, 0) / count
1101
1133
  );
@@ -1104,6 +1136,7 @@ function mergeBatch(batch) {
1104
1136
  endRef: last.endRef,
1105
1137
  count,
1106
1138
  tokens,
1139
+ chars,
1107
1140
  toolPct,
1108
1141
  textPct: 100 - toolPct
1109
1142
  };
@@ -1112,16 +1145,21 @@ function mergeBatch(batch) {
1112
1145
  }
1113
1146
  return merged;
1114
1147
  }
1148
+ function rangeChars(r) {
1149
+ return r.chars ?? r.tokens * 4;
1150
+ }
1115
1151
  function mergeRangesToThreshold(ranges, minChars) {
1116
1152
  if (minChars <= 0 || ranges.length === 0) return ranges;
1117
1153
  const result = [];
1118
1154
  let batch = [];
1155
+ let batchChars = 0;
1119
1156
  for (const r of ranges) {
1120
1157
  batch.push(r);
1121
- const batchTokens = batch.reduce((s, x) => s + x.tokens, 0);
1122
- if (batchTokens * 4 >= minChars) {
1158
+ batchChars += rangeChars(r);
1159
+ if (batchChars >= minChars) {
1123
1160
  result.push(mergeBatch(batch));
1124
1161
  batch = [];
1162
+ batchChars = 0;
1125
1163
  }
1126
1164
  }
1127
1165
  if (batch.length > 0) {
@@ -1716,7 +1754,7 @@ function resolveAdaptiveGrowth(modelContextLimit, nudge) {
1716
1754
  function pendingByTier(state, recommendation, countTokens, minCompressRange) {
1717
1755
  const out = {};
1718
1756
  const merged = recommendation?.recommendedRanges ?? [];
1719
- const effective = minCompressRange > 0 ? merged.filter((r) => r.tokens * 4 >= minCompressRange) : merged;
1757
+ const effective = minCompressRange > 0 ? merged.filter((r) => (r.chars ?? r.tokens * 4) >= minCompressRange) : merged;
1720
1758
  out[1] = { pending: effective.reduce((s, r) => s + r.tokens, 0), targetBlocks: [] };
1721
1759
  const active = activeBlocks(state);
1722
1760
  const t1 = active.filter((b) => b.tier === 1);
@@ -1878,6 +1916,7 @@ function cloneState(state) {
1878
1916
  byRaw: { ...state.messageRefs.byRaw },
1879
1917
  byRef: { ...state.messageRefs.byRef }
1880
1918
  },
1919
+ tokenSnapshot: { ...state.tokenSnapshot ?? {} },
1881
1920
  nudge: { ...state.nudge, anchors: { ...state.nudge.anchors } },
1882
1921
  stats: { ...state.stats },
1883
1922
  nextBlockId: state.nextBlockId,
@@ -2661,6 +2700,11 @@ function extractRanges(input, callId) {
2661
2700
  return ranges;
2662
2701
  }
2663
2702
 
2703
+ // src/transform-channel.ts
2704
+ function resolveTransformChannel(explicit, wireViable) {
2705
+ return explicit ?? (wireViable ? "wire" : "message");
2706
+ }
2707
+
2664
2708
  // src/search/algorithms/substring.ts
2665
2709
  var substringAlgorithm = {
2666
2710
  name: "substring",
@@ -3004,6 +3048,7 @@ export {
3004
3048
  renderVisibleRefs,
3005
3049
  resolveBoundaries,
3006
3050
  resolvePrompts,
3051
+ resolveTransformChannel,
3007
3052
  runPipeline,
3008
3053
  searchBlocks,
3009
3054
  searchBlocksAsync,