acp-kernel 0.0.41 → 0.0.42-pr.136.3

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.
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Compress-call detection and replay-guard on the wire/core stream
3
+ * (Phase K2, moved from billion-context-omp `src/wire-fold.ts`).
4
+ *
5
+ * Two layers:
6
+ * - Detection: `compressToolArgs` accepts the two call shapes that exist in
7
+ * the wild (direct `compress`; legacy `write` to `xd://compress`, args
8
+ * JSON-encoded one or two levels deep), `findCompressCalls` /
9
+ * `findCompressCallsCore` turn a stream tool-call into validated ranges.
10
+ * - Guard: when compression state is REPLAYED against a rebuilt stream
11
+ * (restart, mirror divergence, host re-serialization), every recorded
12
+ * range must prove it still covers the same content — fingerprint match
13
+ * (first/last piece content key), position fallback for drifted
14
+ * boundaries, remap for dangling m-refs.
15
+ *
16
+ * Pure functions over BiliMessage/CoreMessage — no host types, no I/O.
17
+ */
18
+ import type { CoreMessage } from "../types.js";
19
+ import type { BiliMessage } from "./bili-message.js";
20
+ /** A compress call carried by a stream tool-call, normalized to validated
21
+ * ranges. `id` is the tool-call id (replay dedup key). */
22
+ export interface StreamCompressCall {
23
+ id: string;
24
+ ranges: {
25
+ startRef: string;
26
+ endRef: string;
27
+ summary: string;
28
+ topic?: string;
29
+ summaryMaxChars?: number;
30
+ compressCallId: string;
31
+ }[];
32
+ }
33
+ /** Extract a compress tool's arguments from a stream toolCall. Two call
34
+ * shapes exist: (1) top-level — the tools are registered with
35
+ * loadMode:"essential" so hosts do NOT mount them as xd:// devices; the
36
+ * stream shows name:"compress" directly. (2) legacy xd:// — sessions
37
+ * recorded before that change (or hosts forcing discoverable mounting)
38
+ * invoked compress through the write tool with path "xd://compress" and
39
+ * the tool args JSON-encoded in the content field. Both shapes must replay
40
+ * from the stream. Returns normalized compress args (content array plus
41
+ * optional topic / summaryMaxChars from wherever they live). */
42
+ export declare function compressToolArgs(call: {
43
+ name: string;
44
+ arguments?: unknown;
45
+ }): {
46
+ content: unknown[];
47
+ topic?: unknown;
48
+ summaryMaxChars?: unknown;
49
+ } | null;
50
+ /** toolCallId → toolName for every tool-call piece (protected-piece
51
+ * detection: compress + configured tools are never folded). */
52
+ export declare function toolCallNames(msgs: BiliMessage[]): Map<string, string>;
53
+ /** toolCallId → result text for every tool-result piece (compress result
54
+ * rendering inside rebuilt payloads). */
55
+ export declare function toolResultTextsCore(msgs: BiliMessage[]): Map<string, string>;
56
+ /** Compress calls carried by a core tool-call piece. Same two shapes as the
57
+ * AgentMessage stream (direct compress; legacy xd://compress via write),
58
+ * with the arguments JSON-encoded in the piece's text. */
59
+ export declare function findCompressCallsCore(msg: BiliMessage): StreamCompressCall[];
60
+ /** Content key of a core piece for span fingerprints (issue #91): role,
61
+ * contentType, toolName and the FIRST 4096 chars of text. The 4096 cap is
62
+ * deliberate: a host re-serialization that drifts only the tail (beyond
63
+ * char 4096, e.g. truncation of a long tool output) keeps the key intact,
64
+ * so the replay guard tells a benign tail drift from a genuine rewrite. */
65
+ export declare function corePieceKey(cm: CoreMessage): string;
66
+ /** Span fingerprint in content-hash space: hash the content keys of the
67
+ * exact first/last covered pieces. Boundary ids are pre-resolved (byRef /
68
+ * block lookup) — unlike the pN-space spanFingerprint there is no position
69
+ * parsing, ids are unique per piece. */
70
+ export declare function spanFingerprintCore(coreMessages: CoreMessage[], startId: string, endId: string): string;
71
+ /** Index-based span fingerprint (issue #91 replay fallback): hash the content
72
+ * keys of the pieces AT the given stream positions. The stored fp still
73
+ * decides keep/drop — the position is only a recovery hint for a drifted
74
+ * boundary whose content-hash id no longer matches, so a benign tail drift
75
+ * (first-4096 intact) is kept while a real rewrite mismatches. */
76
+ export declare function spanFingerprintCoreIdx(coreMessages: CoreMessage[], startIdx: number, endIdx: number): string;
77
+ /** Structural subset the boundary resolvers need from a compression block
78
+ * (kernel CompressionBlock satisfies this; keeps the guard usable from
79
+ * downstreams that carry lighter block records). */
80
+ export interface BlockLike {
81
+ blockId: string;
82
+ effectiveMessageIds: string[];
83
+ }
84
+ /** Resolve a range boundary to the exact id of the piece it names, in
85
+ * content-hash space. Message refs go through byRef; block refs resolve to
86
+ * the earliest (min) or latest (max) covered piece by STREAM ORDER
87
+ * (index in coreMessages — the hash ids carry no position). */
88
+ export declare function boundaryRawCore(ref: string, byRef: Record<string, string>, blocks: BlockLike[], coreMessages: CoreMessage[], pick: "min" | "max"): string;
89
+ /** Resolve a range boundary to its STREAM INDEX in content-hash space. byRef /
90
+ * block lookup first (the exact piece it names, by array order); on a missed
91
+ * id (a drift re-hashed the piece so its carried ref dangles) fall back to
92
+ * the compress-time recorded index — the position hint, issue #91. -1 =
93
+ * unresolvable. */
94
+ export declare function boundaryIndexCore(ref: string, byRef: Record<string, string>, blocks: BlockLike[], coreMessages: CoreMessage[], pick: "min" | "max", fallbackIdx?: number): number;
95
+ /** Structured replay-guard verdict (issue #91, rework): the position
96
+ * fallback recovers the STREAM INDEX of a drifted boundary, but the kernel
97
+ * resolves ranges by REF — so when a recorded m-ref dangles, the replay
98
+ * must re-apply that boundary under the CURRENT ref of the recovered piece.
99
+ * `remap` carries exactly that (only dangling m-refs are remapped; block
100
+ * refs resolve themselves inside the kernel and are never touched). */
101
+ export type ReplayRangeVerdict = {
102
+ /** Stale: the range must be dropped (master semantics, unchanged). */
103
+ reject?: string;
104
+ /** Dangling m-refs recovered by position, remapped to current refs. */
105
+ remap?: {
106
+ startRef?: string;
107
+ endRef?: string;
108
+ };
109
+ /** True when the result text carried a [pos=] pair for this range —
110
+ * with `reject` set it marks a RECOVERY FAILURE (always logged). */
111
+ hint?: boolean;
112
+ /** Diagnostics — always logged when a recovery happens. */
113
+ recovered?: {
114
+ pos: string;
115
+ startIdx: number;
116
+ endIdx: number;
117
+ };
118
+ };
119
+ /** Current m-ref of the piece at stream index idx (inverse byRef scan).
120
+ * "" when the piece has no ref (protected) — the replay must fail closed
121
+ * rather than hand the kernel a ref it does not know. Replay-time only
122
+ * (replayed compress calls), so the O(refs) scan stays off the hot path. */
123
+ export declare function refOfPieceCore(coreMessages: BiliMessage[], idx: number, byRef: Record<string, string>): string;
124
+ export declare function staleRangeCore(r: {
125
+ startRef: string;
126
+ endRef: string;
127
+ }, rangeIndex: number, resultText: string, coreMessages: BiliMessage[], callIndex: number, byRef: Record<string, string>, blocks: BlockLike[]): ReplayRangeVerdict;
128
+ /** One fingerprint per range for the replay guard, content-hash space
129
+ * (mirrors rangeFingerprints for the pN space). */
130
+ export declare function rangeFingerprintsCore(ranges: Array<{
131
+ startRef: string;
132
+ endRef: string;
133
+ }>, coreMessages: BiliMessage[], byRef: Record<string, string>, blocks: BlockLike[]): string[];
134
+ /** One boundary-index pair per range for the replay fallback (issue #91),
135
+ * aligned with rangeFingerprintsCore: the stream index of each range's exact
136
+ * first/last covered piece at record time ("-1" pair when a boundary can't
137
+ * be positioned), so the replay can recover a drifted boundary by position. */
138
+ export declare function rangePositionsCore(ranges: Array<{
139
+ startRef: string;
140
+ endRef: string;
141
+ }>, coreMessages: CoreMessage[], byRef: Record<string, string>, blocks: BlockLike[]): string[];
142
+ //# sourceMappingURL=compress-detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compress-detect.d.ts","sourceRoot":"","sources":["../../src/wire/compress-detect.ts"],"names":[],"mappings":"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"}
@@ -7,4 +7,5 @@ export * from "./message-id.js";
7
7
  export * from "./demoted-thinking.js";
