acp-kernel 0.0.12 → 0.0.14

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.
@@ -1 +1 @@
1
- {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAcpB,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAEhB,MAAM,EAEN,WAAW,EAGX,iBAAiB,EAEjB,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,KAAK;IACpB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;IACxD,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAAC;IACvE,YAAY,IAAI,YAAY,EAAE,CAAC;IAC/B,UAAU,CACR,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,gBAAgB,GACtB,gBAAgB,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAAC;IACnE,MAAM,CACJ,KAAK,EAAE,gBAAgB,EACvB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,YAAY,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,EAAE,CAAC;IACJ,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnC;AAED,wBAAgB,UAAU,CAAC,KAAK,GAAE,KAAU,GAAG,eAAe,CAoN7D;AAipBD,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAcpB,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAEhB,MAAM,EAEN,WAAW,EAGX,iBAAiB,EAEjB,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,KAAK;IACpB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;IACxD,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAAC;IACvE,YAAY,IAAI,YAAY,EAAE,CAAC;IAC/B,UAAU,CACR,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,gBAAgB,GACtB,gBAAgB,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAAC;IACnE,MAAM,CACJ,KAAK,EAAE,gBAAgB,EACvB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,YAAY,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,EAAE,CAAC;IACJ,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnC;AAED,wBAAgB,UAAU,CAAC,KAAK,GAAE,KAAU,GAAG,eAAe,CAwN7D;AAwqBD,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -25,8 +25,9 @@ export type { HideConsumedResult } from "./hide-consumed.js";
25
25
  export { rebuildCompressionState } from "./rebuild.js";
26
26
  export type { RebuildResult, RebuildPorts } from "./rebuild.js";
27
27
  export { renderVisibleRefs, renderRefsNode } from "./render-refs.js";
28
- export { searchBlocks } from "./search.js";
29
- export type { SearchResult, SearchOptions } from "./search.js";
28
+ export { searchBlocks, searchBlocksAsync, blockDocs, messageDocs } from "./search.js";
29
+ export type { SearchResult, SearchOptions, SearchAlgorithm, AsyncSearchAlgorithm, AnySearchAlgorithm, SearchDoc, SearchDocKind, ScoredBlock, MessageRole, RoleWeights, MessageInput } from "./search.js";
30
+ export { DEFAULT_ALGORITHM, DEFAULT_ROLE_WEIGHTS, registerSearchAlgorithm, getSearchAlgorithm, listSearchAlgorithms } from "./search.js";
30
31
  export { isMessageProtected, matchToolPattern } from "./protected.js";
31
32
  export { runPipeline, makeIO, type PipelineNode, type PipelineContext, type NodeIO, type NodeEffects, } from "./pipeline.js";
32
33
  export * from "./filter/index.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,YAAY,EACV,KAAK,EACL,eAAe,EACf,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,SAAS,EACT,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,UAAU,EACV,SAAS,EACT,SAAS,EACT,WAAW,GACZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAC3F,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAChE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC/H,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EACH,eAAe,EACf,6BAA6B,EAC7B,kBAAkB,EAClB,eAAe,EACf,2BAA2B,EAC3B,mBAAmB,GACtB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxG,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC5D,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EACL,WAAW,EACX,MAAM,EACN,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,WAAW,GACjB,MAAM,eAAe,CAAC;AACvB,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -706,6 +706,14 @@ var renderRefsNode = {
706
706
 
707
707
  // src/protected.ts
708
708
  var ALWAYS_PROTECTED_TOOLS = ["compress"];
709
+ var NEVER_PRESERVE_RECENT_TOOLS = ["decompress", "search_context"];
710
+ function isNeverPreserveRecent(msg) {
711
+ if (msg.contentType !== "tool-call" && msg.contentType !== "tool-result") {
712
+ return false;
713
+ }
714
+ if (!msg.toolName) return false;
715
+ return NEVER_PRESERVE_RECENT_TOOLS.includes(msg.toolName);
716
+ }
709
717
  function matchToolPattern(toolName, pattern) {
710
718
  if (pattern.endsWith("*")) {
711
719
  return toolName.startsWith(pattern.slice(0, -1));
@@ -802,6 +810,7 @@ function computeProtectedRefs(messages, state, config) {
802
810
  const visible = [];
803
811
  for (const msg of messages) {
804
812
  if (isSyntheticOrPruned(msg, state)) continue;
813
+ if (isNeverPreserveRecent(msg)) continue;
805
814
  const ref = state.messageRefs.byRaw[msg.id];
806
815
  if (!ref || ref === "BLOCKED") continue;
807
816
  visible.push({ ref, tokens: estimateMessageTokens(msg) });
@@ -946,6 +955,7 @@ function createCore(ports = {}) {
946
955
  let blocksCreated = 0;
947
956
  let tokensCompressed = 0;
948
957
  const errors = [];
958
+ const warnings = [];
949
959
  const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(input.messages, input.state, input.config);
950
960
  const preExistingCoverage = collectCoverage(state);
951
961
  const rangeIndexSets = [];
@@ -984,7 +994,8 @@ function createCore(ports = {}) {
984
994
  tokensCompressed: 0,
985
995
  errors: [
986
996
  `content: range (${prev.spec.startRef}..${prev.spec.endRef}) overlaps (${curr.spec.startRef}..${curr.spec.endRef}). Overlapping ranges cannot be compressed in the same batch.`
987
- ]
997
+ ],
998
+ warnings: []
988
999
  }
989
1000
  };
990
1001
  }
@@ -1021,14 +1032,15 @@ function createCore(ports = {}) {
1021
1032
  tokensCompressed: 0,
1022
1033
  errors: [
1023
1034
  `Total compressible content too small (${totalRangeChars} chars across ${input.ranges.length} range(s), min ${input.config.compress.minCompressRange}). Combine more messages into your range(s) to meet the threshold.`
1024
- ]
1035
+ ],
1036
+ warnings: []
1025
1037
  }
1026
1038
  };
1027
1039
  }
