billion-context-omp 0.2.6 → 0.2.8

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/dist/index.js CHANGED
@@ -70,6 +70,7 @@ function createInitialState() {
70
70
  return {
71
71
  blocks: [],
72
72
  messageRefs: { byRaw: {}, byRef: {} },
73
+ tokenSnapshot: {},
73
74
  nudge: {
74
75
  lastPerMessageNudgeTokens: 0,
75
76
  lastNudgeShownTokens: 0,
@@ -239,11 +240,23 @@ function syncBlocks(messages, state) {
239
240
  byRaw: { ...state.messageRefs.byRaw },
240
241
  byRef: { ...state.messageRefs.byRef }
241
242
  },
243
+ // Snapshot is keyed by ref with primitive values — shallow copy suffices.
244
+ tokenSnapshot: { ...state.tokenSnapshot ?? {} },
242
245
  nudge: { ...state.nudge, anchors: { ...state.nudge.anchors } },
243
246
  stats: { ...state.stats },
244
247
  nextBlockId: state.nextBlockId,
245
248
  nextRunId: state.nextRunId
246
249
  };
250
+ const liveRefs = new Set(
251
+ messages.map((m) => result.messageRefs.byRaw[m.id]).filter((r) => typeof r === "string")
252
+ );
253
+ if (Object.keys(result.tokenSnapshot).length !== liveRefs.size) {
254
+ const pruned = {};
255
+ for (const [ref, n] of Object.entries(result.tokenSnapshot)) {
256
+ if (liveRefs.has(ref)) pruned[ref] = n;
257
+ }
258
+ result.tokenSnapshot = pruned;
259
+ }
247
260
  const consumedBlockIds = /* @__PURE__ */ new Set();
248
261
  for (const block of result.blocks) {
249
262
  for (const consumedId of block.directBlockIds) {
@@ -718,7 +731,7 @@ var TAG_CLOSE = LT + "/acp" + GT;
718
731
  function acpTag(ref, tokens, type5) {
719
732
  return TAG_OPEN + 'tokens="' + formatTokens(tokens) + '" type="' + type5 + '"' + GT + ref + TAG_CLOSE;
720
733
  }
721
- function renderMessage(message, map, countTokens, strategy) {
734
+ function renderMessage(message, map, countTokens, strategy, snapshot = null) {
722
735
  const ref = refForRaw(map, message.id);
723
736
  if (!ref || ref === BLOCKED_REF) return message;
724
737
  if (strategy === "none") return message;
@@ -729,26 +742,33 @@ function renderMessage(message, map, countTokens, strategy) {
729
742
  "^" + escapeRegex(TAG_OPEN) + "[^>]*" + GT + escapeRegex(ref) + escapeRegex(TAG_CLOSE) + "\\n?"
730
743
  );
731
744
  const cleanText = (message.text || "").replace(ownTagRe, "");
732
- const tokens = countTokens(cleanText);
745
+ const tokens = snapshot ? snapshot[ref] ?? (snapshot[ref] = countTokens(cleanText)) : countTokens(cleanText);
733
746
  const type5 = classifyType(message);
734
747
  const prefix = acpTag(ref, tokens, type5) + "\n";
735
748
  if (!cleanText) return { ...message, text: prefix };
736
749
  return { ...message, text: prefix + cleanText };
737
750
  }
738
- function renderVisibleRefs(messages, state, countTokens = (text) => Math.ceil(text.length / 4), strategy = "all") {
751
+ function renderWithSnapshot(messages, state, countTokens = (text) => Math.ceil(text.length / 4), strategy = "all") {
739
752
  const map = state.messageRefs;
740
- return messages.map(
741
- (message) => renderMessage(message, map, countTokens, strategy)
753
+ const snapshot = { ...state.tokenSnapshot ?? {} };
754
+ const rendered = messages.map(
755
+ (message) => renderMessage(message, map, countTokens, strategy, snapshot)
742
756
  );
757
+ return { messages: rendered, tokenSnapshot: snapshot };
743
758
  }
744
759
  function createRenderRefsNode(strategy) {
745
760
  return {
746
761
  name: "render-refs",
747
762
  run(io, ctx) {
748
- return {
749
- ...io,
750
- messages: renderVisibleRefs(io.messages, io.state, ctx.countTokens, strategy)
751
- };
763
+ const { messages, tokenSnapshot } = renderWithSnapshot(
764
+ io.messages,
765
+ io.state,
766
+ ctx.countTokens,
767
+ strategy
768
+ );
769
+ const prev = io.state.tokenSnapshot;
770
+ const changed = !prev || Object.keys(tokenSnapshot).length !== Object.keys(prev).length;
771
+ return changed ? { ...io, messages, state: { ...io.state, tokenSnapshot } } : { ...io, messages };
752
772
  }
753
773
  };
754
774
  }
@@ -944,6 +964,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
944
964
  ref,
945
965
  refNum: rn,
946
966
  tokens: countTokens(msg.text ?? ""),
967
+ chars: (msg.text ?? "").length,
947
968
  isTool: isToolMessage(msg),
948
969
  isUser: msg.role === "user"
949
970
  });
@@ -964,6 +985,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
964
985
  endRef: info.ref,
965
986
  count: 1,
966
987
  tokens: info.tokens,
988
+ chars: info.chars,
967
989
  toolPct: info.isTool ? 100 : 0,
968
990
  textPct: info.isTool ? 0 : 100
969
991
  };
@@ -971,6 +993,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
971
993
  cur.endRef = info.ref;
972
994
  cur.count++;
973
995
  cur.tokens += info.tokens;
996
+ cur.chars = (cur.chars ?? 0) + info.chars;
974
997
  if (info.isTool) {
975
998
  cur.toolPct = Math.round((cur.toolPct * (cur.count - 1) + 100) / cur.count);
976
999
  } else {
@@ -1018,6 +1041,7 @@ function mergeBatch(batch) {
1018
1041
  const last = batch[batch.length - 1];
1019
1042
  const count = batch.reduce((s, r) => s + r.count, 0);
1020
1043
  const tokens = batch.reduce((s, r) => s + r.tokens, 0);
1044
+ const chars = batch.reduce((s, r) => s + rangeChars(r), 0);
1021
1045
  const toolPct = Math.round(
1022
1046
  batch.reduce((s, r) => s + r.toolPct * r.count, 0) / count
1023
1047
  );
@@ -1026,6 +1050,7 @@ function mergeBatch(batch) {
1026
1050
  endRef: last.endRef,
1027
1051
  count,
1028
1052
  tokens,
1053
+ chars,
1029
1054
  toolPct,
1030
1055
  textPct: 100 - toolPct
1031
1056
  };
@@ -1034,16 +1059,21 @@ function mergeBatch(batch) {
1034
1059
  }
1035
1060
  return merged;
1036
1061
  }
1062
+ function rangeChars(r) {
1063
+ return r.chars ?? r.tokens * 4;
1064
+ }
1037
1065
  function mergeRangesToThreshold(ranges, minChars) {
1038
1066
  if (minChars <= 0 || ranges.length === 0) return ranges;
1039
1067
  const result = [];
1040
1068
  let batch = [];
1069
+ let batchChars = 0;
1041
1070
  for (const r of ranges) {
1042
1071
  batch.push(r);
1043
- const batchTokens = batch.reduce((s, x) => s + x.tokens, 0);
1044
- if (batchTokens * 4 >= minChars) {
1072
+ batchChars += rangeChars(r);
1073
+ if (batchChars >= minChars) {
1045
1074
  result.push(mergeBatch(batch));
1046
1075
  batch = [];
1076
+ batchChars = 0;
1047
1077
  }
1048
1078
  }
1049
1079
  if (batch.length > 0) {
@@ -1631,7 +1661,7 @@ function resolveAdaptiveGrowth(modelContextLimit, nudge) {
1631
1661
  function pendingByTier(state, recommendation, countTokens, minCompressRange) {
1632
1662
  const out = {};
1633
1663
  const merged = recommendation?.recommendedRanges ?? [];
1634
- const effective = minCompressRange > 0 ? merged.filter((r) => r.tokens * 4 >= minCompressRange) : merged;
1664
+ const effective = minCompressRange > 0 ? merged.filter((r) => (r.chars ?? r.tokens * 4) >= minCompressRange) : merged;
1635
1665
  out[1] = { pending: effective.reduce((s, r) => s + r.tokens, 0), targetBlocks: [] };
1636
1666
  const active = activeBlocks(state);
1637
1667
  const t1 = active.filter((b) => b.tier === 1);
@@ -1793,6 +1823,7 @@ function cloneState(state) {
1793
1823
  byRaw: { ...state.messageRefs.byRaw },
1794
1824
  byRef: { ...state.messageRefs.byRef }
1795
1825
  },
1826
+ tokenSnapshot: { ...state.tokenSnapshot ?? {} },
1796
1827
  nudge: { ...state.nudge, anchors: { ...state.nudge.anchors } },
1797
1828
  stats: { ...state.stats },
1798
1829
  nextBlockId: state.nextBlockId,
@@ -2757,8 +2788,12 @@ function resolveConfig(adapter, liveContextLimit) {
2757
2788
  if (c?.maxContextLimit !== void 0) config.nudge.maxContextLimitPct = parsePercent(c.maxContextLimit);
2758
2789
  if (c?.emergencyThresholdPercent !== void 0) {
2759
2790
  const pct2 = parsePercent(c.emergencyThresholdPercent);
2760
- config.nudge.emergencyThresholdPct = pct2;
2761
- config.truncate.threshold = pct2;
2791
+ if (pct2 <= 0) {
2792
+ logWarn("config", { event: "emergency-threshold-ignored", value: String(c.emergencyThresholdPercent), reason: "zero threshold would truncate every turn" });
2793
+ } else {
2794
+ config.nudge.emergencyThresholdPct = pct2;
2795
+ config.truncate.threshold = pct2;
2796
+ }
2762
2797
  }
2763
2798
  if (c?.nudgeGrowthTokens !== void 0) {
2764
2799
  config.nudge.growthFloor = c.nudgeGrowthTokens;
@@ -2863,8 +2898,396 @@ When context usage passes a threshold, the system appends a breakdown showing wh
2863
2898
  `;
2864
2899
  }
2865
2900
 
2866
- // src/messages.ts
2901
+ // src/transform-mode.ts
2902
+ import { VERSION } from "@oh-my-pi/pi-utils";
2903
+ var PROVIDER_VIABLE_APIS = /* @__PURE__ */ new Set(["anthropic-messages", "ollama-chat"]);
2904
+ var OPENAI_COMPLETIONS_VIABLE_FROM = [17, 3, 8];
2905
+ function hostVersionAtLeast(min, version = VERSION) {
2906
+ const m = /^(\d+)\.(\d+)\.(\d+)/.exec(version ?? "");
2907
+ if (!m) return false;
2908
+ const v = [Number(m[1]), Number(m[2]), Number(m[3])];
2909
+ return v[0] > min[0] || v[0] === min[0] && (v[1] > min[1] || v[1] === min[1] && v[2] >= min[2]);
2910
+ }
2911
+ function resolveTransformMode(adapter, model, hostVersion = VERSION) {
2912
+ if (adapter.transformMode) return adapter.transformMode;
2913
+ const api = model?.api;
2914
+ if (api == null) return "context";
2915
+ if (PROVIDER_VIABLE_APIS.has(api)) return "provider";
2916
+ if (api === "openai-completions" && hostVersionAtLeast(OPENAI_COMPLETIONS_VIABLE_FROM, hostVersion)) return "provider";
2917
+ return "context";
2918
+ }
2919
+
2920
+ // node_modules/acp-kernel/dist/wire/index.js
2867
2921
  import { createHash } from "crypto";
2922
+ function hashId(s) {
2923
+ return createHash("sha256").update(s, "utf8").digest("hex").slice(0, 16);
2924
+ }
2925
+ function deriveMessageId(role, contentType, text, options = {}) {
2926
+ const seed = `${role}|${contentType}|${options.toolCallId ?? ""}|${options.toolName ?? ""}|${text}`;
2927
+ return "h_" + hashId(seed);
2928
+ }
2929
+ var ClusterCounter = class {
2930
+ counts = /* @__PURE__ */ new Map();
2931
+ next(baseId) {
2932
+ const n = this.counts.get(baseId) ?? 0;
2933
+ this.counts.set(baseId, n + 1);
2934
+ return n === 0 ? baseId : `${baseId}_${n}`;
2935
+ }
2936
+ };
2937
+ function anthropicToCore(body) {
2938
+ const msgs = [];
2939
+ const cacheControls = /* @__PURE__ */ new Map();
2940
+ const clusters = new ClusterCounter();
2941
+ for (const m of body.messages) {
2942
+ const blocks = typeof m.content === "string" ? [{ type: "text", text: m.content }] : m.content;
2943
+ for (const b of blocks) {
2944
+ switch (b.type) {
2945
+ case "text": {
2946
+ const base = deriveMessageId(m.role, "text", b.text);
2947
+ const id = clusters.next(base);
2948
+ msgs.push({ id, role: m.role, contentType: "text", text: b.text });
2949
+ if (b.cache_control) cacheControls.set(id, b.cache_control);
2950
+ break;
2951
+ }
2952
+ case "tool_use": {
2953
+ const base = deriveMessageId("assistant", "tool-call", safeStringify(b.input), {
2954
+ toolCallId: b.id,
2955
+ toolName: b.name
2956
+ });
2957
+ const id = clusters.next(base);
2958
+ msgs.push({
2959
+ id,
2960
+ role: "assistant",
2961
+ contentType: "tool-call",
2962
+ toolName: b.name,
2963
+ toolCallId: b.id,
2964
+ text: safeStringify(b.input)
2965
+ });
2966
+ if (b.cache_control) cacheControls.set(id, b.cache_control);
2967
+ break;
2968
+ }
2969
+ case "tool_result": {
2970
+ const text = typeof b.content === "string" ? b.content : b.content.map((c) => c.text).join("\n");
2971
+ const base = deriveMessageId("tool", "tool-result", text, { toolCallId: b.tool_use_id });
2972
+ const id = clusters.next(base);
2973
+ msgs.push({
2974
+ id,
2975
+ role: "tool",
2976
+ contentType: "tool-result",
2977
+ toolCallId: b.tool_use_id,
2978
+ text,
2979
+ ...b.is_error === true ? { toolIsError: true } : {}
2980
+ });
2981
+ if (b.cache_control) cacheControls.set(id, b.cache_control);
2982
+ break;
2983
+ }
2984
+ case "thinking": {
2985
+ const base = deriveMessageId("assistant", "reasoning", b.thinking);
2986
+ msgs.push({
2987
+ id: clusters.next(base),
2988
+ role: "assistant",
2989
+ contentType: "reasoning",
2990
+ text: b.thinking,
2991
+ ...b.signature ? { thinkingSignature: b.signature } : {}
2992
+ });
2993
+ break;
2994
+ }
2995
+ case "image": {
2996
+ const base = deriveMessageId(m.role, "text", "[image]");
2997
+ msgs.push({
2998
+ id: clusters.next(base),
2999
+ role: m.role,
3000
+ contentType: "text",
3001
+ text: "[image]",
3002
+ rawAnthropicBlock: b
3003
+ });
3004
+ break;
3005
+ }
3006
+ }
3007
+ }
3008
+ }
3009
+ return { msgs, cacheControls };
3010
+ }
3011
+ function coreToAnthropic(messages, cacheControls) {
3012
+ const out = [];
3013
+ let current = null;
3014
+ const flush = () => {
3015
+ if (current && current.blocks.length > 0) {
3016
+ out.push({ role: current.role, content: current.blocks });
3017
+ }
3018
+ current = null;
3019
+ };
3020
+ const cc = (id) => {
3021
+ const v = cacheControls?.get(id);
3022
+ return v ? { cache_control: v } : {};
3023
+ };
3024
+ for (const m of messages) {
3025
+ const target = m.role === "assistant" ? "assistant" : "user";
3026
+ if (!current || current.role !== target) {
3027
+ flush();
3028
+ current = { role: target, blocks: [] };
3029
+ }
3030
+ switch (m.contentType) {
3031
+ case "text": {
3032
+ if (m.rawAnthropicBlock) {
3033
+ current.blocks.push(m.rawAnthropicBlock);
3034
+ break;
3035
+ }
3036
+ current.blocks.push({ type: "text", text: m.text ?? "", ...cc(m.id) });
3037
+ break;
3038
+ }
3039
+ case "tool-call":
3040
+ current.blocks.push({
3041
+ type: "tool_use",
3042
+ id: m.toolCallId ?? `call_${m.id}`,
3043
+ name: m.toolName ?? "unknown",
3044
+ input: safeParse(m.text),
3045
+ ...cc(m.id)
3046
+ });
3047
+ break;
3048
+ case "tool-result":
3049
+ current.blocks.push({
3050
+ type: "tool_result",
3051
+ tool_use_id: m.toolCallId ?? "",
3052
+ content: m.text ?? "",
3053
+ ...m.toolIsError ? { is_error: true } : {},
3054
+ ...cc(m.id)
3055
+ });
3056
+ break;
3057
+ case "reasoning":
3058
+ current.blocks.push({
3059
+ type: "thinking",
3060
+ thinking: m.text ?? "",
3061
+ ...m.thinkingSignature ? { signature: m.thinkingSignature } : {}
3062
+ });
3063
+ break;
3064
+ }
3065
+ }
3066
+ flush();
3067
+ return out;
3068
+ }
3069
+ function safeStringify(v) {
3070
+ try {
3071
+ return JSON.stringify(v ?? {});
3072
+ } catch {
3073
+ return "{}";
3074
+ }
3075
+ }
3076
+ function safeParse(s) {
3077
+ if (!s) return {};
3078
+ try {
3079
+ return JSON.parse(s);
3080
+ } catch {
3081
+ return {};
3082
+ }
3083
+ }
3084
+ function parseDataUrl(url) {
3085
+ const m = /^data:([^;,]+)(?:;base64)?,(.+)$/i.exec(url);
3086
+ if (!m) return void 0;
3087
+ return { mediaType: m[1], base64: m[2] };
3088
+ }
3089
+ function openaiToCore(body) {
3090
+ const msgs = [];
3091
+ const clusters = new ClusterCounter();
3092
+ for (const m of body.messages) {
3093
+ switch (m.role) {
3094
+ case "system":
3095
+ case "developer": {
3096
+ const base = deriveMessageId(m.role, "text", stringContent(m.content));
3097
+ msgs.push({ id: clusters.next(base), role: "system", contentType: "text", text: stringContent(m.content), originalRole: m.role });
3098
+ break;
3099
+ }
3100
+ case "user": {
3101
+ const text = stringContent(m.content);
3102
+ const img = firstImagePart(m.content);
3103
+ const base = deriveMessageId("user", "text", text);
3104
+ msgs.push({
3105
+ id: clusters.next(base),
3106
+ role: "user",
3107
+ contentType: "text",
3108
+ text,
3109
+ ...img ? { rawOpenaiContent: img.part, imageMediaType: img.mediaType, imageBase64: img.base64 } : {}
3110
+ });
3111
+ break;
3112
+ }
3113
+ case "assistant": {
3114
+ const reasoning = typeof m.reasoning_content === "string" ? m.reasoning_content : "";
3115
+ if (reasoning) {
3116
+ const base = deriveMessageId("assistant", "reasoning", reasoning);
3117
+ msgs.push({
3118
+ id: clusters.next(base),
3119
+ role: "assistant",
3120
+ contentType: "reasoning",
3121
+ text: reasoning,
3122
+ reasoningContent: reasoning
3123
+ });
3124
+ }
3125
+ const text = stringContent(m.content);
3126
+ if (text) {
3127
+ const base = deriveMessageId("assistant", "text", text);
3128
+ msgs.push({ id: clusters.next(base), role: "assistant", contentType: "text", text });
3129
+ }
3130
+ if (Array.isArray(m.tool_calls)) {
3131
+ for (const tc of m.tool_calls) {
3132
+ const base = deriveMessageId("assistant", "tool-call", tc.function.arguments ?? "", {
3133
+ toolCallId: tc.id,
3134
+ toolName: tc.function.name
3135
+ });
3136
+ msgs.push({
3137
+ id: clusters.next(base),
3138
+ role: "assistant",
3139
+ contentType: "tool-call",
3140
+ toolName: tc.function.name,
3141
+ toolCallId: tc.id,
3142
+ text: tc.function.arguments ?? ""
3143
+ });
3144
+ }
3145
+ }
3146
+ break;
3147
+ }
3148
+ case "tool": {
3149
+ const base = deriveMessageId("tool", "tool-result", stringContent(m.content), {
3150
+ toolCallId: m.tool_call_id ?? ""
3151
+ });
3152
+ msgs.push({
3153
+ id: clusters.next(base),
3154
+ role: "tool",
3155
+ contentType: "tool-result",
3156
+ toolCallId: m.tool_call_id ?? "",
3157
+ text: stringContent(m.content)
3158
+ });
3159
+ break;
3160
+ }
3161
+ }
3162
+ }
3163
+ return { msgs };
3164
+ }
3165
+ function coreToOpenai(messages) {
3166
+ const out = [];
3167
+ let pending = null;
3168
+ const flush = () => {
3169
+ if (!pending) return;
3170
+ const reasoning = pending.reasoning !== null && pending.reasoning.length > 0 ? pending.reasoning : void 0;
3171
+ if (pending.toolCalls.length > 0) {
3172
+ out.push({
3173
+ role: "assistant",
3174
+ content: pending.text ?? null,
3175
+ tool_calls: pending.toolCalls,
3176
+ ...reasoning ? { reasoning_content: reasoning } : {}
3177
+ });
3178
+ } else if (pending.text !== null) {
3179
+ out.push({ role: "assistant", content: pending.text, ...reasoning ? { reasoning_content: reasoning } : {} });
3180
+ } else if (reasoning) {
3181
+ out.push({ role: "assistant", content: null, reasoning_content: reasoning });
3182
+ }
3183
+ pending = null;
3184
+ };
3185
+ for (const m of messages) {
3186
+ if (m.role === "assistant") {
3187
+ if (!pending) pending = { text: null, toolCalls: [], reasoning: null };
3188
+ if (m.contentType === "reasoning") {
3189
+ pending.reasoning = (pending.reasoning ?? "") + (m.reasoningContent ?? m.text ?? "");
3190
+ } else if (m.contentType === "text") {
3191
+ pending.text = (pending.text ?? "") + (m.text ?? "");
3192
+ } else if (m.contentType === "tool-call") {
3193
+ pending.toolCalls.push({
3194
+ id: m.toolCallId ?? `call_${m.id}`,
3195
+ type: "function",
3196
+ function: { name: m.toolName ?? "unknown", arguments: m.text ?? "" }
3197
+ });
3198
+ }
3199
+ } else {
3200
+ flush();
3201
+ if (m.role === "system") {
3202
+ out.push({ role: m.originalRole === "developer" ? "developer" : "system", content: m.text ?? "" });
3203
+ } else if (m.role === "user") {
3204
+ if (m.rawOpenaiContent || m.imageBase64) {
3205
+ const parts = [];
3206
+ if (m.text) parts.push({ type: "text", text: m.text });
3207
+ if (m.rawOpenaiContent) {
3208
+ parts.push(m.rawOpenaiContent);
3209
+ } else if (m.imageBase64 && m.imageMediaType) {
3210
+ parts.push({ type: "image_url", image_url: { url: `data:${m.imageMediaType};base64,${m.imageBase64}` } });
3211
+ }
3212
+ out.push({ role: "user", content: parts });
3213
+ } else {
3214
+ out.push({ role: "user", content: m.text ?? "" });
3215
+ }
3216
+ } else if (m.role === "tool") {
3217
+ out.push({ role: "tool", tool_call_id: m.toolCallId ?? "", content: m.text ?? "" });
3218
+ }
3219
+ }
3220
+ }
3221
+ flush();
3222
+ return out;
3223
+ }
3224
+ function stringContent(content) {
3225
+ if (content == null) return "";
3226
+ if (typeof content === "string") return content;
3227
+ if (Array.isArray(content)) {
3228
+ return content.map((p) => typeof p === "string" ? p : p.type === "text" ? p.text ?? "" : "").join("\n");
3229
+ }
3230
+ return "";
3231
+ }
3232
+ function firstImagePart(content) {
3233
+ if (!Array.isArray(content)) return void 0;
3234
+ for (const p of content) {
3235
+ if (p && typeof p === "object" && p.type === "image_url") {
3236
+ const iu = p.image_url;
3237
+ const url = iu?.url;
3238
+ if (typeof url === "string") {
3239
+ const parsed = parseDataUrl(url);
3240
+ if (parsed) return { part: p, mediaType: parsed.mediaType, base64: parsed.base64 };
3241
+ }
3242
+ }
3243
+ }
3244
+ return void 0;
3245
+ }
3246
+ function createSubagentNamespaces() {
3247
+ const anchors = /* @__PURE__ */ new Map();
3248
+ return {
3249
+ namespaceFor(identityValue, instructions) {
3250
+ if (typeof instructions !== "string" || instructions.trim().length === 0) return identityValue;
3251
+ const fp = hashId(instructions);
3252
+ const anchor = anchors.get(identityValue);
3253
+ if (anchor === void 0) {
3254
+ anchors.set(identityValue, fp);
3255
+ return identityValue;
3256
+ }
3257
+ return anchor === fp ? identityValue : `${identityValue}|sub:${fp}`;
3258
+ }
3259
+ };
3260
+ }
3261
+ var defaultNamespaces = createSubagentNamespaces();
3262
+ function detectWireFormat(payload) {
3263
+ if (payload === null || typeof payload !== "object") return void 0;
3264
+ const p = payload;
3265
+ if (Array.isArray(p.input)) return "responses";
3266
+ const messages = p.messages;
3267
+ if (!Array.isArray(messages)) return void 0;
3268
+ if ("system" in p || "anthropic_version" in p) return "anthropic";
3269
+ for (const m of messages) {
3270
+ if (m === null || typeof m !== "object") continue;
3271
+ const c = m.content;
3272
+ if (Array.isArray(c)) {
3273
+ for (const b of c) {
3274
+ if (b && typeof b === "object" && typeof b.type === "string") {
3275
+ if (b.type === "tool_use" || b.type === "tool_result" || b.type === "thinking")
3276
+ return "anthropic";
3277
+ if (b.type === "text" && "cache_control" in b) return "anthropic";
3278
+ }
3279
+ }
3280
+ }
3281
+ if (Array.isArray(m.tool_calls)) return "openai";
3282
+ if (m.role === "tool" && typeof m.tool_call_id === "string")
3283
+ return "openai";
3284
+ if (m.role === "system" || m.role === "developer") return "openai";
3285
+ }
3286
+ return "openai";
3287
+ }
3288
+
3289
+ // src/messages.ts
3290
+ import { createHash as createHash2 } from "crypto";
2868
3291
  var REF_TAG_SOURCE = "(?:<acp\\s[^>]*>m\\d+</acp>|\\[m\\d+\\])";
2869
3292
  var REF_TAG = new RegExp(`^${REF_TAG_SOURCE}\\s?\\n?`);
2870
3293
  var TRAILING_REF_TAG = new RegExp(`\\n*${REF_TAG_SOURCE}\\s*$`);
@@ -3004,7 +3427,7 @@ function fallbackText(msg) {
3004
3427
  function stringifyArgs(args) {
3005
3428
  if (!args) return "";
3006
3429
  if (typeof args === "string") return args;
3007
- return safeStringify(args);
3430
+ return safeStringify2(args);
3008
3431
  }
3009
3432
  function extractText(content, stripTags = true) {
3010
3433
  const clean = stripTags ? stripRefTag : (s) => s;
@@ -3060,7 +3483,7 @@ function allToolCalls(content) {
3060
3483
  }
3061
3484
  return calls;
3062
3485
  }
3063
- function safeStringify(value) {
3486
+ function safeStringify2(value) {
3064
3487
  try {
3065
3488
  return JSON.stringify(value);
3066
3489
  } catch {
@@ -3218,7 +3641,7 @@ function spanFingerprint(coreMessages, startId, endId) {
3218
3641
  const first = find(startId);
3219
3642
  const last = find(endId);
3220
3643
  if (!first || !last) return "";
3221
- return createHash("sha1").update(`${key(first)}\0${key(last)}`).digest("hex").slice(0, 8);
3644
+ return createHash2("sha1").update(`${key(first)}\0${key(last)}`).digest("hex").slice(0, 8);
3222
3645
  }
3223
3646
  function isBlockRef(ref) {
3224
3647
  return /^b\d+$/i.test(ref.trim());
@@ -3247,249 +3670,241 @@ function rangeFingerprints(ranges, coreMessages, byRef, blocks) {
3247
3670
  });
3248
3671
  }
3249
3672
 
3250
- // src/wire-transform.ts
3251
- var AI = /* @__PURE__ */ Symbol("acp.streamIndex");
3252
- function detectWireFormat(payload) {
3253
- if (payload === null || typeof payload !== "object") return "unknown";
3254
- const p = payload;
3255
- const messages = p.messages;
3256
- if (!Array.isArray(messages)) return "unknown";
3257
- if ("system" in p || "anthropic_version" in p) return "anthropic";
3258
- for (const m of messages) {
3259
- if (m === null || typeof m !== "object") continue;
3260
- const c = m.content;
3261
- if (Array.isArray(c)) {
3262
- for (const b of c) {
3263
- if (b && typeof b === "object" && typeof b.type === "string") {
3264
- if (b.type === "tool_use" || b.type === "tool_result" || b.type === "thinking") return "anthropic";
3265
- if (b.type === "text" && "cache_control" in b) return "anthropic";
3266
- }
3267
- }
3268
- }
3269
- if (Array.isArray(m.tool_calls)) return "openai";
3270
- if (m.role === "tool" && typeof m.tool_call_id === "string") return "openai";
3271
- if (m.role === "system" || m.role === "developer") return "openai";
3673
+ // src/wire-fold.ts
3674
+ import { createHash as createHash3 } from "crypto";
3675
+ function detectProviderWireFormat(payload) {
3676
+ const fmt2 = detectWireFormat(payload);
3677
+ return fmt2 === "anthropic" || fmt2 === "openai" ? fmt2 : null;
3678
+ }
3679
+ function payloadToCore(payload, fmt2) {
3680
+ if (fmt2 === "anthropic") {
3681
+ const { msgs: msgs2, cacheControls } = anthropicToCore(payload);
3682
+ return { msgs: msgs2, cacheControls };
3683
+ }
3684
+ const { msgs } = openaiToCore(payload);
3685
+ return { msgs };
3686
+ }
3687
+ function coreToPayloadMessages(msgs, fmt2, cacheControls) {
3688
+ return fmt2 === "anthropic" ? coreToAnthropic(msgs, cacheControls) : coreToOpenai(msgs);
3689
+ }
3690
+ var ANTHROPIC_CODEC_BLOCKS = /* @__PURE__ */ new Set(["text", "tool_use", "tool_result", "thinking", "image"]);
3691
+ var OPENAI_CODEC_ROLES = /* @__PURE__ */ new Set(["system", "developer", "user", "assistant", "tool"]);
3692
+ function payloadRepresentable(payload, fmt2) {
3693
+ const messages = payload.messages;
3694
+ if (!Array.isArray(messages)) return { ok: false, reason: "messages not an array" };
3695
+ for (const message of messages) {
3696
+ if (message === null || typeof message !== "object") return { ok: false, reason: "message not an object" };
3697
+ const bad = fmt2 === "anthropic" ? unrepresentableAnthropicMessage(message) : unrepresentableOpenaiMessage(message);
3698
+ if (bad) return { ok: false, reason: bad };
3272
3699
  }
3273
- return "openai";
3274
- }
3275
- function anthropicBlocks(m) {
3276
- return typeof m.content === "string" ? [{ type: "text", text: m.content }] : m.content ?? [];
3700
+ return { ok: true };
3277
3701
  }
3278
- var assistantBase = () => ({
3279
- api: "anthropic",
3280
- provider: "anthropic",
3281
- model: "wire",
3282
- usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } },
3283
- stopReason: "stop",
3284
- timestamp: Date.now()
3285
- });
3286
- function synthesizeStream(payload, format) {
3287
- const messages = payload.messages ?? [];
3288
- const stream = [];
3289
- const back = [];
3290
- const push = (msg, wi, kind) => {
3291
- msg[AI] = stream.length;
3292
- stream.push(msg);
3293
- back.push({ wi, kind });
3294
- };
3295
- if (format === "anthropic") {
3296
- const toolNames = /* @__PURE__ */ new Map();
3297
- for (const raw of messages) {
3298
- if (raw === null || typeof raw !== "object") continue;
3299
- const m = raw;
3300
- if (m.role !== "assistant") continue;
3301
- for (const b of anthropicBlocks(m)) if (b.type === "tool_use" && b.id) toolNames.set(b.id, b.name ?? "");
3302
- }
3303
- messages.forEach((raw, wi) => {
3304
- if (raw === null || typeof raw !== "object") return;
3305
- const m = raw;
3306
- const blocks = anthropicBlocks(m);
3307
- if (m.role === "user") {
3308
- let texts = [];
3309
- for (const b of blocks) {
3310
- if (b.type === "text" && typeof b.text === "string") texts.push(b.text);
3311
- else if (b.type === "tool_result") {
3312
- if (texts.length > 0) {
3313
- push({ role: "user", content: texts.map((t2) => ({ type: "text", text: t2 })), timestamp: Date.now() }, wi, "text");
3314
- texts = [];
3315
- }
3316
- const trText = typeof b.content === "string" ? b.content : Array.isArray(b.content) ? b.content.map((c) => c.text ?? "").join("\n") : "";
3317
- push({
3318
- role: "toolResult",
3319
- content: [{ type: "text", text: trText }],
3320
- toolName: toolNames.get(b.tool_use_id ?? "") ?? "",
3321
- toolCallId: b.tool_use_id ?? "",
3322
- isError: b.is_error === true,
3323
- timestamp: Date.now()
3324
- }, wi, "toolResult");
3325
- }
3326
- }
3327
- if (texts.length > 0) push({ role: "user", content: texts.map((t2) => ({ type: "text", text: t2 })), timestamp: Date.now() }, wi, "text");
3328
- return;
3329
- }
3330
- if (m.role === "assistant") {
3331
- const content = [];
3332
- for (const b of blocks) {
3333
- if (b.type === "text" && typeof b.text === "string" && b.text.length > 0) content.push({ type: "text", text: b.text });
3334
- else if (b.type === "tool_use") content.push({ type: "toolCall", id: b.id, name: b.name, arguments: b.input ?? {} });
3335
- }
3336
- if (content.length > 0) push({ role: "assistant", ...assistantBase(), content }, wi, "toolCall");
3337
- return;
3702
+ function unrepresentableAnthropicMessage(message) {
3703
+ const content = message.content;
3704
+ if (content == null || typeof content === "string") return null;
3705
+ if (!Array.isArray(content)) return "content neither string nor block array";
3706
+ for (const block of content) {
3707
+ const type5 = block?.type;
3708
+ if (typeof type5 !== "string" || !ANTHROPIC_CODEC_BLOCKS.has(type5)) {
3709
+ return `anthropic block type ${JSON.stringify(type5) ?? "missing"}`;
3710
+ }
3711
+ if (type5 === "tool_result") {
3712
+ const inner = block.content;
3713
+ if (Array.isArray(inner) && inner.some((c) => c?.type !== "text")) {
3714
+ return "tool_result content carries non-text parts (images are flattened away)";
3338
3715
  }
3339
- const t = blocks.map((b) => typeof b.text === "string" ? b.text : "").join("\n");
3340
- if (t) push({ role: "user", content: [{ type: "text", text: t }], timestamp: Date.now() }, wi, "text");
3341
- });
3342
- return { stream, back, format };
3343
- }
3344
- messages.forEach((raw, wi) => {
3345
- if (raw === null || typeof raw !== "object") return;
3346
- const m = raw;
3347
- const textOf = () => {
3348
- const c = m.content;
3349
- if (typeof c === "string") return c;
3350
- if (Array.isArray(c)) return c.map((p) => p.type === "text" ? p.text ?? "" : "").join("\n");
3351
- return "";
3352
- };
3353
- if (m.role === "system" || m.role === "developer") {
3354
- const t2 = textOf();
3355
- if (t2) push({ role: "user", content: [{ type: "text", text: t2 }], timestamp: Date.now() }, wi, "text");
3356
- return;
3357
3716
  }
3358
- if (m.role === "tool") {
3359
- push({
3360
- role: "toolResult",
3361
- content: [{ type: "text", text: textOf() }],
3362
- toolName: "",
3363
- toolCallId: m.tool_call_id ?? "",
3364
- isError: false,
3365
- timestamp: Date.now()
3366
- }, wi, "toolResult");
3367
- return;
3368
- }
3369
- if (m.role === "assistant") {
3370
- const calls = m.tool_calls ?? [];
3371
- if (calls.length > 0) {
3372
- const content = [];
3373
- const t3 = textOf();
3374
- if (t3) content.push({ type: "text", text: t3 });
3375
- for (const c of calls) {
3376
- let args = {};
3377
- try {
3378
- args = c.function?.arguments ? JSON.parse(c.function.arguments) : {};
3379
- } catch {
3380
- args = { raw: c.function?.arguments ?? "" };
3381
- }
3382
- content.push({ type: "toolCall", id: c.id, name: c.function?.name ?? "", arguments: args });
3383
- }
3384
- push({ role: "assistant", ...assistantBase(), content }, wi, "toolCall");
3385
- return;
3386
- }
3387
- const t2 = textOf();
3388
- if (t2) push({ role: "assistant", ...assistantBase(), content: [{ type: "text", text: t2 }] }, wi, "text");
3389
- return;
3717
+ if (type5 === "thinking" && block.cache_control != null) {
3718
+ return "cache_control on a thinking block is not re-attached";
3390
3719
  }
3391
- const t = textOf();
3392
- if (t) push({ role: "user", content: [{ type: "text", text: t }], timestamp: Date.now() }, wi, "text");
3393
- });
3394
- return { stream, back, format };
3720
+ }
3721
+ return null;
3395
3722
  }
3396
- function rebuildWirePayload(rebuilt, payload, synth) {
3397
- const messages = (payload.messages ?? []).slice();
3398
- const out = [];
3399
- const agentIndexOf = (m) => m[AI];
3400
- for (const agent of rebuilt) {
3401
- const ai = agentIndexOf(agent);
3402
- if (ai === void 0 || ai >= synth.back.length) {
3403
- const text2 = extractText(agent.content, false);
3404
- const role = agent.role === "assistant" ? "assistant" : "user";
3405
- if (synth.format === "anthropic") {
3406
- out.push({ role, content: [{ type: "text", text: text2 }] });
3407
- } else {
3408
- out.push({ role, content: text2 });
3409
- }
3723
+ function unrepresentableOpenaiMessage(message) {
3724
+ const role = message.role;
3725
+ if (typeof role !== "string" || !OPENAI_CODEC_ROLES.has(role)) {
3726
+ return `openai role ${JSON.stringify(role) ?? "missing"}`;
3727
+ }
3728
+ const content = message.content;
3729
+ if (content == null || typeof content === "string") return null;
3730
+ if (!Array.isArray(content)) return "content neither string nor part array";
3731
+ let dataImages = 0;
3732
+ for (const part of content) {
3733
+ if (typeof part === "string") continue;
3734
+ const type5 = part?.type;
3735
+ if (type5 === "text") continue;
3736
+ if (type5 === "image_url" && role === "user") {
3737
+ const url = part?.image_url?.url;
3738
+ if (typeof url !== "string" || !url.startsWith("data:")) return "image_url without a data: URL is dropped";
3739
+ if (++dataImages > 1) return "second image_url in one message is dropped";
3410
3740
  continue;
3411
3741
  }
3412
- const { wi, kind } = synth.back[ai];
3413
- const src = messages[wi];
3414
- if (!src) {
3415
- out.push(agent);
3416
- continue;
3417
- }
3418
- if (synth.format === "anthropic") {
3419
- const srcMsg2 = src;
3420
- const blocks = anthropicBlocks(srcMsg2);
3421
- if (kind === "toolResult") {
3422
- const block2 = blocks.find((b) => b.type === "tool_result" && b.tool_use_id === agent.toolCallId);
3423
- const text3 = extractText(agent.content, false);
3424
- if (block2) {
3425
- out.push({ role: "user", content: [{ ...block2, content: [{ type: "text", text: text3 }] }] });
3426
- continue;
3427
- }
3428
- }
3429
- if (kind === "toolCall") {
3430
- const callIds = new Set(
3431
- (agent.content ?? []).filter((b) => b.type === "toolCall" && b.id).map((b) => b.id)
3432
- );
3433
- const agentText = extractText(agent.content, false);
3434
- const outBlocks = [];
3435
- let textSeen = false;
3436
- for (const b of blocks) {
3437
- if (b.type === "tool_use") {
3438
- if (callIds.has(b.id ?? "")) outBlocks.push(b);
3439
- continue;
3440
- }
3441
- if (b.type === "text") {
3442
- if (!textSeen && typeof b.text === "string") {
3443
- outBlocks.push(b);
3444
- textSeen = true;
3445
- }
3446
- continue;
3447
- }
3448
- outBlocks.push(b);
3449
- }
3450
- if (agentText && !textSeen) outBlocks.unshift({ type: "text", text: agentText });
3451
- out.push({ role: "assistant", content: outBlocks });
3452
- continue;
3453
- }
3454
- const text2 = extractText(agent.content, false);
3455
- const block = blocks.find((b) => b.type === "text");
3456
- if (block) out.push({ role: srcMsg2.role, content: [{ ...block, text: text2 }] });
3457
- else out.push({ role: srcMsg2.role, content: [{ type: "text", text: text2 }] });
3458
- continue;
3459
- }
3460
- const srcMsg = src;
3461
- const text = extractText(agent.content, false);
3462
- if (kind === "toolResult") {
3463
- out.push({ role: "tool", tool_call_id: agent.toolCallId ?? srcMsg.tool_call_id, content: text });
3464
- continue;
3465
- }
3466
- if (kind === "toolCall") {
3467
- const callIds = new Set(
3468
- (agent.content ?? []).filter((b) => b.type === "toolCall" && b.id).map((b) => b.id)
3469
- );
3470
- const surviving = (srcMsg.tool_calls ?? []).filter((c) => callIds.has(c.id));
3471
- const entry = { role: "assistant", content: text };
3472
- if (surviving.length > 0) entry.tool_calls = surviving;
3473
- out.push(entry);
3474
- continue;
3742
+ return `openai content part type ${JSON.stringify(type5) ?? "missing"}`;
3743
+ }
3744
+ return null;
3745
+ }
3746
+ var renderRefsAll = createRenderRefsNode("all");
3747
+ function applyWireTagContract(msgs, state, scope) {
3748
+ const stripAssistantTags = (m) => m.contentType === "text" && m.role === "assistant" ? { ...m, text: stripRefTag(m.text ?? "") } : m;
3749
+ const toolResults = msgs.filter((m) => m.contentType === "tool-result");
3750
+ if (toolResults.length === 0) return msgs.map(stripAssistantTags);
3751
+ const names = toolCallNames(msgs);
3752
+ const named = toolResults.map((m) => m.toolName ? m : { ...m, toolName: names.get(m.toolCallId ?? "") ?? "tool" });
3753
+ const io = renderRefsAll.run(
3754
+ { messages: named, state, effects: {} },
3755
+ { config: scope.config, tokenCount: scope.tokenCount, countTokens: defaultCountTokens }
3756
+ );
3757
+ if (io.state !== state) state.tokenSnapshot = io.state.tokenSnapshot;
3758
+ const tagged = io.messages;
3759
+ const bySource = new Map(toolResults.map((m, i) => [m, tagged[i]]));
3760
+ return msgs.map((m) => m.contentType === "tool-result" ? bySource.get(m) ?? m : stripAssistantTags(m));
3761
+ }
3762
+ function coreIdentity(msg) {
3763
+ return JSON.stringify({
3764
+ role: msg.role,
3765
+ contentType: msg.contentType,
3766
+ toolName: msg.toolName ?? null,
3767
+ toolCallId: msg.toolCallId ?? null,
3768
+ text: stripRefTag(msg.text ?? "")
3769
+ });
3770
+ }
3771
+ function toolCallNames(msgs) {
3772
+ const names = /* @__PURE__ */ new Map();
3773
+ for (const m of msgs) {
3774
+ if (m.contentType === "tool-call" && m.toolCallId && m.toolName) names.set(m.toolCallId, m.toolName);
3775
+ }
3776
+ return names;
3777
+ }
3778
+ function toolResultTextsCore(msgs) {
3779
+ const results = /* @__PURE__ */ new Map();
3780
+ for (const m of msgs) {
3781
+ if (m.contentType !== "tool-result" || !m.toolCallId) continue;
3782
+ results.set(m.toolCallId, m.text ?? "");
3783
+ }
3784
+ return results;
3785
+ }
3786
+ function findCompressCallsCore(msg) {
3787
+ if (msg.contentType !== "tool-call" || !msg.toolName) return [];
3788
+ const args = compressToolArgs({ name: msg.toolName, arguments: msg.text });
3789
+ if (!args) return [];
3790
+ const content = args.content;
3791
+ if (!Array.isArray(content)) return [];
3792
+ const ranges = [];
3793
+ const callTopic = typeof args.topic === "string" ? args.topic : void 0;
3794
+ for (const item of content) {
3795
+ const r = item;
3796
+ if (typeof r.startId !== "string" || typeof r.endId !== "string" || typeof r.summary !== "string" || r.summary.length === 0) continue;
3797
+ ranges.push({
3798
+ startRef: r.startId,
3799
+ endRef: r.endId,
3800
+ summary: r.summary,
3801
+ topic: typeof r.topic === "string" ? r.topic : callTopic,
3802
+ summaryMaxChars: typeof args.summaryMaxChars === "number" ? args.summaryMaxChars : void 0,
3803
+ compressCallId: msg.toolCallId ?? ""
3804
+ });
3805
+ }
3806
+ return ranges.length > 0 ? [{ id: msg.toolCallId ?? "", ranges }] : [];
3807
+ }
3808
+ function corePieceKey(cm) {
3809
+ return `${cm.role}|${cm.contentType}|${cm.toolName ?? ""}|${(cm.text ?? "").slice(0, 4096)}`;
3810
+ }
3811
+ function spanFingerprintCoreIdx(coreMessages, startIdx, endIdx) {
3812
+ const first = coreMessages[startIdx];
3813
+ const last = coreMessages[endIdx];
3814
+ if (!first || !last) return "";
3815
+ return createHash3("sha1").update(`${corePieceKey(first)}\0${corePieceKey(last)}`).digest("hex").slice(0, 8);
3816
+ }
3817
+ function boundaryRawCore(ref, byRef, blocks, coreMessages, pick) {
3818
+ const raw = byRef[ref];
3819
+ if (raw) return raw;
3820
+ const m = /^b(\d+)$/i.exec(ref.trim());
3821
+ if (!m) return "";
3822
+ const block = blocks.find((b) => b.blockId.toLowerCase() === `b${m[1]}`);
3823
+ if (!block) return "";
3824
+ const idx = (id) => coreMessages.findIndex((cm) => cm.id === (byRef[id] ?? id));
3825
+ let best = -1;
3826
+ for (const id of block.effectiveMessageIds) {
3827
+ const i = idx(id);
3828
+ if (i < 0) continue;
3829
+ if (best < 0 || (pick === "min" ? i < best : i > best)) best = i;
3830
+ }
3831
+ return best < 0 ? "" : coreMessages[best]?.id ?? "";
3832
+ }
3833
+ function boundaryIndexCore(ref, byRef, blocks, coreMessages, pick, fallbackIdx = -1) {
3834
+ const id = boundaryRawCore(ref, byRef, blocks, coreMessages, pick);
3835
+ if (id) {
3836
+ const i = coreMessages.findIndex((cm) => cm.id === id);
3837
+ if (i >= 0) return i;
3838
+ }
3839
+ return fallbackIdx >= 0 && fallbackIdx < coreMessages.length ? fallbackIdx : -1;
3840
+ }
3841
+ function refOfPieceCore(coreMessages, idx, byRef) {
3842
+ const id = coreMessages[idx]?.id;
3843
+ if (!id) return "";
3844
+ for (const [ref, mapped] of Object.entries(byRef)) if (mapped === id) return ref;
3845
+ return "";
3846
+ }
3847
+ function staleRangeCore(r, rangeIndex, resultText, coreMessages, callIndex, byRef, blocks) {
3848
+ const pm = resultText.match(/\[pos=([0-9,-]+)\]/);
3849
+ const pair = pm ? pm[1].split(",")[rangeIndex] ?? "-" : "-";
3850
+ const hinted = pair !== "-";
3851
+ const [ps, pe] = pair === "-" ? ["", ""] : pair.split("-");
3852
+ const fbStart = ps && ps !== "" ? Number.parseInt(ps, 10) : -1;
3853
+ const fbEnd = pe && pe !== "" ? Number.parseInt(pe, 10) : -1;
3854
+ const startRaw = boundaryRawCore(r.startRef, byRef, blocks, coreMessages, "min");
3855
+ const endRaw = boundaryRawCore(r.endRef, byRef, blocks, coreMessages, "max");
3856
+ const rawStartIdx = startRaw ? coreMessages.findIndex((cm) => cm.id === startRaw) : -1;
3857
+ const rawEndIdx = endRaw ? coreMessages.findIndex((cm) => cm.id === endRaw) : -1;
3858
+ const startIdx = rawStartIdx >= 0 ? rawStartIdx : fbStart >= 0 && fbStart < coreMessages.length ? fbStart : -1;
3859
+ const endIdx = rawEndIdx >= 0 ? rawEndIdx : fbEnd >= 0 && fbEnd < coreMessages.length ? fbEnd : -1;
3860
+ if (startIdx < 0 || endIdx < 0) {
3861
+ if (!/^b\d+$/i.test(r.startRef.trim()) && !/^b\d+$/i.test(r.endRef.trim()))
3862
+ return { reject: `unresolved ${r.startRef}..${r.endRef} -> ${startIdx}..${endIdx}`, ...hinted ? { hint: true } : {} };
3863
+ return {};
3864
+ }
3865
+ if (endIdx > callIndex) return { reject: `end idx ${endIdx} > callIndex ${callIndex}`, ...hinted ? { hint: true } : {} };
3866
+ const m = resultText.match(/\[fp=([0-9a-f,-]+)\]/);
3867
+ if (m) {
3868
+ const want = m[1].split(",")[rangeIndex];
3869
+ if (want !== void 0 && want !== "-") {
3870
+ const got = spanFingerprintCoreIdx(coreMessages, startIdx, endIdx);
3871
+ if (want !== got) return { reject: `fp ${r.startRef}..${r.endRef} want ${want} got ${got} @${startIdx}..${endIdx}`, ...hinted ? { hint: true } : {} };
3475
3872
  }
3476
- out.push({ role: srcMsg.role === "assistant" ? "assistant" : srcMsg.role, content: text });
3477
3873
  }
3478
- return { ...payload, messages: out };
3874
+ const remap = {};
3875
+ if (/^m\d+$/i.test(r.startRef.trim()) && rawStartIdx < 0) {
3876
+ const ref = refOfPieceCore(coreMessages, startIdx, byRef);
3877
+ if (!ref) return { reject: `recovered ${r.startRef} @${startIdx} has no ref (protected piece)`, ...hinted ? { hint: true } : {} };
3878
+ remap.startRef = ref;
3879
+ }
3880
+ if (/^m\d+$/i.test(r.endRef.trim()) && rawEndIdx < 0) {
3881
+ const ref = refOfPieceCore(coreMessages, endIdx, byRef);
3882
+ if (!ref) return { reject: `recovered ${r.endRef} @${endIdx} has no ref (protected piece)`, ...hinted ? { hint: true } : {} };
3883
+ remap.endRef = ref;
3884
+ }
3885
+ if (!remap.startRef && !remap.endRef) return {};
3886
+ return { remap, recovered: { pos: pair, startIdx, endIdx } };
3887
+ }
3888
+ function rangePositionsCore(ranges, coreMessages, byRef, blocks) {
3889
+ return ranges.map((r) => {
3890
+ const s = boundaryIndexCore(r.startRef, byRef, blocks, coreMessages, "min");
3891
+ const e = s >= 0 ? boundaryIndexCore(r.endRef, byRef, blocks, coreMessages, "max") : -1;
3892
+ return s >= 0 && e >= 0 ? `${s}-${e}` : "-";
3893
+ });
3479
3894
  }
3480
- function viewToWireStream(view, systemText) {
3895
+ function viewToCoreStream(view, systemText) {
3481
3896
  const messages = [{ role: "system", content: systemText }];
3482
3897
  for (const message of view) {
3483
3898
  const m = message;
3484
3899
  if (m.role === "user") {
3485
- const text = extractText(m.content);
3900
+ const text = extractViewText(m.content);
3486
3901
  if (text) messages.push({ role: "user", content: text });
3487
3902
  } else if (m.role === "assistant") {
3488
3903
  const blocks = Array.isArray(m.content) ? m.content : [];
3489
3904
  const calls = blocks.filter(
3490
3905
  (b) => b !== null && typeof b === "object" && b.type === "toolCall"
3491
3906
  );
3492
- const text = extractText(m.content);
3907
+ const text = extractViewText(m.content);
3493
3908
  if (calls.length > 0) {
3494
3909
  messages.push({
3495
3910
  role: "assistant",
@@ -3504,13 +3919,63 @@ function viewToWireStream(view, systemText) {
3504
3919
  messages.push({ role: "assistant", content: text });
3505
3920
  }
3506
3921
  } else if (m.role === "toolResult") {
3507
- messages.push({ role: "tool", tool_call_id: m.toolCallId ?? "", content: extractText(m.content) });
3922
+ messages.push({ role: "tool", tool_call_id: m.toolCallId ?? "", content: extractViewText(m.content) });
3508
3923
  } else {
3509
- const text = extractText(m.content) || (typeof m.summary === "string" ? m.summary : "");
3924
+ const text = extractViewText(m.content) || (typeof m.summary === "string" ? m.summary : "");
3510
3925
  if (text) messages.push({ role: "developer", content: text });
3511
3926
  }
3512
3927
  }
3513
- return synthesizeStream({ model: "prime-fold", messages }, "openai").stream;
3928
+ const { msgs } = openaiToCore({ model: "prime-fold", messages });
3929
+ return msgs;
3930
+ }
3931
+ function viewToAnthropicCore(view) {
3932
+ const messages = [];
3933
+ for (const message of view) {
3934
+ const m = message;
3935
+ if (m.role === "user") {
3936
+ const text = extractViewText(m.content);
3937
+ if (text) messages.push({ role: "user", content: [{ type: "text", text }] });
3938
+ } else if (m.role === "assistant") {
3939
+ const blocks = Array.isArray(m.content) ? m.content : [];
3940
+ const calls = blocks.filter(
3941
+ (b) => b !== null && typeof b === "object" && b.type === "toolCall"
3942
+ );
3943
+ const text = extractViewText(m.content);
3944
+ const content = [];
3945
+ if (text) content.push({ type: "text", text });
3946
+ for (const c of calls) {
3947
+ let input = {};
3948
+ try {
3949
+ input = c.arguments && typeof c.arguments === "object" ? c.arguments : JSON.parse(JSON.stringify(c.arguments ?? {}));
3950
+ } catch {
3951
+ input = {};
3952
+ }
3953
+ content.push({ type: "tool_use", id: c.id, name: c.name ?? "", input });
3954
+ }
3955
+ if (content.length > 0) messages.push({ role: "assistant", content });
3956
+ } else if (m.role === "toolResult") {
3957
+ messages.push({
3958
+ role: "user",
3959
+ content: [{ type: "tool_result", tool_use_id: m.toolCallId ?? "", content: extractViewText(m.content) }]
3960
+ });
3961
+ } else {
3962
+ const text = extractViewText(m.content) || (typeof m.summary === "string" ? m.summary : "");
3963
+ if (text) messages.push({ role: "user", content: [{ type: "text", text }] });
3964
+ }
3965
+ }
3966
+ const { msgs, cacheControls } = anthropicToCore({ model: "prime-fold", messages });
3967
+ void cacheControls;
3968
+ return msgs;
3969
+ }
3970
+ function extractViewText(content) {
3971
+ const clean = (s) => stripRefTag(s);
3972
+ if (typeof content === "string") return clean(content);
3973
+ if (!Array.isArray(content)) return "";
3974
+ const parts = [];
3975
+ for (const b of content) {
3976
+ if (b.type === "text" && typeof b.text === "string") parts.push(clean(b.text));
3977
+ }
3978
+ return parts.join("\n");
3514
3979
  }
3515
3980
 
3516
3981
  // src/runtime.ts
@@ -3538,6 +4003,7 @@ function createRuntime(adapter) {
3538
4003
  const core = createCore({ countTokens: defaultCountTokens });
3539
4004
  const locks = /* @__PURE__ */ new Map();
3540
4005
  const slots = /* @__PURE__ */ new Map();
4006
+ const coreSlots = /* @__PURE__ */ new Map();
3541
4007
  let adapterRef = adapter;
3542
4008
  let promptsRef = defaultPrompts;
3543
4009
  async function acquireLock(sid) {
@@ -3567,6 +4033,14 @@ function createRuntime(adapter) {
3567
4033
  }
3568
4034
  return slot;
3569
4035
  }
4036
+ function coreSlotFor(sid) {
4037
+ let slot = coreSlots.get(sid);
4038
+ if (!slot) {
4039
+ slot = freshSlot();
4040
+ coreSlots.set(sid, slot);
4041
+ }
4042
+ return slot;
4043
+ }
3570
4044
  function sidOf(ctx) {
3571
4045
  return ctx.sessionManager.getSessionId();
3572
4046
  }
@@ -3651,8 +4125,90 @@ function createRuntime(adapter) {
3651
4125
  stream.forEach((message, i) => originalById.set(`p${i + 1}`, message));
3652
4126
  return { state: slot.state, coreMessages, originalById, streamLen: stream.length };
3653
4127
  }
4128
+ function foldStreamCore(ctx, stream) {
4129
+ const sid = sidOf(ctx);
4130
+ let slot = coreSlotFor(sid);
4131
+ if (slot.preview) {
4132
+ debug.event("fold-refold", { sid, foldedLen: slot.foldedLen, lcp: 0, streamLen: stream.length, reason: "preview" });
4133
+ slot = freshSlot(slot);
4134
+ coreSlots.set(sid, slot);
4135
+ }
4136
+ const ids = stream.map(coreIdentity);
4137
+ let lcp = 0;
4138
+ while (lcp < Math.min(ids.length, slot.identities.length) && ids[lcp] === slot.identities[lcp]) lcp++;
4139
+ if (lcp < slot.foldedLen) {
4140
+ const flip = isViewFlip(slot.foldedLen, lcp);
4141
+ debug.event("fold-refold", { sid, foldedLen: slot.foldedLen, lcp, streamLen: stream.length, flip, space: "core" });
4142
+ slot = flip ? preserveCompressedSlot(slot) : freshSlot(slot);
4143
+ coreSlots.set(sid, slot);
4144
+ lcp = 0;
4145
+ }
4146
+ const coreMessages = stream;
4147
+ const config = configFor(ctx);
4148
+ const names = toolCallNames(stream);
4149
+ const assigned = assignRefs(coreMessages, {
4150
+ existing: slot.state.messageRefs,
4151
+ nextIndex: highestUsedIndex(slot.state.messageRefs) + 1,
4152
+ isProtected: (m) => {
4153
+ if (m.role !== "tool" || !m.toolCallId) return false;
4154
+ const name = names.get(m.toolCallId);
4155
+ if (!name) return false;
4156
+ if (name === "compress") return true;
4157
+ return (config.protectedTools ?? []).includes(name);
4158
+ }
4159
+ });
4160
+ slot.state = { ...slot.state, messageRefs: assigned.map };
4161
+ const isFreshFold = slot.foldedLen === 0;
4162
+ const resultTexts = toolResultTextsCore(stream);
4163
+ let replayed = 0;
4164
+ for (let i = isFreshFold ? 0 : slot.foldedLen; i < stream.length; i++) {
4165
+ for (const call of findCompressCallsCore(stream[i])) {
4166
+ const resultText = resultTexts.get(call.id) ?? "";
4167
+ if (resultText.includes("No changes applied")) {
4168
+ debug.event("fold-replay-skipped", { sid, callId: call.id });
4169
+ continue;
4170
+ }
4171
+ if (slot.appliedCallIds.has(call.id) || stateHasCompressCall(slot.state, call.id)) continue;
4172
+ const verdicts = call.ranges.map((r, ri) => staleRangeCore(r, ri, resultText, coreMessages, i, slot.state.messageRefs.byRef, slot.state.blocks));
4173
+ const stale = verdicts.find((v) => v.reject);
4174
+ if (stale) {
4175
+ debug.event("fold-replay-stale", { sid, callId: call.id, reason: stale.reject });
4176
+ const failed = verdicts.find((v) => v.hint && v.reject);
4177
+ if (failed) logWarn("fold", { sid, event: "replay-recovery-failed", callId: call.id, reason: failed.reject });
4178
+ continue;
4179
+ }
4180
+ const recovered = verdicts.find((v) => v.recovered);
4181
+ const ranges = recovered ? call.ranges.map((r, ri) => {
4182
+ const m = verdicts[ri].remap;
4183
+ return m ? { ...r, startRef: m.startRef ?? r.startRef, endRef: m.endRef ?? r.endRef } : r;
4184
+ }) : call.ranges;
4185
+ if (recovered) {
4186
+ logWarn("fold", { sid, event: "replay-recovered", callId: call.id, pos: recovered.recovered.pos, startIdx: recovered.recovered.startIdx, endIdx: recovered.recovered.endIdx });
4187
+ }
4188
+ try {
4189
+ const applied = core.applyCompression({ ranges, messages: coreMessages, state: slot.state, config });
4190
+ if (applied.result.errors.length === 0) {
4191
+ slot.state = applied.state;
4192
+ replayed++;
4193
+ debug.event("fold-replay", { sid, callId: call.id, ranges: call.ranges.length });
4194
+ } else {
4195
+ logWarn("fold", { sid, event: "replay-rejected", callId: call.id, errors: applied.result.errors.slice(0, 3) });
4196
+ }
4197
+ } catch (e) {
4198
+ logWarn("fold", { sid, event: "replay-failed", callId: call.id, error: e instanceof Error ? e.message : String(e) });
4199
+ }
4200
+ slot.appliedCallIds.add(call.id);
4201
+ }
4202
+ }
4203
+ if (replayed > 0) logWarn("fold", { sid, event: "replayed", calls: replayed });
4204
+ slot.identities = ids;
4205
+ slot.foldedLen = ids.length;
4206
+ slot.coreMessages = coreMessages;
4207
+ return { state: slot.state, coreMessages, streamLen: stream.length };
4208
+ }
3654
4209
  function stateFor(ctx) {
3655
- const slot = slotFor(sidOf(ctx));
4210
+ const sid = sidOf(ctx);
4211
+ const slot = slotForMode(ctx, sid);
3656
4212
  return Promise.resolve({ state: slot.state, coreMessages: slot.coreMessages });
3657
4213
  }
3658
4214
  function primeFold(ctx) {
@@ -3661,30 +4217,40 @@ function createRuntime(adapter) {
3661
4217
  const sm = ctx.sessionManager;
3662
4218
  const view = sm.buildSessionContext?.().messages ?? [];
3663
4219
  if (view.length === 0) return;
3664
- let stream = view;
3665
- let wire = false;
3666
- if ((adapterRef.transformMode ?? "provider") === "provider" && ctx.model?.api === "openai-completions") {
3667
- const base = getSystemPromptText(ctx);
3668
- const acp = buildAcpSystemPrompt(promptsRef);
3669
- stream = viewToWireStream(view, base.includes(acp) ? base : `${base}
4220
+ if (resolveTransformMode(adapterRef, ctx.model) === "provider") {
4221
+ const api = ctx.model?.api ?? "";
4222
+ let stream;
4223
+ if (api === "anthropic-messages") {
4224
+ stream = viewToAnthropicCore(view);
4225
+ } else {
4226
+ const base = getSystemPromptText(ctx);
4227
+ const acp = buildAcpSystemPrompt(promptsRef);
4228
+ stream = viewToCoreStream(view, base.includes(acp) ? base : `${base}
3670
4229
 
3671
4230
  ${acp}`);
3672
- wire = true;
4231
+ }
4232
+ const r2 = foldStreamCore(ctx, stream);
4233
+ coreSlotFor(sid).preview = true;
4234
+ logInfo("fold", { sid, event: "prime-fold", msgs: stream.length, wire: true, blocks: r2.state.blocks.length });
4235
+ return;
3673
4236
  }
3674
- const r = foldStream(ctx, stream);
4237
+ const r = foldStream(ctx, view);
3675
4238
  slotFor(sid).preview = true;
3676
- logInfo("fold", { sid, event: "prime-fold", msgs: stream.length, wire, blocks: r.state.blocks.length });
4239
+ logInfo("fold", { sid, event: "prime-fold", msgs: view.length, wire: false, blocks: r.state.blocks.length });
3677
4240
  } catch (e) {
3678
4241
  logWarn("fold", { sid, event: "prime-fold-failed", error: e instanceof Error ? e.message : String(e) });
3679
4242
  }
3680
4243
  }
3681
4244
  function forgetSession(sid) {
3682
4245
  slots.delete(sid);
4246
+ coreSlots.delete(sid);
3683
4247
  locks.delete(sid);
3684
4248
  }
4249
+ function slotForMode(ctx, sid) {
4250
+ return resolveTransformMode(adapterRef, ctx.model) === "provider" ? coreSlotFor(sid) : slotFor(sid);
4251
+ }
3685
4252
  function commitFoldState(ctx, state, toolCallId) {
3686
- const sid = sidOf(ctx);
3687
- const slot = slotFor(sid);
4253
+ const slot = slotForMode(ctx, sidOf(ctx));
3688
4254
  slot.state = state;
3689
4255
  if (toolCallId) slot.appliedCallIds.add(toolCallId);
3690
4256
  }
@@ -3693,7 +4259,7 @@ ${acp}`);
3693
4259
  slot.lastRebuiltOutput = rebuilt.map(messageIdentity);
3694
4260
  }
3695
4261
  function noteCompressOutcome(ctx, ok) {
3696
- const slot = slotFor(sidOf(ctx));
4262
+ const slot = slotForMode(ctx, sidOf(ctx));
3697
4263
  slot.rejectStreak = ok ? 0 : slot.rejectStreak + 1;
3698
4264
  return slot.rejectStreak;
3699
4265
  }
@@ -3714,6 +4280,7 @@ ${acp}`);
3714
4280
  liveContextLimit,
3715
4281
  configFor,
3716
4282
  foldStream,
4283
+ foldStreamCore,
3717
4284
  stateFor,
3718
4285
  commitFoldState,
3719
4286
  recordRebuiltOutput,
@@ -3906,9 +4473,11 @@ async function handleCompress(args, runtime, ctx, toolCallId) {
3906
4473
  logError("compress", { sid: ctx.sessionManager.getSessionId(), event: "warnings", count: warnings.length, warnings: warnings.slice(0, 5) });
3907
4474
  }
3908
4475
  const fps = rangeFingerprints(rangeSpecs, coreMessages, applied.state.messageRefs.byRef, applied.state.blocks);
4476
+ const positions = rangePositionsCore(rangeSpecs, coreMessages, applied.state.messageRefs.byRef, applied.state.blocks);
3909
4477
  const lines = [`\u25A3 ACP | ${formatTokens3(beforeTokens)} \u2192 ${formatTokens3(afterTokens)} tokens (~${formatTokens3(tokensCompressed)} reclaimed, ${blocksCreated} block${blocksCreated > 1 ? "s" : ""})`];
3910
4478
  if (warnings.length > 0) lines.push("\u26A0\uFE0F " + warnings.join("; "));
3911
4479
  if (fps.some((fp) => fp !== "-")) lines.push(`[fp=${fps.join(",")}]`);
4480
+ if (positions.some((p) => p !== "-")) lines.push(`[pos=${positions.join(",")}]`);
3912
4481
  return lines.join("\n");
3913
4482
  } finally {
3914
4483
  releaseLock();
@@ -4280,7 +4849,7 @@ function renderMessage2(message, map, countTokens, strategy) {
4280
4849
  if (!cleanText) return { ...message, text: prefix };
4281
4850
  return { ...message, text: prefix + cleanText };
4282
4851
  }
4283
- function renderVisibleRefs2(messages, state, countTokens = (text) => Math.ceil(text.length / 4), strategy = "all") {
4852
+ function renderVisibleRefs(messages, state, countTokens = (text) => Math.ceil(text.length / 4), strategy = "all") {
4284
4853
  const map = state.messageRefs;
4285
4854
  return messages.map(
4286
4855
  (message) => renderMessage2(message, map, countTokens, strategy)
@@ -4292,7 +4861,7 @@ function createRenderRefsNode2(strategy) {
4292
4861
  run(io, ctx) {
4293
4862
  return {
4294
4863
  ...io,
4295
- messages: renderVisibleRefs2(io.messages, io.state, ctx.countTokens, strategy)
4864
+ messages: renderVisibleRefs(io.messages, io.state, ctx.countTokens, strategy)
4296
4865
  };
4297
4866
  }
4298
4867
  };
@@ -4903,7 +5472,7 @@ async function statusReport(runtime, ctx) {
4903
5472
  const coveredIds = collectCoveredMessageIds(state);
4904
5473
  const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
4905
5474
  const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: sentTokens });
4906
- const versionStr = "0.2.6" ? `billion-context-omp@${"0.2.6"}` : void 0;
5475
+ const versionStr = "0.2.8" ? `billion-context-omp@${"0.2.8"}` : void 0;
4907
5476
  return buildStatusPanel({
4908
5477
  version: versionStr,
4909
5478
  tokenCount: sessionTokens,
@@ -5033,6 +5602,10 @@ import { readFileSync, writeFileSync } from "fs";
5033
5602
  import { join as join3 } from "path";
5034
5603
  var MARKER_FILE = ".billion-context-omp-instance.json";
5035
5604
  var FRESH_MS = 6e4;
5605
+ function normalizeLoadPath(p) {
5606
+ const q = p.indexOf("?");
5607
+ return q === -1 ? p : p.slice(0, q);
5608
+ }
5036
5609
  function markerPath() {
5037
5610
  return join3(homeDir(), ".omp", MARKER_FILE);
5038
5611
  }
@@ -5047,7 +5620,7 @@ function readMarker() {
5047
5620
  function detectDualInstance(selfPath, now = Date.now()) {
5048
5621
  const m = readMarker();
5049
5622
  if (!m) return void 0;
5050
- if (m.path === selfPath) return void 0;
5623
+ if (normalizeLoadPath(m.path) === normalizeLoadPath(selfPath)) return void 0;
5051
5624
  if (now - m.ts > FRESH_MS) return void 0;
5052
5625
  return m;
5053
5626
  }
@@ -5193,7 +5766,7 @@ async function checkForUpdate(autoUpdate, notify) {
5193
5766
  const data = await res.json();
5194
5767
  const latest = data.version;
5195
5768
  if (!latest) return;
5196
- const current = runtimeVersion ?? "0.2.6";
5769
+ const current = runtimeVersion ?? "0.2.8";
5197
5770
  const hasUpdate = isNewer(latest, current);
5198
5771
  debug.event("update-check", {
5199
5772
  current,
@@ -5450,7 +6023,6 @@ function createAcpExtension(adapter = {}) {
5450
6023
  return (pi) => {
5451
6024
  const runtime = createRuntime(adapter);
5452
6025
  wireSessionLifecycle(pi, runtime);
5453
- wireSessionLifecycle(pi, runtime);
5454
6026
  wireContextTransform(pi, runtime);
5455
6027
  wireSystemPrompt(pi, runtime);
5456
6028
  wireProviderTransform(pi, runtime);
@@ -5469,9 +6041,9 @@ var index_default = createAcpExtension();
5469
6041
  function wireSessionLifecycle(pi, runtime) {
5470
6042
  pi.on("session_start", async (_event, ctx) => {
5471
6043
  const sid = ctx.sessionManager.getSessionId();
5472
- logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.2.6" : null });
6044
+ logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.2.8" : null });
5473
6045
  const selfPath = import.meta.url;
5474
- const conflict = stampAndDetect(selfPath, true ? "0.2.6" : null);
6046
+ const conflict = stampAndDetect(selfPath, true ? "0.2.8" : null);
5475
6047
  if (conflict) {
5476
6048
  logWarn("instance", { event: "dual-instance", self: selfPath, other: conflict.path, otherPid: conflict.pid, otherVersion: conflict.version });
5477
6049
  try {
@@ -5495,9 +6067,9 @@ function wireSessionLifecycle(pi, runtime) {
5495
6067
  runtime.setPrompts(defaultPrompts);
5496
6068
  }
5497
6069
  runtime.primeFold(ctx);
5498
- await checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
6070
+ void checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
5499
6071
  if (ctx.hasUI) ctx.ui.notify(msg);
5500
- });
6072
+ }).catch((e) => logThrow("update", e, { sid, phase: "session_start" }));
5501
6073
  });
5502
6074
  pi.on("session_shutdown", (_event, ctx) => {
5503
6075
  try {
@@ -5631,14 +6203,14 @@ ${rendered.text}${example}`);
5631
6203
  } finally {
5632
6204
  release();
5633
6205
  }
5634
- await checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
6206
+ void checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
5635
6207
  if (ctx.hasUI) ctx.ui.notify(msg);
5636
- });
6208
+ }).catch((e) => logThrow("update", e, { sid, phase: "context" }));
5637
6209
  return result;
5638
6210
  }
5639
6211
  function wireContextTransform(pi, runtime) {
5640
6212
  pi.on("context", async (event, ctx) => {
5641
- if ((runtime.adapter.transformMode ?? "provider") === "provider") {
6213
+ if (resolveTransformMode(runtime.adapter, ctx.model) === "provider") {
5642
6214
  debug.event("context-observer-skip", { sid: ctx.sessionManager.getSessionId(), msgs: event.messages?.length ?? 0 });
5643
6215
  return void 0;
5644
6216
  }
@@ -5649,34 +6221,158 @@ function wireContextTransform(pi, runtime) {
5649
6221
  }
5650
6222
  function wireProviderTransform(pi, runtime) {
5651
6223
  pi.on("before_provider_request", async (event, ctx) => {
5652
- if ((runtime.adapter.transformMode ?? "provider") !== "provider") return void 0;
6224
+ if (resolveTransformMode(runtime.adapter, ctx.model) !== "provider") return void 0;
5653
6225
  const payload = event.payload;
5654
6226
  if (payload === null || typeof payload !== "object" || !Array.isArray(payload.messages)) return void 0;
5655
6227
  const sid = ctx.sessionManager?.getSessionId?.() ?? "";
5656
- const fmt2 = detectWireFormat(payload);
5657
- if (fmt2 === "unknown") {
6228
+ const fmt2 = detectProviderWireFormat(payload);
6229
+ if (fmt2 === null) {
5658
6230
  debug.event("provider-transform-unknown-format", { sid });
5659
6231
  return void 0;
5660
6232
  }
6233
+ const representable = payloadRepresentable(payload, fmt2);
6234
+ if (!representable.ok) {
6235
+ logInfo("provider-transform", { sid, fmt: fmt2, event: "unrepresentable", reason: representable.reason });
6236
+ debug.event("provider-transform-unrepresentable", { sid, fmt: fmt2, reason: representable.reason });
6237
+ return void 0;
6238
+ }
5661
6239
  try {
5662
- const synth = synthesizeStream(payload, fmt2);
5663
- if (synth.stream.length === 0) return void 0;
5664
- const result = await transformStream(ctx, runtime, synth.stream, "provider");
6240
+ const { msgs, cacheControls } = payloadToCore(payload, fmt2);
6241
+ if (msgs.length === 0) return void 0;
6242
+ const result = await transformStreamCore(ctx, runtime, msgs, fmt2);
5665
6243
  if (!result) return void 0;
5666
- const wireOut = rebuildWirePayload(result.rebuilt, payload, synth);
5667
- const outMsgs = wireOut.messages?.length ?? 0;
6244
+ const outMsgs = coreToPayloadMessages(result.coreOut, fmt2, cacheControls).length;
5668
6245
  const inMsgs = payload.messages?.length ?? 0;
5669
- if (outMsgs !== inMsgs || wireOut !== payload) {
6246
+ if (outMsgs !== inMsgs) {
5670
6247
  logInfo("provider-transform", { sid, fmt: fmt2, inMsgs, outMsgs, nudge: result.nudgeInjected ? "injected" : "idle" });
5671
6248
  }
5672
6249
  debug.event("provider-transform", { sid, fmt: fmt2, inMsgs, outMsgs, nudgeInjected: result.nudgeInjected });
5673
- return wireOut === payload ? void 0 : wireOut;
6250
+ return { ...payload, messages: coreToPayloadMessages(result.coreOut, fmt2, cacheControls) };
5674
6251
  } catch (e) {
5675
6252
  logThrow("provider-transform", e, { sid, fmt: fmt2 });
5676
6253
  return void 0;
5677
6254
  }
5678
6255
  });
5679
6256
  }
6257
+ async function transformStreamCore(ctx, runtime, wireMsgs, fmt2) {
6258
+ const sid = ctx.sessionManager.getSessionId();
6259
+ const release = await runtime.acquireLock(sid);
6260
+ let result;
6261
+ try {
6262
+ if (wireMsgs.length === 0) {
6263
+ debug.event("empty-stream-bypass", { sid, space: "core" });
6264
+ return void 0;
6265
+ }
6266
+ debug.event("context-in-raw", { sid, msgs: wireMsgs.length, mode: "provider" });
6267
+ const { state, coreMessages, streamLen } = runtime.foldStreamCore(ctx, wireMsgs);
6268
+ const preTurnNudgeBaseline = state.nudge.lastPerMessageNudgeTokens;
6269
+ const preTurnNudgeShownTokens = state.nudge.lastNudgeShownTokens;
6270
+ const preTurnNudgeShownByTier = state.nudge.lastShownByTier;
6271
+ const config = runtime.configFor(ctx);
6272
+ const coveredIds = collectCoveredMessageIds(state);
6273
+ const systemPromptTokens = estimateTextTokens2(getSystemPromptText(ctx) ?? "");
6274
+ const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
6275
+ const sessionTokens = ctx.getContextUsage?.()?.tokens ?? null;
6276
+ const tokenCount = sentTokens;
6277
+ debug.event("context-in", {
6278
+ sid,
6279
+ mode: "provider",
6280
+ fmt: fmt2,
6281
+ streamLen,
6282
+ coreMsgs: coreMessages.length,
6283
+ tokenCount,
6284
+ sessionTokens,
6285
+ limit: config.modelContextLimit,
6286
+ blocksBefore: state.blocks.length,
6287
+ activeBefore: state.blocks.filter((b) => b.active).length
6288
+ });
6289
+ const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount, renderTags: "text-only" });
6290
+ runtime.commitFoldState(ctx, turn.state);
6291
+ logInfo("turn", {
6292
+ sid,
6293
+ inMsgs: coreMessages.length,
6294
+ outMsgs: turn.messages.length,
6295
+ tokens: tokenCount,
6296
+ sessionTokens,
6297
+ pct: config.modelContextLimit > 0 ? Math.round(tokenCount / config.modelContextLimit * 100) : null,
6298
+ limit: config.modelContextLimit,
6299
+ nudge: turn.nudge?.shouldInject ? turn.nudge.breakdown?.emergencyOverride === 1 ? "emergency" : "active" : "idle",
6300
+ nudgeReason: turn.nudge?.reason ?? null,
6301
+ blocks: turn.state.blocks.length,
6302
+ activeBlocks: turn.state.blocks.filter((b) => b.active).length
6303
+ });
6304
+ debug.event("processTurn", {
6305
+ outMsgs: turn.messages.length,
6306
+ summaryMsgs: turn.messages.filter((m) => m.id.startsWith("acp_summary")).length,
6307
+ prunedMsgs: coreMessages.length - turn.messages.length + turn.messages.filter((m) => m.id.startsWith("acp_summary")).length,
6308
+ nudgeShouldInject: turn.nudge?.shouldInject ?? false,
6309
+ nudgeReason: turn.nudge?.reason ?? null,
6310
+ nudgeVoice: turn.nudge ? renderNudgeText(turn.nudge, runtime.prompts).voice : null,
6311
+ nudgePct: turn.nudge ? Math.round(turn.nudge.contextUsage * 100) : null,
6312
+ nudgeTier: turn.nudge?.tier ?? null,
6313
+ nudgeCompressibleCount: turn.nudge?.compressibleRanges.length ?? 0,
6314
+ nudgeProtectedCount: turn.nudge?.protectedRanges?.length ?? 0,
6315
+ nothingToCompress: turn.nudge?.reason?.includes("nothing to compress") ?? false,
6316
+ blocksAfter: turn.state.blocks.length,
6317
+ activeAfter: turn.state.blocks.filter((b) => b.active).length
6318
+ });
6319
+ const coreOut = applyWireTagContract(
6320
+ turn.messages.filter((m) => !m.id.startsWith("acp_summary_")),
6321
+ turn.state,
6322
+ { config, tokenCount }
6323
+ );
6324
+ let nudgeInjected = false;
6325
+ if (turn.nudge?.shouldInject) {
6326
+ const lastUser = [...wireMsgs].reverse().find((m) => m.role === "user");
6327
+ const tailText = lastUser ? lastUser.text ?? "" : "";
6328
+ const isFeedbackView = tailText.includes("efficiency nudge to compress early") || tailText.includes("Context limit reached");
6329
+ if (isFeedbackView) {
6330
+ debug.event("nudge-feedback-skip", { sid, msgs: wireMsgs.length });
6331
+ } else {
6332
+ const emergency = turn.nudge.breakdown?.emergencyOverride === 1;
6333
+ const epochReset = turn.state.nudge.lastPerMessageNudgeTokens !== preTurnNudgeBaseline;
6334
+ const prevShown = epochReset ? 0 : preTurnNudgeShownTokens;
6335
+ const cadenceFloor = turn.nudge.breakdown?.growthFloor ?? 0;
6336
+ const suppressed = !emergency && prevShown > 0 && tokenCount - prevShown < cadenceFloor;
6337
+ if (suppressed) {
6338
+ turn.state.nudge.lastNudgeShownTokens = prevShown;
6339
+ turn.state.nudge.lastShownByTier = preTurnNudgeShownByTier;
6340
+ logInfo("nudge", { sid, event: "cadence-suppressed", growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
6341
+ debug.event("nudge-suppressed", { sid, growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
6342
+ } else {
6343
+ nudgeInjected = true;
6344
+ turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
6345
+ const rendered = renderNudgeText(turn.nudge, runtime.prompts);
6346
+ const top = [...turn.nudge.compressibleRanges].sort((a, b) => b.tokens - a.tokens)[0];
6347
+ const example = top ? `
6348
+
6349
+ Example: compress({ content: [{ startId: "${top.startRef}", endId: "${top.endRef}", summary: "..." }] })` : "";
6350
+ if (emergency) {
6351
+ logWarn("nudge", { sid, event: "emergency-inject", pct: Math.round(turn.nudge.contextUsage * 100), voice: rendered.voice, compressible: turn.nudge.compressibleRanges.length });
6352
+ }
6353
+ const debugOn2 = debug.enabled;
6354
+ if (debugOn2 && ctx.hasUI) {
6355
+ ctx.ui.notify(`[ACP nudge \u2192 context]${emergency ? " [EMERGENCY]" : ""}
6356
+ ${rendered.text}${example}`);
6357
+ }
6358
+ debug.event("nudge-injected", { sid, voice: rendered.voice, channels: ["wire", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
6359
+ coreOut.push({ id: `acp_nudge_${Date.now()}`, role: "user", contentType: "text", text: nudgeText(turn.nudge, turn.state.blocks.filter((b) => b.active), runtime.prompts, example) });
6360
+ }
6361
+ }
6362
+ }
6363
+ debug.event("core-out", { sid, coreOutMsgs: coreOut.length, space: "core", fmt: fmt2 });
6364
+ result = { coreOut, nudgeInjected };
6365
+ } catch (e) {
6366
+ logThrow("context-core", e, { sid, phase: "transform" });
6367
+ throw e;
6368
+ } finally {
6369
+ release();
6370
+ }
6371
+ void checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
6372
+ if (ctx.hasUI) ctx.ui.notify(msg);
6373
+ }).catch((e) => logThrow("update", e, { sid, phase: "provider" }));
6374
+ return result;
6375
+ }
5680
6376
  function wireSystemPrompt(pi, runtime) {
5681
6377
  pi.on("before_agent_start", (event) => {
5682
6378
  const acp = buildAcpSystemPrompt(runtime.prompts);
@@ -5708,7 +6404,7 @@ function wireProviderDebug(pi) {
5708
6404
  });
5709
6405
  });
5710
6406
  }
5711
- function nudgeMessage(nudge, blocks, prompts, example) {
6407
+ function nudgeText(nudge, blocks, prompts, example) {
5712
6408
  const rendered = renderNudgeText(nudge, prompts);
5713
6409
  const lines = [rendered.text];
5714
6410
  if (blocks.length > 0) {
@@ -5727,9 +6423,12 @@ function nudgeMessage(nudge, blocks, prompts, example) {
5727
6423
  lines.push(`Compressed blocks: ${blocks.length} active (${tierStr}) \u2014 ${fmt2(totalSummary)} summary, ${fmt2(totalCompressed)} original compressed. Blocks: ${ids}${extra}.`);
5728
6424
  }
5729
6425
  if (example) lines.push(example);
6426
+ return lines.join("\n");
6427
+ }
6428
+ function nudgeMessage(nudge, blocks, prompts, example) {
5730
6429
  return {
5731
6430
  role: "user",
5732
- content: [{ type: "text", text: lines.join("\n") }],
6431
+ content: [{ type: "text", text: nudgeText(nudge, blocks, prompts, example) }],
5733
6432
  timestamp: Date.now()
5734
6433
  };
5735
6434
  }