8
8
  export * from "./mirror.js";
9
9
  export * from "./util.js";
10
+ export * from "./compress-detect.js";
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/wire/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/wire/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC"}
@@ -919,15 +919,194 @@ function mirrorResponsesToCore(view, systemText) {
919
919
  });
920
920
  return msgs;
921
921
  }
922
+
923
+ // src/wire/compress-detect.ts
924
+ import { createHash as createHash2 } from "crypto";
925
+ function compressToolArgs(call) {
926
+ let args = call.arguments;
927
+ if (typeof args === "string") {
928
+ try {
929
+ args = JSON.parse(args);
930
+ } catch {
931
+ return null;
932
+ }
933
+ }
934
+ if (!args || typeof args !== "object" || Array.isArray(args)) return null;
935
+ const a = args;
936
+ if (call.name === "compress") {
937
+ return Array.isArray(a.content) ? { content: a.content, topic: a.topic, summaryMaxChars: a.summaryMaxChars } : null;
938
+ }
939
+ if (call.name !== "write") return null;
940
+ const path = typeof a.path === "string" ? a.path.split("?")[0].replace(/\/+$/, "") : "";
941
+ if (path !== "xd://compress") return null;
942
+ let inner = a.content;
943
+ if (typeof inner === "string") {
944
+ try {
945
+ inner = JSON.parse(inner);
946
+ } catch {
947
+ return null;
948
+ }
949
+ }
950
+ if (!inner || typeof inner !== "object") return null;
951
+ if (Array.isArray(inner)) return { content: inner };
952
+ const ia = inner;
953
+ return Array.isArray(ia.content) ? { content: ia.content, topic: ia.topic, summaryMaxChars: ia.summaryMaxChars } : { content: [ia] };
954
+ }
955
+ function toolCallNames(msgs) {
956
+ const names = /* @__PURE__ */ new Map();
957
+ for (const m of msgs) {
958
+ if (m.contentType === "tool-call" && m.toolCallId && m.toolName) names.set(m.toolCallId, m.toolName);
959
+ }
960
+ return names;
961
+ }
962
+ function toolResultTextsCore(msgs) {
963
+ const results = /* @__PURE__ */ new Map();
964
+ for (const m of msgs) {
965
+ if (m.contentType !== "tool-result" || !m.toolCallId) continue;
966
+ results.set(m.toolCallId, m.text ?? "");
967
+ }
968
+ return results;
969
+ }
970
+ function findCompressCallsCore(msg) {
971
+ if (msg.contentType !== "tool-call" || !msg.toolName) return [];
972
+ const args = compressToolArgs({ name: msg.toolName, arguments: msg.text });
973
+ if (!args) return [];
974
+ const content = args.content;
975
+ if (!Array.isArray(content)) return [];
976
+ const ranges = [];
977
+ const callTopic = typeof args.topic === "string" ? args.topic : void 0;
978
+ for (const item of content) {
979
+ const r = item;
980
+ if (typeof r.startId !== "string" || typeof r.endId !== "string" || typeof r.summary !== "string" || r.summary.length === 0) continue;
981
+ ranges.push({
982
+ startRef: r.startId,
983
+ endRef: r.endId,
984
+ summary: r.summary,
985
+ topic: typeof r.topic === "string" ? r.topic : callTopic,
986
+ summaryMaxChars: typeof args.summaryMaxChars === "number" ? args.summaryMaxChars : void 0,
987
+ compressCallId: msg.toolCallId ?? ""
988
+ });
989
+ }
990
+ return ranges.length > 0 ? [{ id: msg.toolCallId ?? "", ranges }] : [];
991
+ }
992
+ function corePieceKey(cm) {
993
+ return `${cm.role}|${cm.contentType}|${cm.toolName ?? ""}|${(cm.text ?? "").slice(0, 4096)}`;
994
+ }
995
+ function spanFingerprintCore(coreMessages, startId, endId) {
996
+ const find = (id) => coreMessages.find((cm) => cm.id === id);
997
+ const first = find(startId);
998
+ const last = find(endId);
999
+ if (!first || !last) return "";
1000
+ return createHash2("sha1").update(`${corePieceKey(first)}\0${corePieceKey(last)}`).digest("hex").slice(0, 8);
1001
+ }
1002
+ function spanFingerprintCoreIdx(coreMessages, startIdx, endIdx) {
1003
+ const first = coreMessages[startIdx];
1004
+ const last = coreMessages[endIdx];
1005
+ if (!first || !last) return "";
1006
+ return createHash2("sha1").update(`${corePieceKey(first)}\0${corePieceKey(last)}`).digest("hex").slice(0, 8);
1007
+ }
1008
+ function boundaryRawCore(ref, byRef, blocks, coreMessages, pick) {
1009
+ const raw = byRef[ref];
1010
+ if (raw) return raw;
1011
+ const m = /^b(\d+)$/i.exec(ref.trim());
1012
+ if (!m) return "";
1013
+ const block = blocks.find((b) => b.blockId.toLowerCase() === `b${m[1]}`);
1014
+ if (!block) return "";
1015
+ const idx = (id) => coreMessages.findIndex((cm) => cm.id === (byRef[id] ?? id));
1016
+ let best = -1;
1017
+ for (const id of block.effectiveMessageIds) {
1018
+ const i = idx(id);
1019
+ if (i < 0) continue;
1020
+ if (best < 0 || (pick === "min" ? i < best : i > best)) best = i;
1021
+ }
1022
+ return best < 0 ? "" : coreMessages[best]?.id ?? "";
1023
+ }
1024
+ function boundaryIndexCore(ref, byRef, blocks, coreMessages, pick, fallbackIdx = -1) {
1025
+ const id = boundaryRawCore(ref, byRef, blocks, coreMessages, pick);
1026
+ if (id) {
1027
+ const i = coreMessages.findIndex((cm) => cm.id === id);
1028
+ if (i >= 0) return i;
1029
+ }
1030
+ return fallbackIdx >= 0 && fallbackIdx < coreMessages.length ? fallbackIdx : -1;
1031
+ }
1032
+ function refOfPieceCore(coreMessages, idx, byRef) {
1033
+ const id = coreMessages[idx]?.id;
1034
+ if (!id) return "";
1035
+ for (const [ref, mapped] of Object.entries(byRef)) if (mapped === id) return ref;
1036
+ return "";
1037
+ }
1038
+ function staleRangeCore(r, rangeIndex, resultText, coreMessages, callIndex, byRef, blocks) {
1039
+ const pm = resultText.match(/\[pos=([0-9,-]+)\]/);
1040
+ const pair = pm ? pm[1].split(",")[rangeIndex] ?? "-" : "-";
1041
+ const hinted = pair !== "-";
1042
+ const [ps, pe] = pair === "-" ? ["", ""] : pair.split("-");
1043
+ const fbStart = ps && ps !== "" ? Number.parseInt(ps, 10) : -1;
1044
+ const fbEnd = pe && pe !== "" ? Number.parseInt(pe, 10) : -1;
1045
+ const startRaw = boundaryRawCore(r.startRef, byRef, blocks, coreMessages, "min");
1046
+ const endRaw = boundaryRawCore(r.endRef, byRef, blocks, coreMessages, "max");
1047
+ const rawStartIdx = startRaw ? coreMessages.findIndex((cm) => cm.id === startRaw) : -1;
1048
+ const rawEndIdx = endRaw ? coreMessages.findIndex((cm) => cm.id === endRaw) : -1;
1049
+ const startIdx = rawStartIdx >= 0 ? rawStartIdx : fbStart >= 0 && fbStart < coreMessages.length ? fbStart : -1;
1050
+ const endIdx = rawEndIdx >= 0 ? rawEndIdx : fbEnd >= 0 && fbEnd < coreMessages.length ? fbEnd : -1;
1051
+ if (startIdx < 0 || endIdx < 0) {
1052
+ if (!/^b\d+$/i.test(r.startRef.trim()) && !/^b\d+$/i.test(r.endRef.trim()))
1053
+ return { reject: `unresolved ${r.startRef}..${r.endRef} -> ${startIdx}..${endIdx}`, ...hinted ? { hint: true } : {} };
1054
+ return {};
1055
+ }
1056
+ if (endIdx > callIndex) return { reject: `end idx ${endIdx} > callIndex ${callIndex}`, ...hinted ? { hint: true } : {} };
1057
+ const m = resultText.match(/\[fp=([0-9a-f,-]+)\]/);
1058
+ if (m) {
1059
+ const want = m[1].split(",")[rangeIndex];
1060
+ if (want !== void 0 && want !== "-") {
1061
+ const got = spanFingerprintCoreIdx(coreMessages, startIdx, endIdx);
1062
+ if (want !== got) return { reject: `fp ${r.startRef}..${r.endRef} want ${want} got ${got} @${startIdx}..${endIdx}`, ...hinted ? { hint: true } : {} };
1063
+ }
1064
+ }
1065
+ const remap = {};
1066
+ if (/^m\d+$/i.test(r.startRef.trim()) && rawStartIdx < 0) {
1067
+ const ref = refOfPieceCore(coreMessages, startIdx, byRef);
1068
+ if (!ref) return { reject: `recovered ${r.startRef} @${startIdx} has no ref (protected piece)`, ...hinted ? { hint: true } : {} };
1069
+ remap.startRef = ref;
1070
+ }
1071
+ if (/^m\d+$/i.test(r.endRef.trim()) && rawEndIdx < 0) {
1072
+ const ref = refOfPieceCore(coreMessages, endIdx, byRef);
1073
+ if (!ref) return { reject: `recovered ${r.endRef} @${endIdx} has no ref (protected piece)`, ...hinted ? { hint: true } : {} };
1074
+ remap.endRef = ref;
1075
+ }
1076
+ if (!remap.startRef && !remap.endRef) return {};
1077
+ return { remap, recovered: { pos: pair, startIdx, endIdx } };
1078
+ }
1079
+ function rangeFingerprintsCore(ranges, coreMessages, byRef, blocks) {
1080
+ return ranges.map((r) => {
1081
+ const start = boundaryRawCore(r.startRef, byRef, blocks, coreMessages, "min");
1082
+ const end = start ? boundaryRawCore(r.endRef, byRef, blocks, coreMessages, "max") : "";
1083
+ if (start && end) {
1084
+ const fp = spanFingerprintCore(coreMessages, start, end);
1085
+ if (fp.length > 0) return fp;
1086
+ }
1087
+ return "-";
1088
+ });
1089
+ }
1090
+ function rangePositionsCore(ranges, coreMessages, byRef, blocks) {
1091
+ return ranges.map((r) => {
1092
+ const s = boundaryIndexCore(r.startRef, byRef, blocks, coreMessages, "min");
1093
+ const e = s >= 0 ? boundaryIndexCore(r.endRef, byRef, blocks, coreMessages, "max") : -1;
1094
+ return s >= 0 && e >= 0 ? `${s}-${e}` : "-";
1095
+ });
1096
+ }
922
1097
  export {
923
1098
  ClusterCounter,
924
1099
  WIRE_FORMATS,
925
1100
  anthropicToCore,
1101
+ boundaryIndexCore,
1102
+ boundaryRawCore,
926
1103
  buildSystem,
1104
+ compressToolArgs,
927
1105
  conversationIdentityResponses,
928
1106
  conversationSignalAnthropic,
929
1107
  conversationSignalOpenai,
930
1108
  conversationSignalResponses,
1109
+ corePieceKey,
931
1110
  coreToAnthropic,
932
1111
  coreToOpenai,
933
1112
  coreToResponses,
@@ -935,6 +1114,7 @@ export {
935
1114
  deriveMessageId,
936
1115
  detectWireFormat,
937
1116
  extractSystem,
1117
+ findCompressCallsCore,
938
1118
  hashId,
939
1119
  injectOpenaiSystem,
940
1120
  injectResponsesDeveloperMessage,
@@ -948,9 +1128,17 @@ export {
948
1128
  openaiToCore,
949
1129
  parseDataUrl,
950
1130
  patchResponsesInput,
1131
+ rangeFingerprintsCore,
1132
+ rangePositionsCore,
1133
+ refOfPieceCore,
951
1134
  responsesToCore,
1135
+ spanFingerprintCore,
1136
+ spanFingerprintCoreIdx,
952
1137
  splitDemotedThinking,
1138
+ staleRangeCore,
953
1139
  subagentNamespace,
954
- toCoreMessages
1140
+ toCoreMessages,
1141
+ toolCallNames,
1142
+ toolResultTextsCore
955
1143
  };
956
1144
  //# sourceMappingURL=index.js.map