acp-kernel 0.0.13 → 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.
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,7 +706,7 @@ var renderRefsNode = {
706
706
 
707
707
  // src/protected.ts
708
708
  var ALWAYS_PROTECTED_TOOLS = ["compress"];
709
- var NEVER_PRESERVE_RECENT_TOOLS = ["decompress"];
709
+ var NEVER_PRESERVE_RECENT_TOOLS = ["decompress", "search_context"];
710
710
  function isNeverPreserveRecent(msg) {
711
711
  if (msg.contentType !== "tool-call" && msg.contentType !== "tool-result") {
712
712
  return false;
@@ -2394,44 +2394,291 @@ function extractRanges(input, callId) {
2394
2394
  return ranges;
2395
2395
  }
2396
2396
 
2397
- // src/search.ts
2398
- 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) {
2399
2612
  const limit = options.limit ?? 10;
2400
2613
  const previewLength = options.previewLength ?? 200;
2401
- const minScore = options.minScore ?? 1;
2402
- const terms = query.toLowerCase().trim().split(/\s+/).filter((t) => t.length > 0);
2403
- if (terms.length === 0) return [];
2404
- const active = state.blocks.filter((b) => b.active);
2405
- const scored = [];
2406
- for (const block of active) {
2407
- const text = (block.summary || "").toLowerCase();
2408
- const topic = (block.topic || "").toLowerCase();
2409
- const haystack = topic + " " + text;
2410
- let score = 0;
2411
- for (const term of terms) {
2412
- score += countOccurrences2(haystack, term);
2413
- }
2414
- if (score >= minScore) {
2415
- scored.push({
2416
- blockId: block.blockId,
2417
- tier: block.tier ?? 1,
2418
- score,
2419
- topic: block.topic ?? "(no topic)",
2420
- preview: (block.summary || "").slice(0, previewLength),
2421
- block
2422
- });
2423
- }
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)));
2424
2641
  }
2425
- scored.sort((a, b) => b.score - a.score);
2426
- return scored.slice(0, limit);
2642
+ return buildResults(applyRoleWeight(scoredOrPromise, docs, rw));
2427
2643
  }
2428
- function countOccurrences2(haystack, needle) {
2429
- if (needle.length === 0) return 0;
2430
- 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;
2431
2676
  }
2432
2677
  export {
2433
2678
  BLOCKED_REF,
2434
2679
  COMPRESS_PHILOSOPHY,
2680
+ DEFAULT_ALGORITHM,
2681
+ DEFAULT_ROLE_WEIGHTS,
2435
2682
  HOW_TO_COMPRESS_RULES,
2436
2683
  SUMMARY_HEADER,
2437
2684
  TIER2_DISTILL_RULES,
@@ -2443,6 +2690,7 @@ export {
2443
2690
  applyMessageFilters,
2444
2691
  assignRefs,
2445
2692
  blockById,
2693
+ blockDocs,
2446
2694
  buildRecap,
2447
2695
  buildRestoredContentPreview,
2448
2696
  buildStatusReport,
@@ -2461,14 +2709,17 @@ export {
2461
2709
  findBlocksOverlappingMessages,
2462
2710
  formatRanges,
2463
2711
  getMessageFilter,
2712
+ getSearchAlgorithm,
2464
2713
  hideConsumedCompressCalls,
2465
2714
  highestActiveTier,
2466
2715
  highestUsedIndex,
2467
2716
  indexToRef,
2468
2717
  isMessageProtected,
2469
2718
  listMessageFilters,
2719
+ listSearchAlgorithms,
2470
2720
  makeIO,
2471
2721
  matchToolPattern,
2722
+ messageDocs,
2472
2723
  parseBlockIdArg,
2473
2724
  parseBoundary,
2474
2725
  prune,
@@ -2477,6 +2728,7 @@ export {
2477
2728
  refForRaw,
2478
2729
  refToIndex,
2479
2730
  registerMessageFilter,
2731
+ registerSearchAlgorithm,
2480
2732
  renderNudgeText,
2481
2733
  renderRefsNode,
2482
2734
  renderVisibleRefs,
@@ -2484,6 +2736,7 @@ export {
2484
2736
  resolveKeepMarkers,
2485
2737
  runPipeline,
2486
2738
  searchBlocks,
2739
+ searchBlocksAsync,
2487
2740
  syncBlocks,
2488
2741
  truncateLargeToolOutputs,
2489
2742
  validateConfig