billion-context-omp 0.2.6 → 0.2.7
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/config.d.ts +14 -8
- package/dist/index.js +915 -279
- package/dist/index.js.map +1 -1
- package/dist/messages.d.ts +19 -0
- package/dist/runtime.d.ts +16 -1
- package/dist/transform-mode.d.ts +5 -0
- package/dist/wire-fold.d.ts +116 -0
- package/package.json +7 -7
- package/dist/wire-transform.d.ts +0 -71
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,7 +742,7 @@ 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 };
|
|
@@ -741,14 +754,27 @@ function renderVisibleRefs(messages, state, countTokens = (text) => Math.ceil(te
|
|
|
741
754
|
(message) => renderMessage(message, map, countTokens, strategy)
|
|
742
755
|
);
|
|
743
756
|
}
|
|
757
|
+
function renderWithSnapshot(messages, state, countTokens = (text) => Math.ceil(text.length / 4), strategy = "all") {
|
|
758
|
+
const map = state.messageRefs;
|
|
759
|
+
const snapshot = { ...state.tokenSnapshot ?? {} };
|
|
760
|
+
const rendered = messages.map(
|
|
761
|
+
(message) => renderMessage(message, map, countTokens, strategy, snapshot)
|
|
762
|
+
);
|
|
763
|
+
return { messages: rendered, tokenSnapshot: snapshot };
|
|
764
|
+
}
|
|
744
765
|
function createRenderRefsNode(strategy) {
|
|
745
766
|
return {
|
|
746
767
|
name: "render-refs",
|
|
747
768
|
run(io, ctx) {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
769
|
+
const { messages, tokenSnapshot } = renderWithSnapshot(
|
|
770
|
+
io.messages,
|
|
771
|
+
io.state,
|
|
772
|
+
ctx.countTokens,
|
|
773
|
+
strategy
|
|
774
|
+
);
|
|
775
|
+
const prev = io.state.tokenSnapshot;
|
|
776
|
+
const changed = !prev || Object.keys(tokenSnapshot).length !== Object.keys(prev).length;
|
|
777
|
+
return changed ? { ...io, messages, state: { ...io.state, tokenSnapshot } } : { ...io, messages };
|
|
752
778
|
}
|
|
753
779
|
};
|
|
754
780
|
}
|
|
@@ -944,6 +970,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
|
|
|
944
970
|
ref,
|
|
945
971
|
refNum: rn,
|
|
946
972
|
tokens: countTokens(msg.text ?? ""),
|
|
973
|
+
chars: (msg.text ?? "").length,
|
|
947
974
|
isTool: isToolMessage(msg),
|
|
948
975
|
isUser: msg.role === "user"
|
|
949
976
|
});
|
|
@@ -964,6 +991,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
|
|
|
964
991
|
endRef: info.ref,
|
|
965
992
|
count: 1,
|
|
966
993
|
tokens: info.tokens,
|
|
994
|
+
chars: info.chars,
|
|
967
995
|
toolPct: info.isTool ? 100 : 0,
|
|
968
996
|
textPct: info.isTool ? 0 : 100
|
|
969
997
|
};
|
|
@@ -971,6 +999,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
|
|
|
971
999
|
cur.endRef = info.ref;
|
|
972
1000
|
cur.count++;
|
|
973
1001
|
cur.tokens += info.tokens;
|
|
1002
|
+
cur.chars = (cur.chars ?? 0) + info.chars;
|
|
974
1003
|
if (info.isTool) {
|
|
975
1004
|
cur.toolPct = Math.round((cur.toolPct * (cur.count - 1) + 100) / cur.count);
|
|
976
1005
|
} else {
|
|
@@ -1018,6 +1047,7 @@ function mergeBatch(batch) {
|
|
|
1018
1047
|
const last = batch[batch.length - 1];
|
|
1019
1048
|
const count = batch.reduce((s, r) => s + r.count, 0);
|
|
1020
1049
|
const tokens = batch.reduce((s, r) => s + r.tokens, 0);
|
|
1050
|
+
const chars = batch.reduce((s, r) => s + rangeChars(r), 0);
|
|
1021
1051
|
const toolPct = Math.round(
|
|
1022
1052
|
batch.reduce((s, r) => s + r.toolPct * r.count, 0) / count
|
|
1023
1053
|
);
|
|
@@ -1026,6 +1056,7 @@ function mergeBatch(batch) {
|
|
|
1026
1056
|
endRef: last.endRef,
|
|
1027
1057
|
count,
|
|
1028
1058
|
tokens,
|
|
1059
|
+
chars,
|
|
1029
1060
|
toolPct,
|
|
1030
1061
|
textPct: 100 - toolPct
|
|
1031
1062
|
};
|
|
@@ -1034,16 +1065,21 @@ function mergeBatch(batch) {
|
|
|
1034
1065
|
}
|
|
1035
1066
|
return merged;
|
|
1036
1067
|
}
|
|
1068
|
+
function rangeChars(r) {
|
|
1069
|
+
return r.chars ?? r.tokens * 4;
|
|
1070
|
+
}
|
|
1037
1071
|
function mergeRangesToThreshold(ranges, minChars) {
|
|
1038
1072
|
if (minChars <= 0 || ranges.length === 0) return ranges;
|
|
1039
1073
|
const result = [];
|
|
1040
1074
|
let batch = [];
|
|
1075
|
+
let batchChars = 0;
|
|
1041
1076
|
for (const r of ranges) {
|
|
1042
1077
|
batch.push(r);
|
|
1043
|
-
|
|
1044
|
-
if (
|
|
1078
|
+
batchChars += rangeChars(r);
|
|
1079
|
+
if (batchChars >= minChars) {
|
|
1045
1080
|
result.push(mergeBatch(batch));
|
|
1046
1081
|
batch = [];
|
|
1082
|
+
batchChars = 0;
|
|
1047
1083
|
}
|
|
1048
1084
|
}
|
|
1049
1085
|
if (batch.length > 0) {
|
|
@@ -1631,7 +1667,7 @@ function resolveAdaptiveGrowth(modelContextLimit, nudge) {
|
|
|
1631
1667
|
function pendingByTier(state, recommendation, countTokens, minCompressRange) {
|
|
1632
1668
|
const out = {};
|
|
1633
1669
|
const merged = recommendation?.recommendedRanges ?? [];
|
|
1634
|
-
const effective = minCompressRange > 0 ? merged.filter((r) => r.tokens * 4 >= minCompressRange) : merged;
|
|
1670
|
+
const effective = minCompressRange > 0 ? merged.filter((r) => (r.chars ?? r.tokens * 4) >= minCompressRange) : merged;
|
|
1635
1671
|
out[1] = { pending: effective.reduce((s, r) => s + r.tokens, 0), targetBlocks: [] };
|
|
1636
1672
|
const active = activeBlocks(state);
|
|
1637
1673
|
const t1 = active.filter((b) => b.tier === 1);
|
|
@@ -1793,6 +1829,7 @@ function cloneState(state) {
|
|
|
1793
1829
|
byRaw: { ...state.messageRefs.byRaw },
|
|
1794
1830
|
byRef: { ...state.messageRefs.byRef }
|
|
1795
1831
|
},
|
|
1832
|
+
tokenSnapshot: { ...state.tokenSnapshot ?? {} },
|
|
1796
1833
|
nudge: { ...state.nudge, anchors: { ...state.nudge.anchors } },
|
|
1797
1834
|
stats: { ...state.stats },
|
|
1798
1835
|
nextBlockId: state.nextBlockId,
|
|
@@ -2757,8 +2794,12 @@ function resolveConfig(adapter, liveContextLimit) {
|
|
|
2757
2794
|
if (c?.maxContextLimit !== void 0) config.nudge.maxContextLimitPct = parsePercent(c.maxContextLimit);
|
|
2758
2795
|
if (c?.emergencyThresholdPercent !== void 0) {
|
|
2759
2796
|
const pct2 = parsePercent(c.emergencyThresholdPercent);
|
|
2760
|
-
|
|
2761
|
-
|
|
2797
|
+
if (pct2 <= 0) {
|
|
2798
|
+
logWarn("config", { event: "emergency-threshold-ignored", value: String(c.emergencyThresholdPercent), reason: "zero threshold would truncate every turn" });
|
|
2799
|
+
} else {
|
|
2800
|
+
config.nudge.emergencyThresholdPct = pct2;
|
|
2801
|
+
config.truncate.threshold = pct2;
|
|
2802
|
+
}
|
|
2762
2803
|
}
|
|
2763
2804
|
if (c?.nudgeGrowthTokens !== void 0) {
|
|
2764
2805
|
config.nudge.growthFloor = c.nudgeGrowthTokens;
|
|
@@ -2863,8 +2904,396 @@ When context usage passes a threshold, the system appends a breakdown showing wh
|
|
|
2863
2904
|
`;
|
|
2864
2905
|
}
|
|
2865
2906
|
|
|
2866
|
-
// src/
|
|
2907
|
+
// src/transform-mode.ts
|
|
2908
|
+
import { VERSION } from "@oh-my-pi/pi-utils";
|
|
2909
|
+
var PROVIDER_VIABLE_APIS = /* @__PURE__ */ new Set(["anthropic-messages", "ollama-chat"]);
|
|
2910
|
+
var OPENAI_COMPLETIONS_VIABLE_FROM = [17, 3, 8];
|
|
2911
|
+
function hostVersionAtLeast(min, version = VERSION) {
|
|
2912
|
+
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(version ?? "");
|
|
2913
|
+
if (!m) return false;
|
|
2914
|
+
const v = [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
2915
|
+
return v[0] > min[0] || v[0] === min[0] && (v[1] > min[1] || v[1] === min[1] && v[2] >= min[2]);
|
|
2916
|
+
}
|
|
2917
|
+
function resolveTransformMode(adapter, model, hostVersion = VERSION) {
|
|
2918
|
+
if (adapter.transformMode) return adapter.transformMode;
|
|
2919
|
+
const api = model?.api;
|
|
2920
|
+
if (api == null) return "context";
|
|
2921
|
+
if (PROVIDER_VIABLE_APIS.has(api)) return "provider";
|
|
2922
|
+
if (api === "openai-completions" && hostVersionAtLeast(OPENAI_COMPLETIONS_VIABLE_FROM, hostVersion)) return "provider";
|
|
2923
|
+
return "context";
|
|
2924
|
+
}
|
|
2925
|
+
|
|
2926
|
+
// node_modules/acp-kernel/dist/wire/index.js
|
|
2867
2927
|
import { createHash } from "crypto";
|
|
2928
|
+
function hashId(s) {
|
|
2929
|
+
return createHash("sha256").update(s, "utf8").digest("hex").slice(0, 16);
|
|
2930
|
+
}
|
|
2931
|
+
function deriveMessageId(role, contentType, text, options = {}) {
|
|
2932
|
+
const seed = `${role}|${contentType}|${options.toolCallId ?? ""}|${options.toolName ?? ""}|${text}`;
|
|
2933
|
+
return "h_" + hashId(seed);
|
|
2934
|
+
}
|
|
2935
|
+
var ClusterCounter = class {
|
|
2936
|
+
counts = /* @__PURE__ */ new Map();
|
|
2937
|
+
next(baseId) {
|
|
2938
|
+
const n = this.counts.get(baseId) ?? 0;
|
|
2939
|
+
this.counts.set(baseId, n + 1);
|
|
2940
|
+
return n === 0 ? baseId : `${baseId}_${n}`;
|
|
2941
|
+
}
|
|
2942
|
+
};
|
|
2943
|
+
function anthropicToCore(body) {
|
|
2944
|
+
const msgs = [];
|
|
2945
|
+
const cacheControls = /* @__PURE__ */ new Map();
|
|
2946
|
+
const clusters = new ClusterCounter();
|
|
2947
|
+
for (const m of body.messages) {
|
|
2948
|
+
const blocks = typeof m.content === "string" ? [{ type: "text", text: m.content }] : m.content;
|
|
2949
|
+
for (const b of blocks) {
|
|
2950
|
+
switch (b.type) {
|
|
2951
|
+
case "text": {
|
|
2952
|
+
const base = deriveMessageId(m.role, "text", b.text);
|
|
2953
|
+
const id = clusters.next(base);
|
|
2954
|
+
msgs.push({ id, role: m.role, contentType: "text", text: b.text });
|
|
2955
|
+
if (b.cache_control) cacheControls.set(id, b.cache_control);
|
|
2956
|
+
break;
|
|
2957
|
+
}
|
|
2958
|
+
case "tool_use": {
|
|
2959
|
+
const base = deriveMessageId("assistant", "tool-call", safeStringify(b.input), {
|
|
2960
|
+
toolCallId: b.id,
|
|
2961
|
+
toolName: b.name
|
|
2962
|
+
});
|
|
2963
|
+
const id = clusters.next(base);
|
|
2964
|
+
msgs.push({
|
|
2965
|
+
id,
|
|
2966
|
+
role: "assistant",
|
|
2967
|
+
contentType: "tool-call",
|
|
2968
|
+
toolName: b.name,
|
|
2969
|
+
toolCallId: b.id,
|
|
2970
|
+
text: safeStringify(b.input)
|
|
2971
|
+
});
|
|
2972
|
+
if (b.cache_control) cacheControls.set(id, b.cache_control);
|
|
2973
|
+
break;
|
|
2974
|
+
}
|
|
2975
|
+
case "tool_result": {
|
|
2976
|
+
const text = typeof b.content === "string" ? b.content : b.content.map((c) => c.text).join("\n");
|
|
2977
|
+
const base = deriveMessageId("tool", "tool-result", text, { toolCallId: b.tool_use_id });
|
|
2978
|
+
const id = clusters.next(base);
|
|
2979
|
+
msgs.push({
|
|
2980
|
+
id,
|
|
2981
|
+
role: "tool",
|
|
2982
|
+
contentType: "tool-result",
|
|
2983
|
+
toolCallId: b.tool_use_id,
|
|
2984
|
+
text,
|
|
2985
|
+
...b.is_error === true ? { toolIsError: true } : {}
|
|
2986
|
+
});
|
|
2987
|
+
if (b.cache_control) cacheControls.set(id, b.cache_control);
|
|
2988
|
+
break;
|
|
2989
|
+
}
|
|
2990
|
+
case "thinking": {
|
|
2991
|
+
const base = deriveMessageId("assistant", "reasoning", b.thinking);
|
|
2992
|
+
msgs.push({
|
|
2993
|
+
id: clusters.next(base),
|
|
2994
|
+
role: "assistant",
|
|
2995
|
+
contentType: "reasoning",
|
|
2996
|
+
text: b.thinking,
|
|
2997
|
+
...b.signature ? { thinkingSignature: b.signature } : {}
|
|
2998
|
+
});
|
|
2999
|
+
break;
|
|
3000
|
+
}
|
|
3001
|
+
case "image": {
|
|
3002
|
+
const base = deriveMessageId(m.role, "text", "[image]");
|
|
3003
|
+
msgs.push({
|
|
3004
|
+
id: clusters.next(base),
|
|
3005
|
+
role: m.role,
|
|
3006
|
+
contentType: "text",
|
|
3007
|
+
text: "[image]",
|
|
3008
|
+
rawAnthropicBlock: b
|
|
3009
|
+
});
|
|
3010
|
+
break;
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
return { msgs, cacheControls };
|
|
3016
|
+
}
|
|
3017
|
+
function coreToAnthropic(messages, cacheControls) {
|
|
3018
|
+
const out = [];
|
|
3019
|
+
let current = null;
|
|
3020
|
+
const flush = () => {
|
|
3021
|
+
if (current && current.blocks.length > 0) {
|
|
3022
|
+
out.push({ role: current.role, content: current.blocks });
|
|
3023
|
+
}
|
|
3024
|
+
current = null;
|
|
3025
|
+
};
|
|
3026
|
+
const cc = (id) => {
|
|
3027
|
+
const v = cacheControls?.get(id);
|
|
3028
|
+
return v ? { cache_control: v } : {};
|
|
3029
|
+
};
|
|
3030
|
+
for (const m of messages) {
|
|
3031
|
+
const target = m.role === "assistant" ? "assistant" : "user";
|
|
3032
|
+
if (!current || current.role !== target) {
|
|
3033
|
+
flush();
|
|
3034
|
+
current = { role: target, blocks: [] };
|
|
3035
|
+
}
|
|
3036
|
+
switch (m.contentType) {
|
|
3037
|
+
case "text": {
|
|
3038
|
+
if (m.rawAnthropicBlock) {
|
|
3039
|
+
current.blocks.push(m.rawAnthropicBlock);
|
|
3040
|
+
break;
|
|
3041
|
+
}
|
|
3042
|
+
current.blocks.push({ type: "text", text: m.text ?? "", ...cc(m.id) });
|
|
3043
|
+
break;
|
|
3044
|
+
}
|
|
3045
|
+
case "tool-call":
|
|
3046
|
+
current.blocks.push({
|
|
3047
|
+
type: "tool_use",
|
|
3048
|
+
id: m.toolCallId ?? `call_${m.id}`,
|
|
3049
|
+
name: m.toolName ?? "unknown",
|
|
3050
|
+
input: safeParse(m.text),
|
|
3051
|
+
...cc(m.id)
|
|
3052
|
+
});
|
|
3053
|
+
break;
|
|
3054
|
+
case "tool-result":
|
|
3055
|
+
current.blocks.push({
|
|
3056
|
+
type: "tool_result",
|
|
3057
|
+
tool_use_id: m.toolCallId ?? "",
|
|
3058
|
+
content: m.text ?? "",
|
|
3059
|
+
...m.toolIsError ? { is_error: true } : {},
|
|
3060
|
+
...cc(m.id)
|
|
3061
|
+
});
|
|
3062
|
+
break;
|
|
3063
|
+
case "reasoning":
|
|
3064
|
+
current.blocks.push({
|
|
3065
|
+
type: "thinking",
|
|
3066
|
+
thinking: m.text ?? "",
|
|
3067
|
+
...m.thinkingSignature ? { signature: m.thinkingSignature } : {}
|
|
3068
|
+
});
|
|
3069
|
+
break;
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3072
|
+
flush();
|
|
3073
|
+
return out;
|
|
3074
|
+
}
|
|
3075
|
+
function safeStringify(v) {
|
|
3076
|
+
try {
|
|
3077
|
+
return JSON.stringify(v ?? {});
|
|
3078
|
+
} catch {
|
|
3079
|
+
return "{}";
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
function safeParse(s) {
|
|
3083
|
+
if (!s) return {};
|
|
3084
|
+
try {
|
|
3085
|
+
return JSON.parse(s);
|
|
3086
|
+
} catch {
|
|
3087
|
+
return {};
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
function parseDataUrl(url) {
|
|
3091
|
+
const m = /^data:([^;,]+)(?:;base64)?,(.+)$/i.exec(url);
|
|
3092
|
+
if (!m) return void 0;
|
|
3093
|
+
return { mediaType: m[1], base64: m[2] };
|
|
3094
|
+
}
|
|
3095
|
+
function openaiToCore(body) {
|
|
3096
|
+
const msgs = [];
|
|
3097
|
+
const clusters = new ClusterCounter();
|
|
3098
|
+
for (const m of body.messages) {
|
|
3099
|
+
switch (m.role) {
|
|
3100
|
+
case "system":
|
|
3101
|
+
case "developer": {
|
|
3102
|
+
const base = deriveMessageId(m.role, "text", stringContent(m.content));
|
|
3103
|
+
msgs.push({ id: clusters.next(base), role: "system", contentType: "text", text: stringContent(m.content), originalRole: m.role });
|
|
3104
|
+
break;
|
|
3105
|
+
}
|
|
3106
|
+
case "user": {
|
|
3107
|
+
const text = stringContent(m.content);
|
|
3108
|
+
const img = firstImagePart(m.content);
|
|
3109
|
+
const base = deriveMessageId("user", "text", text);
|
|
3110
|
+
msgs.push({
|
|
3111
|
+
id: clusters.next(base),
|
|
3112
|
+
role: "user",
|
|
3113
|
+
contentType: "text",
|
|
3114
|
+
text,
|
|
3115
|
+
...img ? { rawOpenaiContent: img.part, imageMediaType: img.mediaType, imageBase64: img.base64 } : {}
|
|
3116
|
+
});
|
|
3117
|
+
break;
|
|
3118
|
+
}
|
|
3119
|
+
case "assistant": {
|
|
3120
|
+
const reasoning = typeof m.reasoning_content === "string" ? m.reasoning_content : "";
|
|
3121
|
+
if (reasoning) {
|
|
3122
|
+
const base = deriveMessageId("assistant", "reasoning", reasoning);
|
|
3123
|
+
msgs.push({
|
|
3124
|
+
id: clusters.next(base),
|
|
3125
|
+
role: "assistant",
|
|
3126
|
+
contentType: "reasoning",
|
|
3127
|
+
text: reasoning,
|
|
3128
|
+
reasoningContent: reasoning
|
|
3129
|
+
});
|
|
3130
|
+
}
|
|
3131
|
+
const text = stringContent(m.content);
|
|
3132
|
+
if (text) {
|
|
3133
|
+
const base = deriveMessageId("assistant", "text", text);
|
|
3134
|
+
msgs.push({ id: clusters.next(base), role: "assistant", contentType: "text", text });
|
|
3135
|
+
}
|
|
3136
|
+
if (Array.isArray(m.tool_calls)) {
|
|
3137
|
+
for (const tc of m.tool_calls) {
|
|
3138
|
+
const base = deriveMessageId("assistant", "tool-call", tc.function.arguments ?? "", {
|
|
3139
|
+
toolCallId: tc.id,
|
|
3140
|
+
toolName: tc.function.name
|
|
3141
|
+
});
|
|
3142
|
+
msgs.push({
|
|
3143
|
+
id: clusters.next(base),
|
|
3144
|
+
role: "assistant",
|
|
3145
|
+
contentType: "tool-call",
|
|
3146
|
+
toolName: tc.function.name,
|
|
3147
|
+
toolCallId: tc.id,
|
|
3148
|
+
text: tc.function.arguments ?? ""
|
|
3149
|
+
});
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
break;
|
|
3153
|
+
}
|
|
3154
|
+
case "tool": {
|
|
3155
|
+
const base = deriveMessageId("tool", "tool-result", stringContent(m.content), {
|
|
3156
|
+
toolCallId: m.tool_call_id ?? ""
|
|
3157
|
+
});
|
|
3158
|
+
msgs.push({
|
|
3159
|
+
id: clusters.next(base),
|
|
3160
|
+
role: "tool",
|
|
3161
|
+
contentType: "tool-result",
|
|
3162
|
+
toolCallId: m.tool_call_id ?? "",
|
|
3163
|
+
text: stringContent(m.content)
|
|
3164
|
+
});
|
|
3165
|
+
break;
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
return { msgs };
|
|
3170
|
+
}
|
|
3171
|
+
function coreToOpenai(messages) {
|
|
3172
|
+
const out = [];
|
|
3173
|
+
let pending = null;
|
|
3174
|
+
const flush = () => {
|
|
3175
|
+
if (!pending) return;
|
|
3176
|
+
const reasoning = pending.reasoning !== null && pending.reasoning.length > 0 ? pending.reasoning : void 0;
|
|
3177
|
+
if (pending.toolCalls.length > 0) {
|
|
3178
|
+
out.push({
|
|
3179
|
+
role: "assistant",
|
|
3180
|
+
content: pending.text ?? null,
|
|
3181
|
+
tool_calls: pending.toolCalls,
|
|
3182
|
+
...reasoning ? { reasoning_content: reasoning } : {}
|
|
3183
|
+
});
|
|
3184
|
+
} else if (pending.text !== null) {
|
|
3185
|
+
out.push({ role: "assistant", content: pending.text, ...reasoning ? { reasoning_content: reasoning } : {} });
|
|
3186
|
+
} else if (reasoning) {
|
|
3187
|
+
out.push({ role: "assistant", content: null, reasoning_content: reasoning });
|
|
3188
|
+
}
|
|
3189
|
+
pending = null;
|
|
3190
|
+
};
|
|
3191
|
+
for (const m of messages) {
|
|
3192
|
+
if (m.role === "assistant") {
|
|
3193
|
+
if (!pending) pending = { text: null, toolCalls: [], reasoning: null };
|
|
3194
|
+
if (m.contentType === "reasoning") {
|
|
3195
|
+
pending.reasoning = (pending.reasoning ?? "") + (m.reasoningContent ?? m.text ?? "");
|
|
3196
|
+
} else if (m.contentType === "text") {
|
|
3197
|
+
pending.text = (pending.text ?? "") + (m.text ?? "");
|
|
3198
|
+
} else if (m.contentType === "tool-call") {
|
|
3199
|
+
pending.toolCalls.push({
|
|
3200
|
+
id: m.toolCallId ?? `call_${m.id}`,
|
|
3201
|
+
type: "function",
|
|
3202
|
+
function: { name: m.toolName ?? "unknown", arguments: m.text ?? "" }
|
|
3203
|
+
});
|
|
3204
|
+
}
|
|
3205
|
+
} else {
|
|
3206
|
+
flush();
|
|
3207
|
+
if (m.role === "system") {
|
|
3208
|
+
out.push({ role: m.originalRole === "developer" ? "developer" : "system", content: m.text ?? "" });
|
|
3209
|
+
} else if (m.role === "user") {
|
|
3210
|
+
if (m.rawOpenaiContent || m.imageBase64) {
|
|
3211
|
+
const parts = [];
|
|
3212
|
+
if (m.text) parts.push({ type: "text", text: m.text });
|
|
3213
|
+
if (m.rawOpenaiContent) {
|
|
3214
|
+
parts.push(m.rawOpenaiContent);
|
|
3215
|
+
} else if (m.imageBase64 && m.imageMediaType) {
|
|
3216
|
+
parts.push({ type: "image_url", image_url: { url: `data:${m.imageMediaType};base64,${m.imageBase64}` } });
|
|
3217
|
+
}
|
|
3218
|
+
out.push({ role: "user", content: parts });
|
|
3219
|
+
} else {
|
|
3220
|
+
out.push({ role: "user", content: m.text ?? "" });
|
|
3221
|
+
}
|
|
3222
|
+
} else if (m.role === "tool") {
|
|
3223
|
+
out.push({ role: "tool", tool_call_id: m.toolCallId ?? "", content: m.text ?? "" });
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
flush();
|
|
3228
|
+
return out;
|
|
3229
|
+
}
|
|
3230
|
+
function stringContent(content) {
|
|
3231
|
+
if (content == null) return "";
|
|
3232
|
+
if (typeof content === "string") return content;
|
|
3233
|
+
if (Array.isArray(content)) {
|
|
3234
|
+
return content.map((p) => typeof p === "string" ? p : p.type === "text" ? p.text ?? "" : "").join("\n");
|
|
3235
|
+
}
|
|
3236
|
+
return "";
|
|
3237
|
+
}
|
|
3238
|
+
function firstImagePart(content) {
|
|
3239
|
+
if (!Array.isArray(content)) return void 0;
|
|
3240
|
+
for (const p of content) {
|
|
3241
|
+
if (p && typeof p === "object" && p.type === "image_url") {
|
|
3242
|
+
const iu = p.image_url;
|
|
3243
|
+
const url = iu?.url;
|
|
3244
|
+
if (typeof url === "string") {
|
|
3245
|
+
const parsed = parseDataUrl(url);
|
|
3246
|
+
if (parsed) return { part: p, mediaType: parsed.mediaType, base64: parsed.base64 };
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
return void 0;
|
|
3251
|
+
}
|
|
3252
|
+
function createSubagentNamespaces() {
|
|
3253
|
+
const anchors = /* @__PURE__ */ new Map();
|
|
3254
|
+
return {
|
|
3255
|
+
namespaceFor(identityValue, instructions) {
|
|
3256
|
+
if (typeof instructions !== "string" || instructions.trim().length === 0) return identityValue;
|
|
3257
|
+
const fp = hashId(instructions);
|
|
3258
|
+
const anchor = anchors.get(identityValue);
|
|
3259
|
+
if (anchor === void 0) {
|
|
3260
|
+
anchors.set(identityValue, fp);
|
|
3261
|
+
return identityValue;
|
|
3262
|
+
}
|
|
3263
|
+
return anchor === fp ? identityValue : `${identityValue}|sub:${fp}`;
|
|
3264
|
+
}
|
|
3265
|
+
};
|
|
3266
|
+
}
|
|
3267
|
+
var defaultNamespaces = createSubagentNamespaces();
|
|
3268
|
+
function detectWireFormat(payload) {
|
|
3269
|
+
if (payload === null || typeof payload !== "object") return void 0;
|
|
3270
|
+
const p = payload;
|
|
3271
|
+
if (Array.isArray(p.input)) return "responses";
|
|
3272
|
+
const messages = p.messages;
|
|
3273
|
+
if (!Array.isArray(messages)) return void 0;
|
|
3274
|
+
if ("system" in p || "anthropic_version" in p) return "anthropic";
|
|
3275
|
+
for (const m of messages) {
|
|
3276
|
+
if (m === null || typeof m !== "object") continue;
|
|
3277
|
+
const c = m.content;
|
|
3278
|
+
if (Array.isArray(c)) {
|
|
3279
|
+
for (const b of c) {
|
|
3280
|
+
if (b && typeof b === "object" && typeof b.type === "string") {
|
|
3281
|
+
if (b.type === "tool_use" || b.type === "tool_result" || b.type === "thinking")
|
|
3282
|
+
return "anthropic";
|
|
3283
|
+
if (b.type === "text" && "cache_control" in b) return "anthropic";
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
if (Array.isArray(m.tool_calls)) return "openai";
|
|
3288
|
+
if (m.role === "tool" && typeof m.tool_call_id === "string")
|
|
3289
|
+
return "openai";
|
|
3290
|
+
if (m.role === "system" || m.role === "developer") return "openai";
|
|
3291
|
+
}
|
|
3292
|
+
return "openai";
|
|
3293
|
+
}
|
|
3294
|
+
|
|
3295
|
+
// src/messages.ts
|
|
3296
|
+
import { createHash as createHash2 } from "crypto";
|
|
2868
3297
|
var REF_TAG_SOURCE = "(?:<acp\\s[^>]*>m\\d+</acp>|\\[m\\d+\\])";
|
|
2869
3298
|
var REF_TAG = new RegExp(`^${REF_TAG_SOURCE}\\s?\\n?`);
|
|
2870
3299
|
var TRAILING_REF_TAG = new RegExp(`\\n*${REF_TAG_SOURCE}\\s*$`);
|
|
@@ -3004,7 +3433,7 @@ function fallbackText(msg) {
|
|
|
3004
3433
|
function stringifyArgs(args) {
|
|
3005
3434
|
if (!args) return "";
|
|
3006
3435
|
if (typeof args === "string") return args;
|
|
3007
|
-
return
|
|
3436
|
+
return safeStringify2(args);
|
|
3008
3437
|
}
|
|
3009
3438
|
function extractText(content, stripTags = true) {
|
|
3010
3439
|
const clean = stripTags ? stripRefTag : (s) => s;
|
|
@@ -3060,7 +3489,7 @@ function allToolCalls(content) {
|
|
|
3060
3489
|
}
|
|
3061
3490
|
return calls;
|
|
3062
3491
|
}
|
|
3063
|
-
function
|
|
3492
|
+
function safeStringify2(value) {
|
|
3064
3493
|
try {
|
|
3065
3494
|
return JSON.stringify(value);
|
|
3066
3495
|
} catch {
|
|
@@ -3218,7 +3647,7 @@ function spanFingerprint(coreMessages, startId, endId) {
|
|
|
3218
3647
|
const first = find(startId);
|
|
3219
3648
|
const last = find(endId);
|
|
3220
3649
|
if (!first || !last) return "";
|
|
3221
|
-
return
|
|
3650
|
+
return createHash2("sha1").update(`${key(first)}\0${key(last)}`).digest("hex").slice(0, 8);
|
|
3222
3651
|
}
|
|
3223
3652
|
function isBlockRef(ref) {
|
|
3224
3653
|
return /^b\d+$/i.test(ref.trim());
|
|
@@ -3247,249 +3676,179 @@ function rangeFingerprints(ranges, coreMessages, byRef, blocks) {
|
|
|
3247
3676
|
});
|
|
3248
3677
|
}
|
|
3249
3678
|
|
|
3250
|
-
// src/wire-
|
|
3251
|
-
|
|
3252
|
-
function
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
if (
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3679
|
+
// src/wire-fold.ts
|
|
3680
|
+
import { createHash as createHash3 } from "crypto";
|
|
3681
|
+
function detectProviderWireFormat(payload) {
|
|
3682
|
+
const fmt2 = detectWireFormat(payload);
|
|
3683
|
+
return fmt2 === "anthropic" || fmt2 === "openai" ? fmt2 : null;
|
|
3684
|
+
}
|
|
3685
|
+
function payloadToCore(payload, fmt2) {
|
|
3686
|
+
if (fmt2 === "anthropic") {
|
|
3687
|
+
const { msgs: msgs2, cacheControls } = anthropicToCore(payload);
|
|
3688
|
+
return { msgs: msgs2, cacheControls };
|
|
3689
|
+
}
|
|
3690
|
+
const { msgs } = openaiToCore(payload);
|
|
3691
|
+
return { msgs };
|
|
3692
|
+
}
|
|
3693
|
+
function coreToPayloadMessages(msgs, fmt2, cacheControls) {
|
|
3694
|
+
return fmt2 === "anthropic" ? coreToAnthropic(msgs, cacheControls) : coreToOpenai(msgs);
|
|
3695
|
+
}
|
|
3696
|
+
function applyWireTagContract(msgs, state) {
|
|
3697
|
+
const toolResults = msgs.filter((m) => m.contentType === "tool-result");
|
|
3698
|
+
const taggedTools = toolResults.length > 0 ? renderVisibleRefs(toolResults, state, defaultCountTokens, "all") : [];
|
|
3699
|
+
const bySource = new Map(toolResults.map((m, i) => [m, taggedTools[i]]));
|
|
3700
|
+
return msgs.map((m) => {
|
|
3701
|
+
if (m.contentType === "tool-result") return bySource.get(m) ?? m;
|
|
3702
|
+
if (m.contentType === "text" && m.role === "assistant") return { ...m, text: stripRefTag(m.text ?? "") };
|
|
3703
|
+
return m;
|
|
3704
|
+
});
|
|
3705
|
+
}
|
|
3706
|
+
function coreIdentity(msg) {
|
|
3707
|
+
return JSON.stringify({
|
|
3708
|
+
role: msg.role,
|
|
3709
|
+
contentType: msg.contentType,
|
|
3710
|
+
toolName: msg.toolName ?? null,
|
|
3711
|
+
toolCallId: msg.toolCallId ?? null,
|
|
3712
|
+
text: stripRefTag(msg.text ?? "")
|
|
3713
|
+
});
|
|
3714
|
+
}
|
|
3715
|
+
function toolCallNames(msgs) {
|
|
3716
|
+
const names = /* @__PURE__ */ new Map();
|
|
3717
|
+
for (const m of msgs) {
|
|
3718
|
+
if (m.contentType === "tool-call" && m.toolCallId && m.toolName) names.set(m.toolCallId, m.toolName);
|
|
3272
3719
|
}
|
|
3273
|
-
return
|
|
3720
|
+
return names;
|
|
3274
3721
|
}
|
|
3275
|
-
function
|
|
3276
|
-
|
|
3722
|
+
function toolResultTextsCore(msgs) {
|
|
3723
|
+
const results = /* @__PURE__ */ new Map();
|
|
3724
|
+
for (const m of msgs) {
|
|
3725
|
+
if (m.contentType !== "tool-result" || !m.toolCallId) continue;
|
|
3726
|
+
results.set(m.toolCallId, m.text ?? "");
|
|
3727
|
+
}
|
|
3728
|
+
return results;
|
|
3277
3729
|
}
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
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;
|
|
3338
|
-
}
|
|
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");
|
|
3730
|
+
function findCompressCallsCore(msg) {
|
|
3731
|
+
if (msg.contentType !== "tool-call" || !msg.toolName) return [];
|
|
3732
|
+
const args = compressToolArgs({ name: msg.toolName, arguments: msg.text });
|
|
3733
|
+
if (!args) return [];
|
|
3734
|
+
const content = args.content;
|
|
3735
|
+
if (!Array.isArray(content)) return [];
|
|
3736
|
+
const ranges = [];
|
|
3737
|
+
const callTopic = typeof args.topic === "string" ? args.topic : void 0;
|
|
3738
|
+
for (const item of content) {
|
|
3739
|
+
const r = item;
|
|
3740
|
+
if (typeof r.startId !== "string" || typeof r.endId !== "string" || typeof r.summary !== "string" || r.summary.length === 0) continue;
|
|
3741
|
+
ranges.push({
|
|
3742
|
+
startRef: r.startId,
|
|
3743
|
+
endRef: r.endId,
|
|
3744
|
+
summary: r.summary,
|
|
3745
|
+
topic: typeof r.topic === "string" ? r.topic : callTopic,
|
|
3746
|
+
summaryMaxChars: typeof args.summaryMaxChars === "number" ? args.summaryMaxChars : void 0,
|
|
3747
|
+
compressCallId: msg.toolCallId ?? ""
|
|
3341
3748
|
});
|
|
3342
|
-
|
|
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
|
-
}
|
|
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;
|
|
3390
|
-
}
|
|
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 };
|
|
3749
|
+
}
|
|
3750
|
+
return ranges.length > 0 ? [{ id: msg.toolCallId ?? "", ranges }] : [];
|
|
3395
3751
|
}
|
|
3396
|
-
function
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
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;
|
|
3752
|
+
function corePieceKey(cm) {
|
|
3753
|
+
return `${cm.role}|${cm.contentType}|${cm.toolName ?? ""}|${(cm.text ?? "").slice(0, 4096)}`;
|
|
3754
|
+
}
|
|
3755
|
+
function spanFingerprintCoreIdx(coreMessages, startIdx, endIdx) {
|
|
3756
|
+
const first = coreMessages[startIdx];
|
|
3757
|
+
const last = coreMessages[endIdx];
|
|
3758
|
+
if (!first || !last) return "";
|
|
3759
|
+
return createHash3("sha1").update(`${corePieceKey(first)}\0${corePieceKey(last)}`).digest("hex").slice(0, 8);
|
|
3760
|
+
}
|
|
3761
|
+
function boundaryRawCore(ref, byRef, blocks, coreMessages, pick) {
|
|
3762
|
+
const raw = byRef[ref];
|
|
3763
|
+
if (raw) return raw;
|
|
3764
|
+
const m = /^b(\d+)$/i.exec(ref.trim());
|
|
3765
|
+
if (!m) return "";
|
|
3766
|
+
const block = blocks.find((b) => b.blockId.toLowerCase() === `b${m[1]}`);
|
|
3767
|
+
if (!block) return "";
|
|
3768
|
+
const idx = (id) => coreMessages.findIndex((cm) => cm.id === (byRef[id] ?? id));
|
|
3769
|
+
let best = -1;
|
|
3770
|
+
for (const id of block.effectiveMessageIds) {
|
|
3771
|
+
const i = idx(id);
|
|
3772
|
+
if (i < 0) continue;
|
|
3773
|
+
if (best < 0 || (pick === "min" ? i < best : i > best)) best = i;
|
|
3774
|
+
}
|
|
3775
|
+
return best < 0 ? "" : coreMessages[best]?.id ?? "";
|
|
3776
|
+
}
|
|
3777
|
+
function boundaryIndexCore(ref, byRef, blocks, coreMessages, pick, fallbackIdx = -1) {
|
|
3778
|
+
const id = boundaryRawCore(ref, byRef, blocks, coreMessages, pick);
|
|
3779
|
+
if (id) {
|
|
3780
|
+
const i = coreMessages.findIndex((cm) => cm.id === id);
|
|
3781
|
+
if (i >= 0) return i;
|
|
3782
|
+
}
|
|
3783
|
+
return fallbackIdx >= 0 && fallbackIdx < coreMessages.length ? fallbackIdx : -1;
|
|
3784
|
+
}
|
|
3785
|
+
function refOfPieceCore(coreMessages, idx, byRef) {
|
|
3786
|
+
const id = coreMessages[idx]?.id;
|
|
3787
|
+
if (!id) return "";
|
|
3788
|
+
for (const [ref, mapped] of Object.entries(byRef)) if (mapped === id) return ref;
|
|
3789
|
+
return "";
|
|
3790
|
+
}
|
|
3791
|
+
function staleRangeCore(r, rangeIndex, resultText, coreMessages, callIndex, byRef, blocks) {
|
|
3792
|
+
const pm = resultText.match(/\[pos=([0-9,-]+)\]/);
|
|
3793
|
+
const pair = pm ? pm[1].split(",")[rangeIndex] ?? "-" : "-";
|
|
3794
|
+
const hinted = pair !== "-";
|
|
3795
|
+
const [ps, pe] = pair === "-" ? ["", ""] : pair.split("-");
|
|
3796
|
+
const fbStart = ps && ps !== "" ? Number.parseInt(ps, 10) : -1;
|
|
3797
|
+
const fbEnd = pe && pe !== "" ? Number.parseInt(pe, 10) : -1;
|
|
3798
|
+
const startRaw = boundaryRawCore(r.startRef, byRef, blocks, coreMessages, "min");
|
|
3799
|
+
const endRaw = boundaryRawCore(r.endRef, byRef, blocks, coreMessages, "max");
|
|
3800
|
+
const rawStartIdx = startRaw ? coreMessages.findIndex((cm) => cm.id === startRaw) : -1;
|
|
3801
|
+
const rawEndIdx = endRaw ? coreMessages.findIndex((cm) => cm.id === endRaw) : -1;
|
|
3802
|
+
const startIdx = rawStartIdx >= 0 ? rawStartIdx : fbStart >= 0 && fbStart < coreMessages.length ? fbStart : -1;
|
|
3803
|
+
const endIdx = rawEndIdx >= 0 ? rawEndIdx : fbEnd >= 0 && fbEnd < coreMessages.length ? fbEnd : -1;
|
|
3804
|
+
if (startIdx < 0 || endIdx < 0) {
|
|
3805
|
+
if (!/^b\d+$/i.test(r.startRef.trim()) && !/^b\d+$/i.test(r.endRef.trim()))
|
|
3806
|
+
return { reject: `unresolved ${r.startRef}..${r.endRef} -> ${startIdx}..${endIdx}`, ...hinted ? { hint: true } : {} };
|
|
3807
|
+
return {};
|
|
3808
|
+
}
|
|
3809
|
+
if (endIdx > callIndex) return { reject: `end idx ${endIdx} > callIndex ${callIndex}`, ...hinted ? { hint: true } : {} };
|
|
3810
|
+
const m = resultText.match(/\[fp=([0-9a-f,-]+)\]/);
|
|
3811
|
+
if (m) {
|
|
3812
|
+
const want = m[1].split(",")[rangeIndex];
|
|
3813
|
+
if (want !== void 0 && want !== "-") {
|
|
3814
|
+
const got = spanFingerprintCoreIdx(coreMessages, startIdx, endIdx);
|
|
3815
|
+
if (want !== got) return { reject: `fp ${r.startRef}..${r.endRef} want ${want} got ${got} @${startIdx}..${endIdx}`, ...hinted ? { hint: true } : {} };
|
|
3475
3816
|
}
|
|
3476
|
-
out.push({ role: srcMsg.role === "assistant" ? "assistant" : srcMsg.role, content: text });
|
|
3477
3817
|
}
|
|
3478
|
-
|
|
3818
|
+
const remap = {};
|
|
3819
|
+
if (/^m\d+$/i.test(r.startRef.trim()) && rawStartIdx < 0) {
|
|
3820
|
+
const ref = refOfPieceCore(coreMessages, startIdx, byRef);
|
|
3821
|
+
if (!ref) return { reject: `recovered ${r.startRef} @${startIdx} has no ref (protected piece)`, ...hinted ? { hint: true } : {} };
|
|
3822
|
+
remap.startRef = ref;
|
|
3823
|
+
}
|
|
3824
|
+
if (/^m\d+$/i.test(r.endRef.trim()) && rawEndIdx < 0) {
|
|
3825
|
+
const ref = refOfPieceCore(coreMessages, endIdx, byRef);
|
|
3826
|
+
if (!ref) return { reject: `recovered ${r.endRef} @${endIdx} has no ref (protected piece)`, ...hinted ? { hint: true } : {} };
|
|
3827
|
+
remap.endRef = ref;
|
|
3828
|
+
}
|
|
3829
|
+
if (!remap.startRef && !remap.endRef) return {};
|
|
3830
|
+
return { remap, recovered: { pos: pair, startIdx, endIdx } };
|
|
3479
3831
|
}
|
|
3480
|
-
function
|
|
3832
|
+
function rangePositionsCore(ranges, coreMessages, byRef, blocks) {
|
|
3833
|
+
return ranges.map((r) => {
|
|
3834
|
+
const s = boundaryIndexCore(r.startRef, byRef, blocks, coreMessages, "min");
|
|
3835
|
+
const e = s >= 0 ? boundaryIndexCore(r.endRef, byRef, blocks, coreMessages, "max") : -1;
|
|
3836
|
+
return s >= 0 && e >= 0 ? `${s}-${e}` : "-";
|
|
3837
|
+
});
|
|
3838
|
+
}
|
|
3839
|
+
function viewToCoreStream(view, systemText) {
|
|
3481
3840
|
const messages = [{ role: "system", content: systemText }];
|
|
3482
3841
|
for (const message of view) {
|
|
3483
3842
|
const m = message;
|
|
3484
3843
|
if (m.role === "user") {
|
|
3485
|
-
const text =
|
|
3844
|
+
const text = extractViewText(m.content);
|
|
3486
3845
|
if (text) messages.push({ role: "user", content: text });
|
|
3487
3846
|
} else if (m.role === "assistant") {
|
|
3488
3847
|
const blocks = Array.isArray(m.content) ? m.content : [];
|
|
3489
3848
|
const calls = blocks.filter(
|
|
3490
3849
|
(b) => b !== null && typeof b === "object" && b.type === "toolCall"
|
|
3491
3850
|
);
|
|
3492
|
-
const text =
|
|
3851
|
+
const text = extractViewText(m.content);
|
|
3493
3852
|
if (calls.length > 0) {
|
|
3494
3853
|
messages.push({
|
|
3495
3854
|
role: "assistant",
|
|
@@ -3504,13 +3863,63 @@ function viewToWireStream(view, systemText) {
|
|
|
3504
3863
|
messages.push({ role: "assistant", content: text });
|
|
3505
3864
|
}
|
|
3506
3865
|
} else if (m.role === "toolResult") {
|
|
3507
|
-
messages.push({ role: "tool", tool_call_id: m.toolCallId ?? "", content:
|
|
3866
|
+
messages.push({ role: "tool", tool_call_id: m.toolCallId ?? "", content: extractViewText(m.content) });
|
|
3508
3867
|
} else {
|
|
3509
|
-
const text =
|
|
3868
|
+
const text = extractViewText(m.content) || (typeof m.summary === "string" ? m.summary : "");
|
|
3510
3869
|
if (text) messages.push({ role: "developer", content: text });
|
|
3511
3870
|
}
|
|
3512
3871
|
}
|
|
3513
|
-
|
|
3872
|
+
const { msgs } = openaiToCore({ model: "prime-fold", messages });
|
|
3873
|
+
return msgs;
|
|
3874
|
+
}
|
|
3875
|
+
function viewToAnthropicCore(view) {
|
|
3876
|
+
const messages = [];
|
|
3877
|
+
for (const message of view) {
|
|
3878
|
+
const m = message;
|
|
3879
|
+
if (m.role === "user") {
|
|
3880
|
+
const text = extractViewText(m.content);
|
|
3881
|
+
if (text) messages.push({ role: "user", content: [{ type: "text", text }] });
|
|
3882
|
+
} else if (m.role === "assistant") {
|
|
3883
|
+
const blocks = Array.isArray(m.content) ? m.content : [];
|
|
3884
|
+
const calls = blocks.filter(
|
|
3885
|
+
(b) => b !== null && typeof b === "object" && b.type === "toolCall"
|
|
3886
|
+
);
|
|
3887
|
+
const text = extractViewText(m.content);
|
|
3888
|
+
const content = [];
|
|
3889
|
+
if (text) content.push({ type: "text", text });
|
|
3890
|
+
for (const c of calls) {
|
|
3891
|
+
let input = {};
|
|
3892
|
+
try {
|
|
3893
|
+
input = c.arguments && typeof c.arguments === "object" ? c.arguments : JSON.parse(JSON.stringify(c.arguments ?? {}));
|
|
3894
|
+
} catch {
|
|
3895
|
+
input = {};
|
|
3896
|
+
}
|
|
3897
|
+
content.push({ type: "tool_use", id: c.id, name: c.name ?? "", input });
|
|
3898
|
+
}
|
|
3899
|
+
if (content.length > 0) messages.push({ role: "assistant", content });
|
|
3900
|
+
} else if (m.role === "toolResult") {
|
|
3901
|
+
messages.push({
|
|
3902
|
+
role: "user",
|
|
3903
|
+
content: [{ type: "tool_result", tool_use_id: m.toolCallId ?? "", content: extractViewText(m.content) }]
|
|
3904
|
+
});
|
|
3905
|
+
} else {
|
|
3906
|
+
const text = extractViewText(m.content) || (typeof m.summary === "string" ? m.summary : "");
|
|
3907
|
+
if (text) messages.push({ role: "user", content: [{ type: "text", text }] });
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
const { msgs, cacheControls } = anthropicToCore({ model: "prime-fold", messages });
|
|
3911
|
+
void cacheControls;
|
|
3912
|
+
return msgs;
|
|
3913
|
+
}
|
|
3914
|
+
function extractViewText(content) {
|
|
3915
|
+
const clean = (s) => stripRefTag(s);
|
|
3916
|
+
if (typeof content === "string") return clean(content);
|
|
3917
|
+
if (!Array.isArray(content)) return "";
|
|
3918
|
+
const parts = [];
|
|
3919
|
+
for (const b of content) {
|
|
3920
|
+
if (b.type === "text" && typeof b.text === "string") parts.push(clean(b.text));
|
|
3921
|
+
}
|
|
3922
|
+
return parts.join("\n");
|
|
3514
3923
|
}
|
|
3515
3924
|
|
|
3516
3925
|
// src/runtime.ts
|
|
@@ -3538,6 +3947,7 @@ function createRuntime(adapter) {
|
|
|
3538
3947
|
const core = createCore({ countTokens: defaultCountTokens });
|
|
3539
3948
|
const locks = /* @__PURE__ */ new Map();
|
|
3540
3949
|
const slots = /* @__PURE__ */ new Map();
|
|
3950
|
+
const coreSlots = /* @__PURE__ */ new Map();
|
|
3541
3951
|
let adapterRef = adapter;
|
|
3542
3952
|
let promptsRef = defaultPrompts;
|
|
3543
3953
|
async function acquireLock(sid) {
|
|
@@ -3567,6 +3977,14 @@ function createRuntime(adapter) {
|
|
|
3567
3977
|
}
|
|
3568
3978
|
return slot;
|
|
3569
3979
|
}
|
|
3980
|
+
function coreSlotFor(sid) {
|
|
3981
|
+
let slot = coreSlots.get(sid);
|
|
3982
|
+
if (!slot) {
|
|
3983
|
+
slot = freshSlot();
|
|
3984
|
+
coreSlots.set(sid, slot);
|
|
3985
|
+
}
|
|
3986
|
+
return slot;
|
|
3987
|
+
}
|
|
3570
3988
|
function sidOf(ctx) {
|
|
3571
3989
|
return ctx.sessionManager.getSessionId();
|
|
3572
3990
|
}
|
|
@@ -3651,8 +4069,90 @@ function createRuntime(adapter) {
|
|
|
3651
4069
|
stream.forEach((message, i) => originalById.set(`p${i + 1}`, message));
|
|
3652
4070
|
return { state: slot.state, coreMessages, originalById, streamLen: stream.length };
|
|
3653
4071
|
}
|
|
4072
|
+
function foldStreamCore(ctx, stream) {
|
|
4073
|
+
const sid = sidOf(ctx);
|
|
4074
|
+
let slot = coreSlotFor(sid);
|
|
4075
|
+
if (slot.preview) {
|
|
4076
|
+
debug.event("fold-refold", { sid, foldedLen: slot.foldedLen, lcp: 0, streamLen: stream.length, reason: "preview" });
|
|
4077
|
+
slot = freshSlot(slot);
|
|
4078
|
+
coreSlots.set(sid, slot);
|
|
4079
|
+
}
|
|
4080
|
+
const ids = stream.map(coreIdentity);
|
|
4081
|
+
let lcp = 0;
|
|
4082
|
+
while (lcp < Math.min(ids.length, slot.identities.length) && ids[lcp] === slot.identities[lcp]) lcp++;
|
|
4083
|
+
if (lcp < slot.foldedLen) {
|
|
4084
|
+
const flip = isViewFlip(slot.foldedLen, lcp);
|
|
4085
|
+
debug.event("fold-refold", { sid, foldedLen: slot.foldedLen, lcp, streamLen: stream.length, flip, space: "core" });
|
|
4086
|
+
slot = flip ? preserveCompressedSlot(slot) : freshSlot(slot);
|
|
4087
|
+
coreSlots.set(sid, slot);
|
|
4088
|
+
lcp = 0;
|
|
4089
|
+
}
|
|
4090
|
+
const coreMessages = stream;
|
|
4091
|
+
const config = configFor(ctx);
|
|
4092
|
+
const names = toolCallNames(stream);
|
|
4093
|
+
const assigned = assignRefs(coreMessages, {
|
|
4094
|
+
existing: slot.state.messageRefs,
|
|
4095
|
+
nextIndex: highestUsedIndex(slot.state.messageRefs) + 1,
|
|
4096
|
+
isProtected: (m) => {
|
|
4097
|
+
if (m.role !== "tool" || !m.toolCallId) return false;
|
|
4098
|
+
const name = names.get(m.toolCallId);
|
|
4099
|
+
if (!name) return false;
|
|
4100
|
+
if (name === "compress") return true;
|
|
4101
|
+
return (config.protectedTools ?? []).includes(name);
|
|
4102
|
+
}
|
|
4103
|
+
});
|
|
4104
|
+
slot.state = { ...slot.state, messageRefs: assigned.map };
|
|
4105
|
+
const isFreshFold = slot.foldedLen === 0;
|
|
4106
|
+
const resultTexts = toolResultTextsCore(stream);
|
|
4107
|
+
let replayed = 0;
|
|
4108
|
+
for (let i = isFreshFold ? 0 : slot.foldedLen; i < stream.length; i++) {
|
|
4109
|
+
for (const call of findCompressCallsCore(stream[i])) {
|
|
4110
|
+
const resultText = resultTexts.get(call.id) ?? "";
|
|
4111
|
+
if (resultText.includes("No changes applied")) {
|
|
4112
|
+
debug.event("fold-replay-skipped", { sid, callId: call.id });
|
|
4113
|
+
continue;
|
|
4114
|
+
}
|
|
4115
|
+
if (slot.appliedCallIds.has(call.id) || stateHasCompressCall(slot.state, call.id)) continue;
|
|
4116
|
+
const verdicts = call.ranges.map((r, ri) => staleRangeCore(r, ri, resultText, coreMessages, i, slot.state.messageRefs.byRef, slot.state.blocks));
|
|
4117
|
+
const stale = verdicts.find((v) => v.reject);
|
|
4118
|
+
if (stale) {
|
|
4119
|
+
debug.event("fold-replay-stale", { sid, callId: call.id, reason: stale.reject });
|
|
4120
|
+
const failed = verdicts.find((v) => v.hint && v.reject);
|
|
4121
|
+
if (failed) logWarn("fold", { sid, event: "replay-recovery-failed", callId: call.id, reason: failed.reject });
|
|
4122
|
+
continue;
|
|
4123
|
+
}
|
|
4124
|
+
const recovered = verdicts.find((v) => v.recovered);
|
|
4125
|
+
const ranges = recovered ? call.ranges.map((r, ri) => {
|
|
4126
|
+
const m = verdicts[ri].remap;
|
|
4127
|
+
return m ? { ...r, startRef: m.startRef ?? r.startRef, endRef: m.endRef ?? r.endRef } : r;
|
|
4128
|
+
}) : call.ranges;
|
|
4129
|
+
if (recovered) {
|
|
4130
|
+
logWarn("fold", { sid, event: "replay-recovered", callId: call.id, pos: recovered.recovered.pos, startIdx: recovered.recovered.startIdx, endIdx: recovered.recovered.endIdx });
|
|
4131
|
+
}
|
|
4132
|
+
try {
|
|
4133
|
+
const applied = core.applyCompression({ ranges, messages: coreMessages, state: slot.state, config });
|
|
4134
|
+
if (applied.result.errors.length === 0) {
|
|
4135
|
+
slot.state = applied.state;
|
|
4136
|
+
replayed++;
|
|
4137
|
+
debug.event("fold-replay", { sid, callId: call.id, ranges: call.ranges.length });
|
|
4138
|
+
} else {
|
|
4139
|
+
logWarn("fold", { sid, event: "replay-rejected", callId: call.id, errors: applied.result.errors.slice(0, 3) });
|
|
4140
|
+
}
|
|
4141
|
+
} catch (e) {
|
|
4142
|
+
logWarn("fold", { sid, event: "replay-failed", callId: call.id, error: e instanceof Error ? e.message : String(e) });
|
|
4143
|
+
}
|
|
4144
|
+
slot.appliedCallIds.add(call.id);
|
|
4145
|
+
}
|
|
4146
|
+
}
|
|
4147
|
+
if (replayed > 0) logWarn("fold", { sid, event: "replayed", calls: replayed });
|
|
4148
|
+
slot.identities = ids;
|
|
4149
|
+
slot.foldedLen = ids.length;
|
|
4150
|
+
slot.coreMessages = coreMessages;
|
|
4151
|
+
return { state: slot.state, coreMessages, streamLen: stream.length };
|
|
4152
|
+
}
|
|
3654
4153
|
function stateFor(ctx) {
|
|
3655
|
-
const
|
|
4154
|
+
const sid = sidOf(ctx);
|
|
4155
|
+
const slot = slotForMode(ctx, sid);
|
|
3656
4156
|
return Promise.resolve({ state: slot.state, coreMessages: slot.coreMessages });
|
|
3657
4157
|
}
|
|
3658
4158
|
function primeFold(ctx) {
|
|
@@ -3661,30 +4161,40 @@ function createRuntime(adapter) {
|
|
|
3661
4161
|
const sm = ctx.sessionManager;
|
|
3662
4162
|
const view = sm.buildSessionContext?.().messages ?? [];
|
|
3663
4163
|
if (view.length === 0) return;
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
4164
|
+
if (resolveTransformMode(adapterRef, ctx.model) === "provider") {
|
|
4165
|
+
const api = ctx.model?.api ?? "";
|
|
4166
|
+
let stream;
|
|
4167
|
+
if (api === "anthropic-messages") {
|
|
4168
|
+
stream = viewToAnthropicCore(view);
|
|
4169
|
+
} else {
|
|
4170
|
+
const base = getSystemPromptText(ctx);
|
|
4171
|
+
const acp = buildAcpSystemPrompt(promptsRef);
|
|
4172
|
+
stream = viewToCoreStream(view, base.includes(acp) ? base : `${base}
|
|
3670
4173
|
|
|
3671
4174
|
${acp}`);
|
|
3672
|
-
|
|
4175
|
+
}
|
|
4176
|
+
const r2 = foldStreamCore(ctx, stream);
|
|
4177
|
+
coreSlotFor(sid).preview = true;
|
|
4178
|
+
logInfo("fold", { sid, event: "prime-fold", msgs: stream.length, wire: true, blocks: r2.state.blocks.length });
|
|
4179
|
+
return;
|
|
3673
4180
|
}
|
|
3674
|
-
const r = foldStream(ctx,
|
|
4181
|
+
const r = foldStream(ctx, view);
|
|
3675
4182
|
slotFor(sid).preview = true;
|
|
3676
|
-
logInfo("fold", { sid, event: "prime-fold", msgs:
|
|
4183
|
+
logInfo("fold", { sid, event: "prime-fold", msgs: view.length, wire: false, blocks: r.state.blocks.length });
|
|
3677
4184
|
} catch (e) {
|
|
3678
4185
|
logWarn("fold", { sid, event: "prime-fold-failed", error: e instanceof Error ? e.message : String(e) });
|
|
3679
4186
|
}
|
|
3680
4187
|
}
|
|
3681
4188
|
function forgetSession(sid) {
|
|
3682
4189
|
slots.delete(sid);
|
|
4190
|
+
coreSlots.delete(sid);
|
|
3683
4191
|
locks.delete(sid);
|
|
3684
4192
|
}
|
|
4193
|
+
function slotForMode(ctx, sid) {
|
|
4194
|
+
return resolveTransformMode(adapterRef, ctx.model) === "provider" ? coreSlotFor(sid) : slotFor(sid);
|
|
4195
|
+
}
|
|
3685
4196
|
function commitFoldState(ctx, state, toolCallId) {
|
|
3686
|
-
const
|
|
3687
|
-
const slot = slotFor(sid);
|
|
4197
|
+
const slot = slotForMode(ctx, sidOf(ctx));
|
|
3688
4198
|
slot.state = state;
|
|
3689
4199
|
if (toolCallId) slot.appliedCallIds.add(toolCallId);
|
|
3690
4200
|
}
|
|
@@ -3693,7 +4203,7 @@ ${acp}`);
|
|
|
3693
4203
|
slot.lastRebuiltOutput = rebuilt.map(messageIdentity);
|
|
3694
4204
|
}
|
|
3695
4205
|
function noteCompressOutcome(ctx, ok) {
|
|
3696
|
-
const slot =
|
|
4206
|
+
const slot = slotForMode(ctx, sidOf(ctx));
|
|
3697
4207
|
slot.rejectStreak = ok ? 0 : slot.rejectStreak + 1;
|
|
3698
4208
|
return slot.rejectStreak;
|
|
3699
4209
|
}
|
|
@@ -3714,6 +4224,7 @@ ${acp}`);
|
|
|
3714
4224
|
liveContextLimit,
|
|
3715
4225
|
configFor,
|
|
3716
4226
|
foldStream,
|
|
4227
|
+
foldStreamCore,
|
|
3717
4228
|
stateFor,
|
|
3718
4229
|
commitFoldState,
|
|
3719
4230
|
recordRebuiltOutput,
|
|
@@ -3906,9 +4417,11 @@ async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
|
3906
4417
|
logError("compress", { sid: ctx.sessionManager.getSessionId(), event: "warnings", count: warnings.length, warnings: warnings.slice(0, 5) });
|
|
3907
4418
|
}
|
|
3908
4419
|
const fps = rangeFingerprints(rangeSpecs, coreMessages, applied.state.messageRefs.byRef, applied.state.blocks);
|
|
4420
|
+
const positions = rangePositionsCore(rangeSpecs, coreMessages, applied.state.messageRefs.byRef, applied.state.blocks);
|
|
3909
4421
|
const lines = [`\u25A3 ACP | ${formatTokens3(beforeTokens)} \u2192 ${formatTokens3(afterTokens)} tokens (~${formatTokens3(tokensCompressed)} reclaimed, ${blocksCreated} block${blocksCreated > 1 ? "s" : ""})`];
|
|
3910
4422
|
if (warnings.length > 0) lines.push("\u26A0\uFE0F " + warnings.join("; "));
|
|
3911
4423
|
if (fps.some((fp) => fp !== "-")) lines.push(`[fp=${fps.join(",")}]`);
|
|
4424
|
+
if (positions.some((p) => p !== "-")) lines.push(`[pos=${positions.join(",")}]`);
|
|
3912
4425
|
return lines.join("\n");
|
|
3913
4426
|
} finally {
|
|
3914
4427
|
releaseLock();
|
|
@@ -4903,7 +5416,7 @@ async function statusReport(runtime, ctx) {
|
|
|
4903
5416
|
const coveredIds = collectCoveredMessageIds(state);
|
|
4904
5417
|
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
4905
5418
|
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: sentTokens });
|
|
4906
|
-
const versionStr = "0.2.
|
|
5419
|
+
const versionStr = "0.2.7" ? `billion-context-omp@${"0.2.7"}` : void 0;
|
|
4907
5420
|
return buildStatusPanel({
|
|
4908
5421
|
version: versionStr,
|
|
4909
5422
|
tokenCount: sessionTokens,
|
|
@@ -5033,6 +5546,10 @@ import { readFileSync, writeFileSync } from "fs";
|
|
|
5033
5546
|
import { join as join3 } from "path";
|
|
5034
5547
|
var MARKER_FILE = ".billion-context-omp-instance.json";
|
|
5035
5548
|
var FRESH_MS = 6e4;
|
|
5549
|
+
function normalizeLoadPath(p) {
|
|
5550
|
+
const q = p.indexOf("?");
|
|
5551
|
+
return q === -1 ? p : p.slice(0, q);
|
|
5552
|
+
}
|
|
5036
5553
|
function markerPath() {
|
|
5037
5554
|
return join3(homeDir(), ".omp", MARKER_FILE);
|
|
5038
5555
|
}
|
|
@@ -5047,7 +5564,7 @@ function readMarker() {
|
|
|
5047
5564
|
function detectDualInstance(selfPath, now = Date.now()) {
|
|
5048
5565
|
const m = readMarker();
|
|
5049
5566
|
if (!m) return void 0;
|
|
5050
|
-
if (m.path === selfPath) return void 0;
|
|
5567
|
+
if (normalizeLoadPath(m.path) === normalizeLoadPath(selfPath)) return void 0;
|
|
5051
5568
|
if (now - m.ts > FRESH_MS) return void 0;
|
|
5052
5569
|
return m;
|
|
5053
5570
|
}
|
|
@@ -5193,7 +5710,7 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
5193
5710
|
const data = await res.json();
|
|
5194
5711
|
const latest = data.version;
|
|
5195
5712
|
if (!latest) return;
|
|
5196
|
-
const current = runtimeVersion ?? "0.2.
|
|
5713
|
+
const current = runtimeVersion ?? "0.2.7";
|
|
5197
5714
|
const hasUpdate = isNewer(latest, current);
|
|
5198
5715
|
debug.event("update-check", {
|
|
5199
5716
|
current,
|
|
@@ -5450,7 +5967,6 @@ function createAcpExtension(adapter = {}) {
|
|
|
5450
5967
|
return (pi) => {
|
|
5451
5968
|
const runtime = createRuntime(adapter);
|
|
5452
5969
|
wireSessionLifecycle(pi, runtime);
|
|
5453
|
-
wireSessionLifecycle(pi, runtime);
|
|
5454
5970
|
wireContextTransform(pi, runtime);
|
|
5455
5971
|
wireSystemPrompt(pi, runtime);
|
|
5456
5972
|
wireProviderTransform(pi, runtime);
|
|
@@ -5469,9 +5985,9 @@ var index_default = createAcpExtension();
|
|
|
5469
5985
|
function wireSessionLifecycle(pi, runtime) {
|
|
5470
5986
|
pi.on("session_start", async (_event, ctx) => {
|
|
5471
5987
|
const sid = ctx.sessionManager.getSessionId();
|
|
5472
|
-
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.2.
|
|
5988
|
+
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.2.7" : null });
|
|
5473
5989
|
const selfPath = import.meta.url;
|
|
5474
|
-
const conflict = stampAndDetect(selfPath, true ? "0.2.
|
|
5990
|
+
const conflict = stampAndDetect(selfPath, true ? "0.2.7" : null);
|
|
5475
5991
|
if (conflict) {
|
|
5476
5992
|
logWarn("instance", { event: "dual-instance", self: selfPath, other: conflict.path, otherPid: conflict.pid, otherVersion: conflict.version });
|
|
5477
5993
|
try {
|
|
@@ -5495,9 +6011,9 @@ function wireSessionLifecycle(pi, runtime) {
|
|
|
5495
6011
|
runtime.setPrompts(defaultPrompts);
|
|
5496
6012
|
}
|
|
5497
6013
|
runtime.primeFold(ctx);
|
|
5498
|
-
|
|
6014
|
+
void checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
|
|
5499
6015
|
if (ctx.hasUI) ctx.ui.notify(msg);
|
|
5500
|
-
});
|
|
6016
|
+
}).catch((e) => logThrow("update", e, { sid, phase: "session_start" }));
|
|
5501
6017
|
});
|
|
5502
6018
|
pi.on("session_shutdown", (_event, ctx) => {
|
|
5503
6019
|
try {
|
|
@@ -5631,14 +6147,14 @@ ${rendered.text}${example}`);
|
|
|
5631
6147
|
} finally {
|
|
5632
6148
|
release();
|
|
5633
6149
|
}
|
|
5634
|
-
|
|
6150
|
+
void checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
|
|
5635
6151
|
if (ctx.hasUI) ctx.ui.notify(msg);
|
|
5636
|
-
});
|
|
6152
|
+
}).catch((e) => logThrow("update", e, { sid, phase: "context" }));
|
|
5637
6153
|
return result;
|
|
5638
6154
|
}
|
|
5639
6155
|
function wireContextTransform(pi, runtime) {
|
|
5640
6156
|
pi.on("context", async (event, ctx) => {
|
|
5641
|
-
if ((runtime.adapter.
|
|
6157
|
+
if (resolveTransformMode(runtime.adapter, ctx.model) === "provider") {
|
|
5642
6158
|
debug.event("context-observer-skip", { sid: ctx.sessionManager.getSessionId(), msgs: event.messages?.length ?? 0 });
|
|
5643
6159
|
return void 0;
|
|
5644
6160
|
}
|
|
@@ -5649,34 +6165,151 @@ function wireContextTransform(pi, runtime) {
|
|
|
5649
6165
|
}
|
|
5650
6166
|
function wireProviderTransform(pi, runtime) {
|
|
5651
6167
|
pi.on("before_provider_request", async (event, ctx) => {
|
|
5652
|
-
if ((runtime.adapter.
|
|
6168
|
+
if (resolveTransformMode(runtime.adapter, ctx.model) !== "provider") return void 0;
|
|
5653
6169
|
const payload = event.payload;
|
|
5654
6170
|
if (payload === null || typeof payload !== "object" || !Array.isArray(payload.messages)) return void 0;
|
|
5655
6171
|
const sid = ctx.sessionManager?.getSessionId?.() ?? "";
|
|
5656
|
-
const fmt2 =
|
|
5657
|
-
if (fmt2 ===
|
|
6172
|
+
const fmt2 = detectProviderWireFormat(payload);
|
|
6173
|
+
if (fmt2 === null) {
|
|
5658
6174
|
debug.event("provider-transform-unknown-format", { sid });
|
|
5659
6175
|
return void 0;
|
|
5660
6176
|
}
|
|
5661
6177
|
try {
|
|
5662
|
-
const
|
|
5663
|
-
if (
|
|
5664
|
-
const result = await
|
|
6178
|
+
const { msgs, cacheControls } = payloadToCore(payload, fmt2);
|
|
6179
|
+
if (msgs.length === 0) return void 0;
|
|
6180
|
+
const result = await transformStreamCore(ctx, runtime, msgs, fmt2);
|
|
5665
6181
|
if (!result) return void 0;
|
|
5666
|
-
const
|
|
5667
|
-
const outMsgs = wireOut.messages?.length ?? 0;
|
|
6182
|
+
const outMsgs = coreToPayloadMessages(result.coreOut, fmt2, cacheControls).length;
|
|
5668
6183
|
const inMsgs = payload.messages?.length ?? 0;
|
|
5669
|
-
if (outMsgs !== inMsgs
|
|
6184
|
+
if (outMsgs !== inMsgs) {
|
|
5670
6185
|
logInfo("provider-transform", { sid, fmt: fmt2, inMsgs, outMsgs, nudge: result.nudgeInjected ? "injected" : "idle" });
|
|
5671
6186
|
}
|
|
5672
6187
|
debug.event("provider-transform", { sid, fmt: fmt2, inMsgs, outMsgs, nudgeInjected: result.nudgeInjected });
|
|
5673
|
-
return
|
|
6188
|
+
return { ...payload, messages: coreToPayloadMessages(result.coreOut, fmt2, cacheControls) };
|
|
5674
6189
|
} catch (e) {
|
|
5675
6190
|
logThrow("provider-transform", e, { sid, fmt: fmt2 });
|
|
5676
6191
|
return void 0;
|
|
5677
6192
|
}
|
|
5678
6193
|
});
|
|
5679
6194
|
}
|
|
6195
|
+
async function transformStreamCore(ctx, runtime, wireMsgs, fmt2) {
|
|
6196
|
+
const sid = ctx.sessionManager.getSessionId();
|
|
6197
|
+
const release = await runtime.acquireLock(sid);
|
|
6198
|
+
let result;
|
|
6199
|
+
try {
|
|
6200
|
+
if (wireMsgs.length === 0) {
|
|
6201
|
+
debug.event("empty-stream-bypass", { sid, space: "core" });
|
|
6202
|
+
return void 0;
|
|
6203
|
+
}
|
|
6204
|
+
debug.event("context-in-raw", { sid, msgs: wireMsgs.length, mode: "provider" });
|
|
6205
|
+
const { state, coreMessages, streamLen } = runtime.foldStreamCore(ctx, wireMsgs);
|
|
6206
|
+
const preTurnNudgeBaseline = state.nudge.lastPerMessageNudgeTokens;
|
|
6207
|
+
const preTurnNudgeShownTokens = state.nudge.lastNudgeShownTokens;
|
|
6208
|
+
const preTurnNudgeShownByTier = state.nudge.lastShownByTier;
|
|
6209
|
+
const config = runtime.configFor(ctx);
|
|
6210
|
+
const coveredIds = collectCoveredMessageIds(state);
|
|
6211
|
+
const systemPromptTokens = estimateTextTokens2(getSystemPromptText(ctx) ?? "");
|
|
6212
|
+
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
6213
|
+
const sessionTokens = ctx.getContextUsage?.()?.tokens ?? null;
|
|
6214
|
+
const tokenCount = sentTokens;
|
|
6215
|
+
debug.event("context-in", {
|
|
6216
|
+
sid,
|
|
6217
|
+
mode: "provider",
|
|
6218
|
+
fmt: fmt2,
|
|
6219
|
+
streamLen,
|
|
6220
|
+
coreMsgs: coreMessages.length,
|
|
6221
|
+
tokenCount,
|
|
6222
|
+
sessionTokens,
|
|
6223
|
+
limit: config.modelContextLimit,
|
|
6224
|
+
blocksBefore: state.blocks.length,
|
|
6225
|
+
activeBefore: state.blocks.filter((b) => b.active).length
|
|
6226
|
+
});
|
|
6227
|
+
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount, renderTags: "text-only" });
|
|
6228
|
+
runtime.commitFoldState(ctx, turn.state);
|
|
6229
|
+
logInfo("turn", {
|
|
6230
|
+
sid,
|
|
6231
|
+
inMsgs: coreMessages.length,
|
|
6232
|
+
outMsgs: turn.messages.length,
|
|
6233
|
+
tokens: tokenCount,
|
|
6234
|
+
sessionTokens,
|
|
6235
|
+
pct: config.modelContextLimit > 0 ? Math.round(tokenCount / config.modelContextLimit * 100) : null,
|
|
6236
|
+
limit: config.modelContextLimit,
|
|
6237
|
+
nudge: turn.nudge?.shouldInject ? turn.nudge.breakdown?.emergencyOverride === 1 ? "emergency" : "active" : "idle",
|
|
6238
|
+
nudgeReason: turn.nudge?.reason ?? null,
|
|
6239
|
+
blocks: turn.state.blocks.length,
|
|
6240
|
+
activeBlocks: turn.state.blocks.filter((b) => b.active).length
|
|
6241
|
+
});
|
|
6242
|
+
debug.event("processTurn", {
|
|
6243
|
+
outMsgs: turn.messages.length,
|
|
6244
|
+
summaryMsgs: turn.messages.filter((m) => m.id.startsWith("acp_summary")).length,
|
|
6245
|
+
prunedMsgs: coreMessages.length - turn.messages.length + turn.messages.filter((m) => m.id.startsWith("acp_summary")).length,
|
|
6246
|
+
nudgeShouldInject: turn.nudge?.shouldInject ?? false,
|
|
6247
|
+
nudgeReason: turn.nudge?.reason ?? null,
|
|
6248
|
+
nudgeVoice: turn.nudge ? renderNudgeText(turn.nudge, runtime.prompts).voice : null,
|
|
6249
|
+
nudgePct: turn.nudge ? Math.round(turn.nudge.contextUsage * 100) : null,
|
|
6250
|
+
nudgeTier: turn.nudge?.tier ?? null,
|
|
6251
|
+
nudgeCompressibleCount: turn.nudge?.compressibleRanges.length ?? 0,
|
|
6252
|
+
nudgeProtectedCount: turn.nudge?.protectedRanges?.length ?? 0,
|
|
6253
|
+
nothingToCompress: turn.nudge?.reason?.includes("nothing to compress") ?? false,
|
|
6254
|
+
blocksAfter: turn.state.blocks.length,
|
|
6255
|
+
activeAfter: turn.state.blocks.filter((b) => b.active).length
|
|
6256
|
+
});
|
|
6257
|
+
const coreOut = applyWireTagContract(
|
|
6258
|
+
turn.messages.filter((m) => !m.id.startsWith("acp_summary_")),
|
|
6259
|
+
turn.state
|
|
6260
|
+
);
|
|
6261
|
+
let nudgeInjected = false;
|
|
6262
|
+
if (turn.nudge?.shouldInject) {
|
|
6263
|
+
const lastUser = [...wireMsgs].reverse().find((m) => m.role === "user");
|
|
6264
|
+
const tailText = lastUser ? lastUser.text ?? "" : "";
|
|
6265
|
+
const isFeedbackView = tailText.includes("efficiency nudge to compress early") || tailText.includes("Context limit reached");
|
|
6266
|
+
if (isFeedbackView) {
|
|
6267
|
+
debug.event("nudge-feedback-skip", { sid, msgs: wireMsgs.length });
|
|
6268
|
+
} else {
|
|
6269
|
+
const emergency = turn.nudge.breakdown?.emergencyOverride === 1;
|
|
6270
|
+
const epochReset = turn.state.nudge.lastPerMessageNudgeTokens !== preTurnNudgeBaseline;
|
|
6271
|
+
const prevShown = epochReset ? 0 : preTurnNudgeShownTokens;
|
|
6272
|
+
const cadenceFloor = turn.nudge.breakdown?.growthFloor ?? 0;
|
|
6273
|
+
const suppressed = !emergency && prevShown > 0 && tokenCount - prevShown < cadenceFloor;
|
|
6274
|
+
if (suppressed) {
|
|
6275
|
+
turn.state.nudge.lastNudgeShownTokens = prevShown;
|
|
6276
|
+
turn.state.nudge.lastShownByTier = preTurnNudgeShownByTier;
|
|
6277
|
+
logInfo("nudge", { sid, event: "cadence-suppressed", growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
6278
|
+
debug.event("nudge-suppressed", { sid, growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
6279
|
+
} else {
|
|
6280
|
+
nudgeInjected = true;
|
|
6281
|
+
turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
|
|
6282
|
+
const rendered = renderNudgeText(turn.nudge, runtime.prompts);
|
|
6283
|
+
const top = [...turn.nudge.compressibleRanges].sort((a, b) => b.tokens - a.tokens)[0];
|
|
6284
|
+
const example = top ? `
|
|
6285
|
+
|
|
6286
|
+
Example: compress({ content: [{ startId: "${top.startRef}", endId: "${top.endRef}", summary: "..." }] })` : "";
|
|
6287
|
+
if (emergency) {
|
|
6288
|
+
logWarn("nudge", { sid, event: "emergency-inject", pct: Math.round(turn.nudge.contextUsage * 100), voice: rendered.voice, compressible: turn.nudge.compressibleRanges.length });
|
|
6289
|
+
}
|
|
6290
|
+
const debugOn2 = debug.enabled;
|
|
6291
|
+
if (debugOn2 && ctx.hasUI) {
|
|
6292
|
+
ctx.ui.notify(`[ACP nudge \u2192 context]${emergency ? " [EMERGENCY]" : ""}
|
|
6293
|
+
${rendered.text}${example}`);
|
|
6294
|
+
}
|
|
6295
|
+
debug.event("nudge-injected", { sid, voice: rendered.voice, channels: ["wire", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
|
|
6296
|
+
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) });
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
6299
|
+
}
|
|
6300
|
+
debug.event("core-out", { sid, coreOutMsgs: coreOut.length, space: "core", fmt: fmt2 });
|
|
6301
|
+
result = { coreOut, nudgeInjected };
|
|
6302
|
+
} catch (e) {
|
|
6303
|
+
logThrow("context-core", e, { sid, phase: "transform" });
|
|
6304
|
+
throw e;
|
|
6305
|
+
} finally {
|
|
6306
|
+
release();
|
|
6307
|
+
}
|
|
6308
|
+
void checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
|
|
6309
|
+
if (ctx.hasUI) ctx.ui.notify(msg);
|
|
6310
|
+
}).catch((e) => logThrow("update", e, { sid, phase: "provider" }));
|
|
6311
|
+
return result;
|
|
6312
|
+
}
|
|
5680
6313
|
function wireSystemPrompt(pi, runtime) {
|
|
5681
6314
|
pi.on("before_agent_start", (event) => {
|
|
5682
6315
|
const acp = buildAcpSystemPrompt(runtime.prompts);
|
|
@@ -5708,7 +6341,7 @@ function wireProviderDebug(pi) {
|
|
|
5708
6341
|
});
|
|
5709
6342
|
});
|
|
5710
6343
|
}
|
|
5711
|
-
function
|
|
6344
|
+
function nudgeText(nudge, blocks, prompts, example) {
|
|
5712
6345
|
const rendered = renderNudgeText(nudge, prompts);
|
|
5713
6346
|
const lines = [rendered.text];
|
|
5714
6347
|
if (blocks.length > 0) {
|
|
@@ -5727,9 +6360,12 @@ function nudgeMessage(nudge, blocks, prompts, example) {
|
|
|
5727
6360
|
lines.push(`Compressed blocks: ${blocks.length} active (${tierStr}) \u2014 ${fmt2(totalSummary)} summary, ${fmt2(totalCompressed)} original compressed. Blocks: ${ids}${extra}.`);
|
|
5728
6361
|
}
|
|
5729
6362
|
if (example) lines.push(example);
|
|
6363
|
+
return lines.join("\n");
|
|
6364
|
+
}
|
|
6365
|
+
function nudgeMessage(nudge, blocks, prompts, example) {
|
|
5730
6366
|
return {
|
|
5731
6367
|
role: "user",
|
|
5732
|
-
content: [{ type: "text", text:
|
|
6368
|
+
content: [{ type: "text", text: nudgeText(nudge, blocks, prompts, example) }],
|
|
5733
6369
|
timestamp: Date.now()
|
|
5734
6370
|
};
|
|
5735
6371
|
}
|