1028
1040
  }
1029
1041
  for (const spec of input.ranges) {
1030
1042
  try {
1031
- const compressed = applySingleRange({
1043
+ const outcome = applySingleRange({
1032
1044
  spec,
1033
1045
  messages: input.messages,
1034
1046
  state,
@@ -1039,7 +1051,8 @@ function createCore(ports = {}) {
1039
1051
  preExistingCoverage
1040
1052
  });
1041
1053
  blocksCreated++;
1042
- tokensCompressed += compressed;
1054
+ tokensCompressed += outcome.tokens;
1055
+ warnings.push(...outcome.warnings);
1043
1056
  } catch (error) {
1044
1057
  errors.push(error instanceof Error ? error.message : String(error));
1045
1058
  }
@@ -1051,7 +1064,7 @@ function createCore(ports = {}) {
1051
1064
  state.nudge.lastNudgeShownTokens = 0;
1052
1065
  state.nudge.lastShownByTier = {};
1053
1066
  }
1054
- return { state, result: { blocksCreated, tokensCompressed, errors } };
1067
+ return { state, result: { blocksCreated, tokensCompressed, errors, warnings } };
1055
1068
  }
1056
1069
  function processTurn(input) {
1057
1070
  const configErrors = validateConfig(input.config);
@@ -1234,6 +1247,7 @@ var emergencyTruncateNode = {
1234
1247
  }
1235
1248
  };
1236
1249
  function applySingleRange(input) {
1250
+ const warnings = [];
1237
1251
  const resolved = resolveBoundaries({
1238
1252
  startRef: input.spec.startRef,
1239
1253
  endRef: input.spec.endRef,
@@ -1281,7 +1295,7 @@ function applySingleRange(input) {
1281
1295
  const directMessageIds = [...effectiveMessageIds].filter(
1282
1296
  (id) => !input.preExistingCoverage.has(id)
1283
1297
  );
1284
- const filteredIds = filterProtectedToolMessages(
1298
+ let filteredIds = filterProtectedToolMessages(
1285
1299
  directMessageIds,
1286
1300
  input.messages,
1287
1301
  input.config
@@ -1292,12 +1306,22 @@ function applySingleRange(input) {
1292
1306
  return ref !== void 0 && protectedRefs.has(ref);
1293
1307
  }) : [];
1294
1308
  if (hitProtectedRaw.length > 0) {
1295
- const recentN = input.config.preserveRecentMessages;
1296
- const hitRefs = hitProtectedRaw.map((id) => input.state.messageRefs.byRaw[id]).filter(Boolean);
1297
- throw new Error(
1298
- `Range includes protected messages (the last ${recentN} messages and/or the most recent user message) which must not be compressed: ${hitRefs.join(
1309
+ const protectedSet = new Set(hitProtectedRaw);
1310
+ filteredIds = filteredIds.filter((id) => !protectedSet.has(id));
1311
+ for (const id of hitProtectedRaw) effectiveMessageIds.delete(id);
1312
+ const hitRefs = hitProtectedRaw.map((id) => input.state.messageRefs.byRaw[id]).filter((v) => typeof v === "string");
1313
+ if (filteredIds.length === 0 && consumedBlockIds.length === 0) {
1314
+ const recentN = input.config.preserveRecentMessages;
1315
+ throw new Error(
1316
+ `Range is entirely within the protected zone (the last ${recentN} messages and/or the most recent user message): ${hitRefs.join(
1317
+ ", "
1318
+ )}. Adjust startId/endId to older messages.`
1319
+ );
1320
+ }
1321
+ warnings.push(
1322
+ `Excluded ${hitProtectedRaw.length} protected message(s) ${hitRefs.join(
1299
1323
  ", "
1300
- )}. Adjust startId/endId to exclude them.`
1324
+ )} from compression range (recent/last-user zone).`
1301
1325
  );
1302
1326
  }
1303
1327
  validateCompressionRange(input, filteredIds, consumedBlockIds.length);
@@ -1334,7 +1358,7 @@ function applySingleRange(input) {
1334
1358
  const consumed = blockById(input.state, consumedId);
1335
1359
  if (consumed) consumed.active = false;
1336
1360
  }
1337
- return compressedTokens;
1361
+ return { tokens: compressedTokens, warnings };
1338
1362
  }
1339
1363
  function applyToolPairAdjustment(resolved, messages) {
1340
1364
  if (resolved.boundaryKind === "block") {
@@ -2370,44 +2394,291 @@ function extractRanges(input, callId) {
2370
2394
  return ranges;
2371
2395
  }
2372
2396
 
2373
- // src/search.ts
2374
- function searchBlocks(state, query, options = {}) {
2397
+ // src/search/algorithms/substring.ts
2398
+ var substringAlgorithm = {
2399
+ name: "substring",
2400
+ description: "Exact substring counting (original baseline). Predictable, no normalization.",
2401
+ score(docs, query) {
2402
+ const terms = query.toLowerCase().trim().split(/\s+/).filter((t) => t.length > 0);
2403
+ if (terms.length === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
2404
+ return docs.map((d) => {
2405
+ const haystack = d.text.toLowerCase();
2406
+ let score = 0;
2407
+ for (const term of terms) score += countOccurrences2(haystack, term);
2408
+ return { ref: d.ref, score };
2409
+ });
2410
+ }
2411
+ };
2412
+ function countOccurrences2(haystack, needle) {
2413
+ if (!needle) return 0;
2414
+ return haystack.split(needle).length - 1;
2415
+ }
2416
+
2417
+ // src/search/stemmer.ts
2418
+ function stem(word) {
2419
+ let w = word;
2420
+ if (w.length <= 3) return w;
2421
+ if (w.endsWith("ies")) w = w.slice(0, -3) + "y";
2422
+ else if (w.endsWith("ses") || w.endsWith("xes") || w.endsWith("zes")) w = w.slice(0, -2);
2423
+ else if (w.endsWith("ches") || w.endsWith("shes")) w = w.slice(0, -2);
2424
+ else if (w.endsWith("s") && !w.endsWith("ss")) w = w.slice(0, -1);
2425
+ if (w.endsWith("ing") && w.length > 5) w = w.slice(0, -3);
2426
+ if (w.endsWith("ed") && w.length > 4) w = w.slice(0, -2);
2427
+ if (w.endsWith("ation") && w.length > 6) w = w.slice(0, -3);
2428
+ else if (w.endsWith("tion") && w.length > 5) w = w.slice(0, -4) + "t";
2429
+ else if (w.endsWith("ion") && w.length > 4) w = w.slice(0, -3);
2430
+ if (w.endsWith("ment") && w.length > 6) w = w.slice(0, -4);
2431
+ if (w.endsWith("ness") && w.length > 6) w = w.slice(0, -4);
2432
+ if (w.endsWith("ly") && w.length > 4) w = w.slice(0, -2);
2433
+ return w;
2434
+ }
2435
+
2436
+ // src/search/tokenizer.ts
2437
+ var CJK = /[\u3400-\u9fff\uf900-\ufaff\u3040-\u30ff\uac00-\ud7af]/;
2438
+ var CJK_RUN = new RegExp(`${CJK.source}+`, "g");
2439
+ var LATIN_WORD = /[a-z][a-z0-9_]*[a-z0-9]|[a-z0-9]/g;
2440
+ function tokenize(text, opts = {}) {
2441
+ const lower = text.toLowerCase();
2442
+ const tokens = [];
2443
+ const latin = lower.match(LATIN_WORD) ?? [];
2444
+ for (let w of latin) {
2445
+ if (w.length >= 2) {
2446
+ if (opts.stem) w = stem(w);
2447
+ tokens.push(w);
2448
+ }
2449
+ }
2450
+ const cjkRuns = lower.match(CJK_RUN) ?? [];
2451
+ for (const run of cjkRuns) {
2452
+ if (run.length === 1) {
2453
+ tokens.push(run);
2454
+ } else {
2455
+ for (let i = 0; i < run.length - 1; i++) tokens.push(run.slice(i, i + 2));
2456
+ for (const ch of run) tokens.push(ch);
2457
+ }
2458
+ }
2459
+ return tokens;
2460
+ }
2461
+ function charBigrams(text) {
2462
+ const grams = [];
2463
+ for (let i = 0; i < text.length - 1; i++) {
2464
+ const pair = text.slice(i, i + 2);
2465
+ if (pair.trim().length === pair.length) grams.push(pair);
2466
+ }
2467
+ return grams;
2468
+ }
2469
+ function tfMap(text, stem2) {
2470
+ const m = /* @__PURE__ */ new Map();
2471
+ for (const t of tokenize(text, { stem: stem2 })) m.set(t, (m.get(t) ?? 0) + 1);
2472
+ return m;
2473
+ }
2474
+
2475
+ // src/search/algorithms/bm25.ts
2476
+ var bm25Algorithm = {
2477
+ name: "bm25",
2478
+ description: "BM25 with stemming + CJK bigram tokenization. IR-standard relevance ranking.",
2479
+ score(docs, query) {
2480
+ const N = docs.length;
2481
+ const k1 = 1.2;
2482
+ const b = 0.75;
2483
+ const parsed = docs.map((d) => {
2484
+ const text = d.text;
2485
+ const tf = tfMap(text, true);
2486
+ let len = 0;
2487
+ for (const v of tf.values()) len += v;
2488
+ return { id: d.ref, tf, len };
2489
+ });
2490
+ const avgdl = parsed.reduce((s, d) => s + d.len, 0) / (N || 1);
2491
+ const qTerms = tokenize(query, { stem: true });
2492
+ if (qTerms.length === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
2493
+ const idf = /* @__PURE__ */ new Map();
2494
+ for (const t of new Set(qTerms)) {
2495
+ let df = 0;
2496
+ for (const d of parsed) if (d.tf.has(t)) df++;
2497
+ idf.set(t, Math.log(1 + (N - df + 0.5) / (df + 0.5)));
2498
+ }
2499
+ return parsed.map((d) => {
2500
+ let score = 0;
2501
+ for (const t of qTerms) {
2502
+ const f = d.tf.get(t) ?? 0;
2503
+ if (f === 0) continue;
2504
+ const idfT = idf.get(t) ?? 0;
2505
+ score += idfT * (f * (k1 + 1)) / (f + k1 * (1 - b + b * d.len / (avgdl || 1)));
2506
+ }
2507
+ return { ref: d.id, score };
2508
+ });
2509
+ }
2510
+ };
2511
+
2512
+ // src/search/algorithms/fuzzy.ts
2513
+ var fuzzyAlgorithm = {
2514
+ name: "fuzzy",
2515
+ description: "Character bigram overlap. Typo-tolerant, script-agnostic, high recall.",
2516
+ score(docs, query) {
2517
+ const qTokens = query.toLowerCase().split(/[\s,]+/).filter((t) => t.length >= 4);
2518
+ if (qTokens.length === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
2519
+ const qGrams = /* @__PURE__ */ new Set();
2520
+ for (const t of qTokens) for (const g of charBigrams(t)) qGrams.add(g);
2521
+ if (qGrams.size === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
2522
+ return docs.map((d) => {
2523
+ const haystack = d.text.toLowerCase();
2524
+ const docGrams = new Set(charBigrams(haystack));
2525
+ let hits = 0;
2526
+ for (const g of qGrams) if (docGrams.has(g)) hits++;
2527
+ return { ref: d.ref, score: hits / qGrams.size };
2528
+ });
2529
+ }
2530
+ };
2531
+
2532
+ // src/search/algorithms/hybrid.ts
2533
+ var W_BM25 = 0.7;
2534
+ var W_FUZZY = 0.3;
2535
+ var hybridAlgorithm = {
2536
+ name: "hybrid",
2537
+ description: "Weighted BM25(stem) + fuzzy n-gram. Default \u2014 best precision + recall.",
2538
+ score(docs, query) {
2539
+ const bm = bm25Algorithm.score(docs, query);
2540
+ const fz = fuzzyAlgorithm.score(docs, query);
2541
+ const maxBm = Math.max(...bm.map((r) => r.score), 1e-9);
2542
+ const maxFz = Math.max(...fz.map((r) => r.score), 1e-9);
2543
+ const bmMap = new Map(bm.map((r) => [r.ref, r.score / maxBm]));
2544
+ const fzMap = new Map(fz.map((r) => [r.ref, r.score / maxFz]));
2545
+ return docs.map((d) => ({
2546
+ ref: d.ref,
2547
+ score: W_BM25 * (bmMap.get(d.ref) ?? 0) + W_FUZZY * (fzMap.get(d.ref) ?? 0)
2548
+ }));
2549
+ }
2550
+ };
2551
+
2552
+ // src/search/registry.ts
2553
+ var registry2 = /* @__PURE__ */ new Map();
2554
+ function registerSearchAlgorithm(algo) {
2555
+ registry2.set(algo.name, algo);
2556
+ }
2557
+ function getSearchAlgorithm(name) {
2558
+ return registry2.get(name);
2559
+ }
2560
+ function listSearchAlgorithms() {
2561
+ return [...registry2.values()];
2562
+ }
2563
+ registerSearchAlgorithm(substringAlgorithm);
2564
+ registerSearchAlgorithm(bm25Algorithm);
2565
+ registerSearchAlgorithm(fuzzyAlgorithm);
2566
+ registerSearchAlgorithm(hybridAlgorithm);
2567
+
2568
+ // src/search/types.ts
2569
+ var DEFAULT_ROLE_WEIGHTS = {
2570
+ user: 1.5,
2571
+ assistant: 1,
2572
+ tool: 0.6,
2573
+ block: 1
2574
+ };
2575
+ var DEFAULT_ALGORITHM = "hybrid";
2576
+
2577
+ // src/search/index.ts
2578
+ function blockDocs(state) {
2579
+ return state.blocks.map((b) => ({
2580
+ kind: "block",
2581
+ ref: b.blockId,
2582
+ text: `${b.topic ?? ""} ${b.summary ?? ""}`,
2583
+ title: b.topic ?? b.blockId,
2584
+ blockId: b.blockId,
2585
+ tier: b.tier ?? 1,
2586
+ tokens: b.compressedTokens
2587
+ }));
2588
+ }
2589
+ function messageDocs(msgs) {
2590
+ return msgs.map((m) => ({
2591
+ kind: "message",
2592
+ ref: m.ref,
2593
+ text: m.text,
2594
+ title: `${m.role}: ${m.text.slice(0, 60)}`,
2595
+ role: m.role,
2596
+ blockId: m.blockId,
2597
+ tier: m.tier,
2598
+ tokens: m.tokens
2599
+ }));
2600
+ }
2601
+ function applyRoleWeight(scored, docs, rw) {
2602
+ if (docs.length === 0) return scored;
2603
+ const docByRef = new Map(docs.map((d) => [d.ref, d]));
2604
+ return scored.map((s) => {
2605
+ const doc = docByRef.get(s.ref);
2606
+ if (!doc) return s;
2607
+ const w = doc.kind === "message" ? doc.role === "user" ? rw.user : doc.role === "assistant" ? rw.assistant : rw.tool : rw.block;
2608
+ return { ref: s.ref, score: s.score * w };
2609
+ });
2610
+ }
2611
+ function runSearch(docs, query, options) {
2375
2612
  const limit = options.limit ?? 10;
2376
2613
  const previewLength = options.previewLength ?? 200;
2377
- const minScore = options.minScore ?? 1;
2378
- const terms = query.toLowerCase().trim().split(/\s+/).filter((t) => t.length > 0);
2379
- if (terms.length === 0) return [];
2380
- const active = state.blocks.filter((b) => b.active);
2381
- const scored = [];
2382
- for (const block of active) {
2383
- const text = (block.summary || "").toLowerCase();
2384
- const topic = (block.topic || "").toLowerCase();
2385
- const haystack = topic + " " + text;
2386
- let score = 0;
2387
- for (const term of terms) {
2388
- score += countOccurrences2(haystack, term);
2389
- }
2390
- if (score >= minScore) {
2391
- scored.push({
2392
- blockId: block.blockId,
2393
- tier: block.tier ?? 1,
2394
- score,
2395
- topic: block.topic ?? "(no topic)",
2396
- preview: (block.summary || "").slice(0, previewLength),
2397
- block
2398
- });
2399
- }
2614
+ const minScore = options.minScore ?? 0.01;
2615
+ const algoName = options.algorithm ?? DEFAULT_ALGORITHM;
2616
+ const rw = { ...DEFAULT_ROLE_WEIGHTS, ...options.roleWeights };
2617
+ const algo = getSearchAlgorithm(algoName);
2618
+ if (!algo) return [];
2619
+ if (docs.length === 0) return [];
2620
+ const scoredOrPromise = algo.score(docs, query);
2621
+ const buildResults = (weighted) => {
2622
+ const byRef = new Map(docs.map((d) => [d.ref, d]));
2623
+ return weighted.map((s) => {
2624
+ const doc = byRef.get(s.ref);
2625
+ if (!doc) return null;
2626
+ return {
2627
+ kind: doc.kind,
2628
+ ref: doc.ref,
2629
+ blockId: doc.blockId,
2630
+ tier: doc.tier ?? 1,
2631
+ score: s.score,
2632
+ title: doc.title,
2633
+ preview: makePreview(doc.text, query, previewLength),
2634
+ role: doc.role,
2635
+ tokens: doc.tokens
2636
+ };
2637
+ }).filter((r) => r !== null && r.score >= minScore).sort((a, b) => b.score - a.score).slice(0, limit);
2638
+ };
2639
+ if (scoredOrPromise instanceof Promise) {
2640
+ return scoredOrPromise.then((raw) => buildResults(applyRoleWeight(raw, docs, rw)));
2400
2641
  }
2401
- scored.sort((a, b) => b.score - a.score);
2402
- return scored.slice(0, limit);
2642
+ return buildResults(applyRoleWeight(scoredOrPromise, docs, rw));
2403
2643
  }
2404
- function countOccurrences2(haystack, needle) {
2405
- if (needle.length === 0) return 0;
2406
- return haystack.split(needle).length - 1;
2644
+ function searchBlocks(docs, query, options = {}) {
2645
+ const result = runSearch(docs, query, options);
2646
+ if (result instanceof Promise) {
2647
+ throw new Error(
2648
+ `searchBlocks: algorithm "${options.algorithm ?? DEFAULT_ALGORITHM}" is async (e.g. semantic). Use searchBlocksAsync() instead.`
2649
+ );
2650
+ }
2651
+ return result;
2652
+ }
2653
+ async function searchBlocksAsync(docs, query, options = {}) {
2654
+ return await runSearch(docs, query, options);
2655
+ }
2656
+ function makePreview(text, query, len) {
2657
+ if (!text) return "";
2658
+ const terms = query.toLowerCase().trim().split(/\s+/).filter((t) => t.length > 1);
2659
+ if (terms.length === 0) return text.slice(0, len);
2660
+ const lower = text.toLowerCase();
2661
+ let hitIdx = -1;
2662
+ for (const term of terms) {
2663
+ const idx = lower.indexOf(term);
2664
+ if (idx >= 0) {
2665
+ hitIdx = idx;
2666
+ break;
2667
+ }
2668
+ }
2669
+ if (hitIdx < 0) return text.slice(0, len);
2670
+ const half = Math.max(0, Math.floor(len / 2) - 10);
2671
+ const start = Math.max(0, hitIdx - half);
2672
+ const end = Math.min(text.length, start + len);
2673
+ const prefix = start > 0 ? "\u2026" : "";
2674
+ const suffix = end < text.length ? "\u2026" : "";
2675
+ return prefix + text.slice(start, end).trim() + suffix;
2407
2676
  }
2408
2677
  export {
2409
2678
  BLOCKED_REF,
2410
2679
  COMPRESS_PHILOSOPHY,
2680
+ DEFAULT_ALGORITHM,
2681
+ DEFAULT_ROLE_WEIGHTS,
2411
2682
  HOW_TO_COMPRESS_RULES,
2412
2683
  SUMMARY_HEADER,
2413
2684
  TIER2_DISTILL_RULES,
@@ -2419,6 +2690,7 @@ export {
2419
2690
  applyMessageFilters,
2420
2691
  assignRefs,
2421
2692
  blockById,
2693
+ blockDocs,
2422
2694
  buildRecap,
2423
2695
  buildRestoredContentPreview,
2424
2696
  buildStatusReport,
@@ -2437,14 +2709,17 @@ export {
2437
2709
  findBlocksOverlappingMessages,
2438
2710
  formatRanges,
2439
2711
  getMessageFilter,
2712
+ getSearchAlgorithm,
2440
2713
  hideConsumedCompressCalls,
2441
2714
  highestActiveTier,
2442
2715
  highestUsedIndex,
2443
2716
  indexToRef,
2444
2717
  isMessageProtected,
2445
2718
  listMessageFilters,
2719
+ listSearchAlgorithms,
2446
2720
  makeIO,
2447
2721
  matchToolPattern,
2722
+ messageDocs,
2448
2723
  parseBlockIdArg,
2449
2724
  parseBoundary,
2450
2725
  prune,
@@ -2453,6 +2728,7 @@ export {
2453
2728
  refForRaw,
2454
2729
  refToIndex,
2455
2730
  registerMessageFilter,
2731
+ registerSearchAlgorithm,
2456
2732
  renderNudgeText,
2457
2733
  renderRefsNode,
2458
2734
  renderVisibleRefs,
@@ -2460,6 +2736,7 @@ export {
2460
2736
  resolveKeepMarkers,
2461
2737
  runPipeline,
2462
2738
  searchBlocks,
2739
+ searchBlocksAsync,
2463
2740
  syncBlocks,
2464
2741
  truncateLargeToolOutputs,
2465
2742
  validateConfig