acp-kernel 0.0.42 → 0.0.43

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/DESIGN.md CHANGED
@@ -201,6 +201,7 @@ A default `countTokens` ships with the core (word-level + unicode CJK tokenizer,
201
201
  | prune (range → summary block) | **core** `processTurn` (pure) |
202
202
  | boundary resolution / search | **core** `resolveBoundaries` (pure) |
203
203
  | block allocation / state mutation / tiers | **core** `applyCompression` (pure) |
204
+ | compress **argument parsing** (lenient: fences, trailing commas, truncated-array salvage; field-name variants) | **core** `parseCompressArgs` (pure; diagnostics are data — adapters emit them) |
204
205
  | young→old promotion / batch merge | **core** `sync-blocks` node (`advanceSurvival`) + `merge-blocks` node (pure) |
205
206
  | emergency truncation (context near full) | **core** `emergency-truncate` node — the LAST safety valve; no age-based GC |
206
207
  | protected-tools filtering logic | **core** (pure: message + config → bool) |
package/dist/index.d.ts CHANGED
@@ -25,6 +25,8 @@ export { hideConsumedCompressCalls } from "./hide-consumed.js";
25
25
  export type { HideConsumedResult } from "./hide-consumed.js";
26
26
  export { rebuildCompressionState } from "./rebuild.js";
27
27
  export type { RebuildResult, RebuildPorts } from "./rebuild.js";
28
+ export { parseCompressArgs } from "./parse-compress-input.js";
29
+ export type { CompressParseDiagnostics, CompressParseKind, ParsedCompressInput } from "./parse-compress-input.js";
28
30
  export { renderVisibleRefs, renderRefsNode, createRenderRefsNode, } from "./render-refs.js";
29
31
  export type { RenderStrategy } from "./render-refs.js";
30
32
  export { resolveTransformChannel } from "./transform-channel.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
@@ -1336,7 +1336,7 @@ function createCore(ports = {}) {
1336
1336
  if (!hasBlockBoundaryRange && totalRangeChars < input.config.compress.minCompressRange) {
1337
1337
  const live = activeBlocks(state).map((b) => b.blockId).sort((x, y) => numericBlockId(x) - numericBlockId(y));
1338
1338
  const liveHint = live.length > 0 ? ` Current active blocks span ${live[0]}..${live[live.length - 1]} \u2014 retry with startId/endId set to active block IDs in that span.` : "";
1339
- const gateMessage = resolvableCount === 0 && consumedRanges.length === 0 && unknownCount > 0 ? `None of the ${input.ranges.length} requested range(s) resolved \u2014 every ref failed with "does not exist in this session". Refs recorded before an earlier compress are stale: each successful compress renumbers the remaining refs. Run acp_status, then re-issue the compress in the same turn using only the refs it reports.` : consumedRanges.length > 0 ? `Requested range(s) already compressed (e.g. ${consumedRanges[0].startRef}..${consumedRanges[0].endRef}); remaining compressible content ${totalRangeChars} chars < min ${input.config.compress.minCompressRange}. Nothing to do.${liveHint}` : `Total compressible content too small (${totalRangeChars} chars across ${countedRanges} range(s), min ${input.config.compress.minCompressRange}). Combine more messages into your range(s) to meet the threshold.`;
1339
+ const gateMessage = resolvableCount === 0 && consumedRanges.length === 0 && unknownCount > 0 ? `None of the ${input.ranges.length} requested range(s) resolved \u2014 every ref failed with "does not exist in this session". Refs recorded before an earlier compress are stale: each successful compress renumbers the remaining refs. Run acp_status, then call the compress tool again using only the refs it reports.` : consumedRanges.length > 0 ? `Requested range(s) already compressed (e.g. ${consumedRanges[0].startRef}..${consumedRanges[0].endRef}) \u2014 your refs are stale: a prior compress renumbered the remaining refs, so this range now falls inside an active block. Run acp_status, then call the compress tool again using only the CURRENT compressible ranges it reports.${liveHint}` : `Total compressible content too small (${totalRangeChars} chars across ${countedRanges} range(s), min ${input.config.compress.minCompressRange}). Combine more messages into your range(s) to meet the threshold.`;
1340
1340
  return {
1341
1341
  state: input.state,
1342
1342
  result: {
@@ -2776,6 +2776,298 @@ Call with blockId to get the full summary.`);
2776
2776
  return lines.join("\n");
2777
2777
  }
2778
2778
 
2779
+ // src/parse-compress-input.ts
2780
+ function parseCompressArgs(input, opts) {
2781
+ const callId = opts?.callId;
2782
+ const diag = { ok: false, kind: "ok", invalidItems: 0 };
2783
+ if (input === null || input === void 0) {
2784
+ diag.kind = "empty-input";
2785
+ return finish([], diag);
2786
+ }
2787
+ if (typeof input === "string") {
2788
+ return parseStringInput(input, callId, diag);
2789
+ }
2790
+ if (typeof input !== "object" || Array.isArray(input)) {
2791
+ diag.kind = "not-object";
2792
+ return finish([], diag);
2793
+ }
2794
+ return parseObjectValue(input, callId, diag);
2795
+ }
2796
+ function parseStringInput(raw, callId, diag) {
2797
+ diag.rawPrefix = raw.slice(0, 800);
2798
+ diag.length = raw.length;
2799
+ const cleaned = stripFence(raw.trim());
2800
+ if (cleaned === "") {
2801
+ diag.kind = "empty-input";
2802
+ return finish([], diag);
2803
+ }
2804
+ let value = tryParseLenient(cleaned);
2805
+ if (typeof value === "string") {
2806
+ const inner = tryParseLenient(stripFence(value));
2807
+ if (inner !== void 0) value = inner;
2808
+ }
2809
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
2810
+ return parseObjectValue(value, callId, diag);
2811
+ }
2812
+ if (value !== void 0) {
2813
+ diag.kind = "not-object";
2814
+ return finish([], diag);
2815
+ }
2816
+ const entries = salvageContentEntries(cleaned);
2817
+ return finishSalvage(entries, callId, diag, looksTruncated(cleaned));
2818
+ }
2819
+ function parseObjectValue(value, callId, diag) {
2820
+ diag.keys = Object.keys(value);
2821
+ const content = value["content"];
2822
+ if (content === void 0) {
2823
+ const single = validateEntry(value, callId);
2824
+ if (single) {
2825
+ diag.kind = "ok";
2826
+ return finish([single], diag);
2827
+ }
2828
+ diag.kind = "missing-content";
2829
+ return finish([], diag);
2830
+ }
2831
+ let entries;
2832
+ let salvaged = false;
2833
+ if (Array.isArray(content)) {
2834
+ entries = content;
2835
+ } else if (typeof content === "string") {
2836
+ const parsed = parseContentArray(content);
2837
+ if (parsed === null) {
2838
+ diag.kind = "content-not-array";
2839
+ return finish([], diag);
2840
+ }
2841
+ entries = parsed.entries;
2842
+ salvaged = parsed.salvaged;
2843
+ } else {
2844
+ diag.kind = "content-not-array";
2845
+ return finish([], diag);
2846
+ }
2847
+ const { ranges, invalid } = validateEntries(entries, callId);
2848
+ const topTopic = stringOr(value["topic"]);
2849
+ const topMaxChars = value["summaryMaxChars"];
2850
+ const hasTopMaxChars = typeof topMaxChars === "number" && Number.isFinite(topMaxChars);
2851
+ if (topTopic !== void 0 || hasTopMaxChars) {
2852
+ for (const r of ranges) {
2853
+ if (r.topic === void 0 && topTopic !== void 0) r.topic = topTopic;
2854
+ if (r.summaryMaxChars === void 0 && hasTopMaxChars) r.summaryMaxChars = topMaxChars;
2855
+ }
2856
+ }
2857
+ diag.invalidItems = invalid;
2858
+ diag.kind = salvaged ? "truncated" : ranges.length > 0 ? "ok" : "no-valid-ranges";
2859
+ return finish(ranges, diag);
2860
+ }
2861
+ function parseContentArray(s) {
2862
+ const cleaned = stripFence(s.trim());
2863
+ let value = cleaned === "" ? void 0 : tryParseLenient(cleaned);
2864
+ if (typeof value === "string") {
2865
+ value = tryParseLenient(stripFence(value));
2866
+ }
2867
+ if (Array.isArray(value)) {
2868
+ return { entries: value, salvaged: false };
2869
+ }
2870
+ if (value === void 0) {
2871
+ const entries = salvageContentEntries('{"content": ' + cleaned);
2872
+ return { entries, salvaged: entries.length > 0 };
2873
+ }
2874
+ return null;
2875
+ }
2876
+ function finish(ranges, diag) {
2877
+ diag.ok = ranges.length > 0;
2878
+ return { ranges, diagnostics: diag };
2879
+ }
2880
+ function finishSalvage(entries, callId, diag, truncatedShape) {
2881
+ const { ranges, invalid } = validateEntries(entries, callId);
2882
+ diag.invalidItems = invalid;
2883
+ diag.kind = entries.length > 0 || truncatedShape ? "truncated" : "malformed-json";
2884
+ return finish(ranges, diag);
2885
+ }
2886
+ function validateEntries(entries, callId) {
2887
+ const ranges = [];
2888
+ let invalid = 0;
2889
+ for (const entry of entries) {
2890
+ const range = validateEntry(entry, callId);
2891
+ if (range !== null) ranges.push(range);
2892
+ else invalid++;
2893
+ }
2894
+ return { ranges, invalid };
2895
+ }
2896
+ function validateEntry(entry, callId) {
2897
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) return null;
2898
+ const e = entry;
2899
+ const start = stringOr(e["startRef"]) ?? stringOr(e["startId"]) ?? stringOr(e["messageId"]);
2900
+ const end = stringOr(e["endRef"]) ?? stringOr(e["endId"]) ?? stringOr(e["messageId"]);
2901
+ if (start === void 0 || end === void 0) return null;
2902
+ const summary = stringOr(e["summary"]);
2903
+ if (summary === void 0) return null;
2904
+ const range = { startRef: start, endRef: end, summary };
2905
+ const topic = stringOr(e["topic"]);
2906
+ if (topic !== void 0) range.topic = topic;
2907
+ const maxChars = e["summaryMaxChars"];
2908
+ if (typeof maxChars === "number" && Number.isFinite(maxChars)) range.summaryMaxChars = maxChars;
2909
+ if (callId !== void 0) range.compressCallId = callId;
2910
+ return range;
2911
+ }
2912
+ function stringOr(value) {
2913
+ return typeof value === "string" && value.length > 0 ? value : void 0;
2914
+ }
2915
+ function tryParseLenient(s) {
2916
+ if (s === "") return void 0;
2917
+ try {
2918
+ return JSON.parse(s);
2919
+ } catch {
2920
+ }
2921
+ const noTrailingCommas = stripTrailingCommas(s);
2922
+ if (noTrailingCommas !== s) {
2923
+ try {
2924
+ return JSON.parse(noTrailingCommas);
2925
+ } catch {
2926
+ }
2927
+ }
2928
+ const fixed = escapeRawNewlinesInStrings(noTrailingCommas);
2929
+ if (fixed !== noTrailingCommas) {
2930
+ try {
2931
+ return JSON.parse(fixed);
2932
+ } catch {
2933
+ }
2934
+ }
2935
+ return void 0;
2936
+ }
2937
+ function stripFence(s) {
2938
+ if (!s.startsWith("```")) return s;
2939
+ const firstNewline = s.indexOf("\n");
2940
+ if (firstNewline === -1) return s;
2941
+ const bodyStart = firstNewline + 1;
2942
+ const end = s.lastIndexOf("```");
2943
+ return end > bodyStart ? s.slice(bodyStart, end).trim() : s.slice(bodyStart).trim();
2944
+ }
2945
+ function stripTrailingCommas(s) {
2946
+ let out = "";
2947
+ let inString = false;
2948
+ let escaped = false;
2949
+ for (let i = 0; i < s.length; i++) {
2950
+ const ch = s.charAt(i);
2951
+ if (inString) {
2952
+ out += ch;
2953
+ if (escaped) escaped = false;
2954
+ else if (ch === "\\") escaped = true;
2955
+ else if (ch === '"') inString = false;
2956
+ continue;
2957
+ }
2958
+ if (ch === '"') {
2959
+ inString = true;
2960
+ out += ch;
2961
+ continue;
2962
+ }
2963
+ if (ch === ",") {
2964
+ let j = i + 1;
2965
+ while (j < s.length && (s.charAt(j) === " " || s.charAt(j) === " " || s.charAt(j) === "\n" || s.charAt(j) === "\r")) j++;
2966
+ if (j < s.length && (s.charAt(j) === "}" || s.charAt(j) === "]")) continue;
2967
+ }
2968
+ out += ch;
2969
+ }
2970
+ return out;
2971
+ }
2972
+ function escapeRawNewlinesInStrings(s) {
2973
+ let out = "";
2974
+ let inString = false;
2975
+ let escaped = false;
2976
+ for (let i = 0; i < s.length; i++) {
2977
+ const ch = s.charAt(i);
2978
+ if (!inString) {
2979
+ if (ch === '"') inString = true;
2980
+ out += ch;
2981
+ continue;
2982
+ }
2983
+ if (escaped) {
2984
+ out += ch;
2985
+ escaped = false;
2986
+ continue;
2987
+ }
2988
+ if (ch === "\\") {
2989
+ out += ch;
2990
+ escaped = true;
2991
+ continue;
2992
+ }
2993
+ if (ch === "\n") {
2994
+ out += "\\n";
2995
+ continue;
2996
+ }
2997
+ if (ch === "\r") {
2998
+ out += "\\r";
2999
+ continue;
3000
+ }
3001
+ if (ch === " ") {
3002
+ out += "\\t";
3003
+ continue;
3004
+ }
3005
+ if (ch === '"') inString = false;
3006
+ out += ch;
3007
+ }
3008
+ return out;
3009
+ }
3010
+ function looksTruncated(s) {
3011
+ let depth = 0;
3012
+ let inString = false;
3013
+ let escaped = false;
3014
+ for (let i = 0; i < s.length; i++) {
3015
+ const ch = s.charAt(i);
3016
+ if (inString) {
3017
+ if (escaped) escaped = false;
3018
+ else if (ch === "\\") escaped = true;
3019
+ else if (ch === '"') inString = false;
3020
+ continue;
3021
+ }
3022
+ if (ch === '"') {
3023
+ inString = true;
3024
+ continue;
3025
+ }
3026
+ if (ch === "{" || ch === "[") depth++;
3027
+ else if (ch === "}" || ch === "]") depth--;
3028
+ }
3029
+ return depth > 0 || inString;
3030
+ }
3031
+ function salvageContentEntries(raw) {
3032
+ const match = /"content"\s*:\s*\[/.exec(raw);
3033
+ if (match === null) return [];
3034
+ const arrayStart = match.index + match[0].length - 1;
3035
+ const entries = [];
3036
+ let depth = 0;
3037
+ let inString = false;
3038
+ let escaped = false;
3039
+ let entryStart = -1;
3040
+ for (let i = arrayStart + 1; i < raw.length; i++) {
3041
+ const ch = raw.charAt(i);
3042
+ if (inString) {
3043
+ if (escaped) escaped = false;
3044
+ else if (ch === "\\") escaped = true;
3045
+ else if (ch === '"') inString = false;
3046
+ continue;
3047
+ }
3048
+ if (ch === '"') {
3049
+ inString = true;
3050
+ continue;
3051
+ }
3052
+ if (ch === "{" || ch === "[") {
3053
+ if (depth === 0 && ch === "{" && entryStart === -1) entryStart = i;
3054
+ depth++;
3055
+ continue;
3056
+ }
3057
+ if (ch === "}" || ch === "]") {
3058
+ depth--;
3059
+ if (depth < 0) break;
3060
+ if (depth === 0 && entryStart !== -1) {
3061
+ const entrySlice = raw.slice(entryStart, i + 1);
3062
+ entryStart = -1;
3063
+ const parsed = tryParseLenient(entrySlice);
3064
+ if (parsed !== void 0) entries.push(parsed);
3065
+ }
3066
+ }
3067
+ }
3068
+ return entries;
3069
+ }
3070
+
2779
3071
  // src/rebuild.ts
2780
3072
  function rebuildCompressionState(state, messages, config, ports = {}) {
2781
3073
  const core = createCore({ countTokens: ports.countTokens ?? defaultCountTokens });
@@ -2787,7 +3079,7 @@ function rebuildCompressionState(state, messages, config, ports = {}) {
2787
3079
  const invocations = collectCompressInvocations(messages);
2788
3080
  let blocksRebuilt = 0;
2789
3081
  for (const invocation of invocations) {
2790
- const ranges = extractRanges(invocation.input, invocation.callId);
3082
+ const { ranges } = parseCompressArgs(invocation.raw, { callId: invocation.callId });
2791
3083
  if (ranges.length === 0) continue;
2792
3084
  const result = core.applyCompression({ ranges, messages, state: working, config });
2793
3085
  working = result.state;
@@ -2799,37 +3091,10 @@ function collectCompressInvocations(messages) {
2799
3091
  const invocations = [];
2800
3092
  for (const message of messages) {
2801
3093
  if (message.toolName !== "compress" || message.contentType !== "tool-call") continue;
2802
- let input;
2803
- try {
2804
- input = JSON.parse(message.text ?? "");
2805
- } catch {
2806
- continue;
2807
- }
2808
- invocations.push({ callId: message.toolCallId, input });
3094
+ invocations.push({ callId: message.toolCallId, raw: message.text ?? "" });
2809
3095
  }
2810
3096
  return invocations;
2811
3097
  }
2812
- function extractRanges(input, callId) {
2813
- const content = input?.content;
2814
- if (!Array.isArray(content)) return [];
2815
- const ranges = [];
2816
- for (const entry of content) {
2817
- if (!entry || typeof entry !== "object") continue;
2818
- const e = entry;
2819
- if (typeof e.summary !== "string") continue;
2820
- const start = e.startId ?? e.messageId;
2821
- const end = e.endId ?? e.messageId;
2822
- if (typeof start !== "string" || typeof end !== "string") continue;
2823
- ranges.push({
2824
- startRef: start,
2825
- endRef: end,
2826
- summary: e.summary,
2827
- topic: typeof e.topic === "string" ? e.topic : void 0,
2828
- compressCallId: callId
2829
- });
2830
- }
2831
- return ranges;
2832
- }
2833
3098
 
2834
3099
  // src/transform-channel.ts
2835
3100
  function resolveTransformChannel(explicit, wireViable) {
@@ -3264,6 +3529,7 @@ export {
3264
3529
  messageDocs,
3265
3530
  parseBlockIdArg,
3266
3531
  parseBoundary,
3532
+ parseCompressArgs,
3267
3533
  parseCompressInput,
3268
3534
  prune,
3269
3535
  rawForRef,