@maximem/synap-js-sdk 0.5.7 → 0.5.9
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 +5 -1
- package/dist/anticipation-cache-LPNKMRBH.cjs +20 -0
- package/dist/anticipation-cache-LPNKMRBH.cjs.map +1 -0
- package/dist/anticipation-cache-RWKF55J4.js +20 -0
- package/dist/{chunk-FXGLT6AT.js → chunk-I7TWR4S6.js} +59 -3
- package/dist/chunk-I7TWR4S6.js.map +1 -0
- package/dist/{chunk-T6U5PVAD.cjs → chunk-ICUFS3B5.cjs} +449 -12
- package/dist/chunk-ICUFS3B5.cjs.map +1 -0
- package/dist/{chunk-ODVOQOSO.js → chunk-R72YL6Q4.js} +445 -8
- package/dist/chunk-R72YL6Q4.js.map +1 -0
- package/dist/{chunk-HCULAVJC.cjs → chunk-RE4DDIWO.cjs} +66 -10
- package/dist/chunk-RE4DDIWO.cjs.map +1 -0
- package/dist/chunk-T372OL3Q.cjs.map +1 -1
- package/dist/chunk-ZKOJDUN4.cjs.map +1 -1
- package/dist/env-55ULHNJZ.cjs.map +1 -1
- package/dist/{errors-Bl3vY1eb.d.cts → errors-Bdayqlzy.d.cts} +88 -2
- package/dist/{errors-Bl3vY1eb.d.ts → errors-Bdayqlzy.d.ts} +88 -2
- package/dist/grpc/index.cjs +4 -4
- package/dist/grpc/index.cjs.map +1 -1
- package/dist/grpc/index.d.cts +8 -3
- package/dist/grpc/index.d.ts +8 -3
- package/dist/grpc/index.js +3 -3
- package/dist/index.cjs +1304 -116
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +281 -5
- package/dist/index.d.ts +281 -5
- package/dist/index.js +1257 -69
- package/dist/index.js.map +1 -1
- package/dist/onnx-encoder-4RSL6ID4.cjs +103 -0
- package/dist/onnx-encoder-4RSL6ID4.cjs.map +1 -0
- package/dist/onnx-encoder-NP35KH4H.js +103 -0
- package/dist/onnx-encoder-NP35KH4H.js.map +1 -0
- package/dist/{stream-client-VWKINE47.js → stream-client-3WHXGW4U.js} +2 -2
- package/dist/stream-client-L6OQPZN7.cjs +16 -0
- package/dist/stream-client-L6OQPZN7.cjs.map +1 -0
- package/package.json +4 -2
- package/dist/anticipation-cache-LRE3TLK4.cjs +0 -14
- package/dist/anticipation-cache-LRE3TLK4.cjs.map +0 -1
- package/dist/anticipation-cache-WLU4NMD5.js +0 -14
- package/dist/chunk-FXGLT6AT.js.map +0 -1
- package/dist/chunk-HCULAVJC.cjs.map +0 -1
- package/dist/chunk-ODVOQOSO.js.map +0 -1
- package/dist/chunk-T6U5PVAD.cjs.map +0 -1
- package/dist/stream-client-N46JBT5A.cjs +0 -16
- package/dist/stream-client-N46JBT5A.cjs.map +0 -1
- /package/dist/{anticipation-cache-WLU4NMD5.js.map → anticipation-cache-RWKF55J4.js.map} +0 -0
- /package/dist/{stream-client-VWKINE47.js.map → stream-client-3WHXGW4U.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/behavior/anticipation.json","../src/behavior/contract.ts","../src/cache/bm25.ts","../src/context/anticipation-cache.ts"],"sourcesContent":["{\n \"$comment\": \"SOURCE OF TRUTH for anticipation-cache behavior, shared by the Python and JS/TS SDKs. Do not duplicate these values into either SDK's source. See ../README.md. Regexes are restricted to the intersection of Python 're' and ECMAScript; both SDKs have a parity test.\",\n \"version\": 1,\n \"recall_bypass\": {\n \"note\": \"WS4 of the playground query-hygiene plan. A recall-shaped question never serves from the anticipation cache. Asymmetric cost is why: a stale false-hit makes the agent answer 'I don't see that' while the store holds the fact (the BZQR6J cross-session failure), whereas bypassing costs only cold-fetch latency on a turn where the user already expects a lookup. Stem coverage cannot gate this \\u2014 false hits were measured at coverage=1.00. Ties break toward INCLUDING a pattern.\",\n \"env_flag\": \"SYNAP_SDK_CACHE_RECALL_BYPASS\",\n \"default\": false,\n \"patterns\": [\n {\n \"pattern\": \"\\\\bremind me\\\\b\",\n \"flags\": \"i\",\n \"note\": \"Original set (WS4, playground query-hygiene plan).\"\n },\n {\n \"pattern\": \"\\\\bon file\\\\b\",\n \"flags\": \"i\",\n \"note\": \"Original set (WS4).\"\n },\n {\n \"pattern\": \"\\\\b(your|my|the) records\\\\b\",\n \"flags\": \"i\",\n \"note\": \"Original set (WS4).\"\n },\n {\n \"pattern\": \"\\\\bdo you (have|know|remember|recall|see)\\\\b\",\n \"flags\": \"i\",\n \"note\": \"Original set (WS4).\"\n },\n {\n \"pattern\": \"\\\\bwhat (do|did) (i|you) (say|tell|mention|have)\\\\b\",\n \"flags\": \"i\",\n \"note\": \"Original set (WS4).\"\n },\n {\n \"pattern\": \"\\\\bwhat('s| is) my\\\\b\",\n \"flags\": \"i\",\n \"note\": \"Original set (WS4).\"\n },\n {\n \"pattern\": \"\\\\bwhere (am i|do i live)\\\\b\",\n \"flags\": \"i\",\n \"note\": \"Original set (WS4).\"\n },\n {\n \"pattern\": \"\\\\bhow long have i\\\\b\",\n \"flags\": \"i\",\n \"note\": \"Original set (WS4).\"\n },\n {\n \"pattern\": \"\\\\bwhich of my\\\\b\",\n \"flags\": \"i\",\n \"note\": \"Original set (WS4).\"\n },\n {\n \"pattern\": \"\\\\bwhat .{0,40}\\\\b(again|earlier|last time|previously|yesterday)\\\\b\",\n \"flags\": \"i\",\n \"note\": \"Original set (WS4). 'again' alone is deliberately NOT a marker: 'book me a ride again' is a task query, not a recall query.\"\n },\n {\n \"pattern\": \"\\\\bwill you use to (contact|reach|notify)\\\\b\",\n \"flags\": \"i\",\n \"note\": \"Original set (WS4).\"\n },\n {\n \"pattern\": \"\\\\b(confirm|verify|double.?check)\\\\b.{0,40}\\\\bmy\\\\b\",\n \"flags\": \"i\",\n \"note\": \"2026-07-16 eval register issue #11: 'can you confirm my email address?' served a stale bundle. Also catches 'confirm my booking' \\u2014 an intentional over-match, costing one cloud fetch.\"\n },\n {\n \"pattern\": \"\\\\b(linked to|associated with|registered (to|on|with)) my\\\\b\",\n \"flags\": \"i\",\n \"note\": \"2026-07-16 eval register issue #11: 'what name is linked to my Uber account?' served a stale bundle.\"\n },\n {\n \"pattern\": \"\\\\b(best|preferred) way to (reach|contact|update|notify) me\\\\b\",\n \"flags\": \"i\",\n \"note\": \"2026-07-16 eval register issue #11: 'best way to contact me with updates' served a stale bundle.\"\n },\n {\n \"pattern\": \"\\\\bhow (do|will|can|should) you (reach|contact|notify) me\\\\b\",\n \"flags\": \"i\",\n \"note\": \"2026-07-16 eval register issue #11, sibling phrasing of the above.\"\n },\n {\n \"pattern\": \"\\\\bwhich .{0,40}\\\\b(are|will) you (going to )?(use|using)\\\\b\",\n \"flags\": \"i\",\n \"note\": \"2026-07-16 eval register issue #11: 'which email are you going to use?' served a stale bundle.\"\n },\n {\n \"pattern\": \"\\\\bon (my|the) account\\\\b\",\n \"flags\": \"i\",\n \"note\": \"2026-07-16 eval register issue #11.\"\n }\n ]\n },\n \"thresholds\": {\n \"bm25\": {\n \"value\": 1.5,\n \"note\": \"Nominal threshold. The value actually applied is derived per query: max(effective_floor, min(bm25, query_token_scale * len(query_tokens))). Short queries cannot reach the nominal score, so holding them to it would miss every time.\"\n },\n \"novel_term\": {\n \"value\": 0.45,\n \"note\": \"Fraction of query stems absent from the cached corpus that forces a miss.\"\n },\n \"min_corpus_for_novel_gate\": {\n \"value\": 200,\n \"note\": \"The novel-term gate is meaningless on a small corpus, where the ratio is dominated by ordinary English word forms. Measured: a fresh playground conversation reaches ~60 stems after the first push and would trip the 0.45 gate on essentially every second-turn query. 200 means the gate engages once a customer has ~3-4 typical bundles cached.\"\n },\n \"effective_floor\": {\n \"value\": 0.6,\n \"note\": \"Lower bound on the derived per-query threshold. Below this, BM25 stops discriminating.\"\n },\n \"query_token_scale\": {\n \"value\": 0.3,\n \"note\": \"Scales the threshold down for short queries: 0.3 points of score per query token.\"\n }\n },\n \"bm25\": {\n \"k1\": 1.5,\n \"b\": 0.75,\n \"idf_floor\": 0.5,\n \"min_token_len\": 2,\n \"min_stem_len\": 4,\n \"suffixes\": [\n \"ences\",\n \"ances\",\n \"ation\",\n \"tion\",\n \"sion\",\n \"ence\",\n \"ance\",\n \"ness\",\n \"ment\",\n \"able\",\n \"ible\",\n \"ical\",\n \"ally\",\n \"ies\",\n \"ive\",\n \"ful\",\n \"ous\",\n \"ing\",\n \"ary\",\n \"ly\",\n \"ed\",\n \"es\",\n \"s\"\n ],\n \"stop_words\": [\n \"a\",\n \"about\",\n \"after\",\n \"an\",\n \"and\",\n \"are\",\n \"at\",\n \"be\",\n \"been\",\n \"before\",\n \"being\",\n \"but\",\n \"by\",\n \"can\",\n \"could\",\n \"did\",\n \"do\",\n \"does\",\n \"for\",\n \"from\",\n \"had\",\n \"has\",\n \"have\",\n \"how\",\n \"if\",\n \"in\",\n \"into\",\n \"is\",\n \"it\",\n \"its\",\n \"may\",\n \"might\",\n \"no\",\n \"nor\",\n \"not\",\n \"of\",\n \"on\",\n \"or\",\n \"over\",\n \"should\",\n \"so\",\n \"than\",\n \"that\",\n \"the\",\n \"then\",\n \"these\",\n \"this\",\n \"those\",\n \"to\",\n \"was\",\n \"were\",\n \"what\",\n \"when\",\n \"where\",\n \"which\",\n \"who\",\n \"whom\",\n \"why\",\n \"will\",\n \"with\",\n \"would\"\n ],\n \"alias_token\": {\n \"pattern\": \"\\\\[\\\\[([A-Za-z][A-Za-z0-9_]*_[a-z0-9]{12})\\\\]\\\\]\",\n \"note\": \"An alias label as it appears in text: [[PERSON_PHONE_h2n7v5cx8m0d]]. Kept whole rather than split, because 'person' and 'phone' are boilerplate shared by every label of that field type and would make two different people's memories score alike. Mirrors synap/cloud/shared/tokenizer.py \\u2014 keep the two in step.\"\n },\n \"suffix_note\": \"Order matters: longest-first, because _stem returns on first match. Reordering this list silently changes every stem and therefore every BM25 score.\"\n },\n \"env_flags\": {\n \"$comment\": \"Read at CALL time, never at module load. Python does os.environ.get inside the function bodies; JS must do the same or late process.env mutation stops working and the flag looks broken. See gotcha G-S.\",\n \"SYNAP_SDK_CACHE_RECALL_BYPASS\": {\n \"default\": false,\n \"truthy\": [\n \"true\",\n \"1\",\n \"yes\"\n ]\n },\n \"SYNAP_SDK_CACHE_HONOR_TTL_HINT\": {\n \"default\": false,\n \"truthy\": [\n \"true\",\n \"1\",\n \"yes\"\n ],\n \"note\": \"A bundle's server ttl_hint_seconds may only SHORTEN the global TTL, never extend it.\"\n },\n \"SYNAP_SDK_CACHE_INVALIDATE_ON_WRITE\": {\n \"default\": false,\n \"truthy\": [\n \"true\",\n \"1\",\n \"yes\"\n ],\n \"note\": \"2026-07-16 eval register issue #8: the cloud invalidates its own Redis caches on ingest, but that stops at the process boundary.\"\n },\n \"SYNAP_SDK_CACHE_MAX_ENTRY_AGE\": {\n \"default\": null,\n \"note\": \"2026-07-16 eval register issue #6: stored_at is a sliding lease renewed on hit, so under steady traffic a pre-update snapshot never expired. Defaults to 2x the sliding TTL; 0 disables.\"\n },\n \"SYNAP_SDK_CACHE_COVERAGE_MIN\": {\n \"default\": null\n }\n },\n \"coverage_gate\": {\n \"note\": \"Fraction of the query's stems present in the picked items. A bundle can clear the BM25 threshold on one shared token ('account') while lacking the asked-for fact entirely: the false HIT behind the eval's 'forgot the name' failure. ALWAYS computed and reported; ENFORCED only when SYNAP_SDK_CACHE_COVERAGE_MIN is explicitly set, so default behavior is unchanged. When it does fire it must gate BEFORE any side effect, so a rejected lookup leaves cache state untouched.\",\n \"env_flag\": \"SYNAP_SDK_CACHE_COVERAGE_MIN\",\n \"default\": null,\n \"empty_query_coverage\": 1.0\n },\n \"telemetry\": {\n \"rejected_items_cap\": {\n \"value\": 20,\n \"note\": \"Rejected items reported per lookup, highest score first, so a large cache cannot bloat the payload.\"\n },\n \"content_preview_chars\": 120\n },\n \"rung_key\": {\n \"$comment\": \"SOURCE OF TRUTH for how a scope rung is written on the wire and compared in both SDK anticipation caches. The server stamps a pushed bundle with this string (ContextBundleProto.anticipation_scope_rung) and each SDK builds the same string from the `scope` path its own fetch names; a bundle is servable only when the two agree. Three implementations exist by necessity (server Python, SDK Python, SDK TypeScript) because the producer is not an SDK, so the vectors below are what stops them drifting.\",\n \"note\": \"Node ids are deliberately not what travels. They are registry surrogates and an SDK in a customer's process has never seen one; a caller names a rung with level keys and its own external ids, so that is the vocabulary the comparison happens in. Level keys are ^[a-z][a-z0-9_]{0,63}$ so they can hold neither separator and only the value needs escaping.\",\n \"separator\": \"|\",\n \"assign\": \"=\",\n \"sort\": \"by level key, ascending, plain codepoint order\",\n \"escapes\": {\n \"\\\\\": \"\\\\\\\\\",\n \"|\": \"\\\\p\",\n \"=\": \"\\\\e\"\n },\n \"escape_order\": \"backslash first. Reversing it makes `a|b` and `a\\\\pb` encode to the same string, which is a false match between two different rungs.\",\n \"absent\": \"null (an empty proto string on the wire) means the bundle or the request named no rung. Serving requires the two to be EQUAL, so absent matches only absent. That leaves every client without nested scoping byte-identical to today and refuses every genuinely cross-rung serve; the cost of the rule being wrong is a cold fetch, and the cost of a subset rule being wrong is one person's context handed to another.\",\n \"vectors\": [\n {\n \"path\": {},\n \"key\": null,\n \"note\": \"no path named\"\n },\n {\n \"path\": {\n \"customer\": \"acme\"\n },\n \"key\": \"customer=acme\"\n },\n {\n \"path\": {\n \"customer\": \"acme\",\n \"team\": \"payments\",\n \"user\": \"dana\"\n },\n \"key\": \"customer=acme|team=payments|user=dana\"\n },\n {\n \"path\": {\n \"user\": \"dana\",\n \"team\": \"payments\",\n \"customer\": \"acme\"\n },\n \"key\": \"customer=acme|team=payments|user=dana\",\n \"note\": \"insertion order must not change the key; a dict has whatever order the caller wrote and ladder order is not available here\"\n },\n {\n \"path\": {\n \"customer\": \"a|b\"\n },\n \"key\": \"customer=a\\\\pb\"\n },\n {\n \"path\": {\n \"customer\": \"a=b\"\n },\n \"key\": \"customer=a\\\\eb\"\n },\n {\n \"path\": {\n \"customer\": \"a\\\\b\"\n },\n \"key\": \"customer=a\\\\\\\\b\"\n },\n {\n \"path\": {\n \"customer\": \"a\\\\pb\"\n },\n \"key\": \"customer=a\\\\\\\\pb\",\n \"note\": \"the escape-order case: this must NOT collide with a|b above\"\n },\n {\n \"path\": {\n \"customer\": \"ab\"\n },\n \"key\": \"customer=ab\"\n },\n {\n \"path\": {\n \"customer\": \"a\",\n \"b\": \"c\"\n },\n \"key\": \"b=c|customer=a\",\n \"note\": \"sorted by key, so `customer=ab` and this cannot collapse\"\n }\n ]\n },\n \"semantic_match\": {\n \"$comment\": \"Matching a fetch query to prefetched items by meaning, with the SDK's local embedding model (`model`, which history search uses too). An item's score is its best cosine to the query, over the item's own text and the questions it answers (its HQEs, sent with the bundle item; item_questions), with the query as asked and with the asker's pronouns in the third person (canonical_query). Three bands: at sure_min_cosine or above the cache answers; between min_cosine and sure_min_cosine the match is borderline and the SDK asks the server too, serving the server's answer if it arrives within race_deadline_ms and the cached one if not; below min_cosine the server answers. A hit also serves items at serve_min_cosine or above, and items from a bundle holding a hit at bundle_serve_min_cosine or above. asked_kind: a question that asks for a time of day, a duration or an amount is not answered by served items holding none (asked_details). Items not embedded yet, and every item without the model, are matched as before (BM25 with its novel-term gate). The thresholds hold for `model` only, one text per run. Measured on harnesses/anticipation_match (404 labelled queries, 4 languages, production-shaped bundles with production-prompt HQEs), held-out half, per 100 questions with the answer cached for 68: BM25 served 27 right and 13 wrong; MiniLM on the memory text alone 30 and 5.5; with all of the above (sure bar 0.74) 44-47 and 2.5-3, 21 Oct 2026.\",\n \"model\": \"onnx:Xenova/paraphrase-multilingual-MiniLM-L12-v2@2c4055b12046f11709e9df2c122e59ffbdc2f900/onnx/model_quantized.onnx\",\n \"mode\": \"semantic\",\n \"min_cosine\": 0.66,\n \"serve_min_cosine\": 0.4,\n \"asked_kind\": true,\n \"max_queries\": 4,\n \"item_questions\": true,\n \"canonical_query\": \"both\",\n \"self_pronouns\": {\n \"$comment\": \"canonical_query rewrites the asker's own pronouns into the third person memories are stored in ('my landlord' -> 'the user's landlord'). Applied in order, case-insensitive. 'canon' compares only the rewritten query, 'both' takes the better of the two (HQEs are written in the first person).\",\n \"en\": [\n [\"\\\\b(i am|i'm)\\\\b\", \"the user is\"],\n [\"\\\\b(i have|i've)\\\\b\", \"the user has\"],\n [\"\\\\b(i was)\\\\b\", \"the user was\"],\n [\"\\\\b(my|mine)\\\\b\", \"the user's\"],\n [\"\\\\b(i|me|myself)\\\\b\", \"the user\"]\n ]\n },\n \"asked_details\": {\n \"$comment\": \"asked_kind: a question that asks for one of these kinds of value is not answered by served items that hold none (asked 'what time is my flight', served 'the flight is on 25 October'). Its own list, not history_search.asked_kinds, so history search's ranking is untouched. Query patterns are matched case-insensitively; value patterns are written to be language-neutral (digits, units, currency) and avoid \\\\\\\\b next to non-ASCII letters, which ECMAScript's \\\\\\\\b does not treat as word characters.\",\n \"time_of_day\": {\n \"query\": \"what time|at what time|which time|opening hours|open hours|what hours|when does .{0,40}(open|close|start|begin)|qu[eé] hora|a qu[eé] hora|que horas|a que horas|hor[aá]rio|किस समय|कितने बजे|टाइम\",\n \"value\": \"\\\\b\\\\d{1,2}(:\\\\d{2})? ?([ap]\\\\.?m\\\\.?)(\\\\W|$)|\\\\b\\\\d{1,2}:\\\\d{2}\\\\b|\\\\b\\\\d{1,2}h(\\\\d{2})?\\\\b|noon|midnight|mediod[ií]a|meio-dia|बजे\"\n },\n \"duration\": {\n \"query\": \"how long|duration|how many (minutes|hours)|cu[aá]nto dura|cu[aá]nto tiempo|quanto tempo|dura[cç][aã]o|कितनी देर|कितने समय\",\n \"value\": \"\\\\d+(\\\\.\\\\d+)? ?(minutes?|mins?|hours?|hrs?|days?|weeks?|months?|years?|minutos?|horas?|d[ií]as|semanas?|meses|años|anos|मिनट|घंटे|घंटा|दिन|हफ्ते|महीने|साल)\"\n },\n \"money\": {\n \"query\": \"how much|cost|price|fee|charge|budget|amount|quote|cu[aá]nto cuesta|precio|costo|tarifa|quanto custa|pre[cç]o|valor|custo|कितने पैसे|कितना पैसा|कीमत|खर्च|शुल्क|कितने रुपये\",\n \"value\": \"[$€£¥₹]\\\\s?\\\\d|\\\\d[\\\\d,.]* ?(usd|eur|gbp|inr|brl|mxn|dollars?|euros?|pounds?|rupees?|reais|real|pesos?|रुपये|रुपए)|\\\\b(usd|eur|gbp|inr|brl|rs\\\\.?|r\\\\$) ?\\\\d\"\n }\n },\n \"bundle_serve_min_cosine\": 0.3,\n \"sure_min_cosine\": 0.74,\n \"race_deadline_ms\": 250\n },\n \"history_search\": {\n \"$comment\": \"Local search over the raw turns a compaction replaced (COMPACTION_HYBRID_PLAN.md P5). Keyword first (literal values, then BM25 with the bm25 section's tokenizer), embeddings only when installed and keywords come up short; results fused by reciprocal rank with a small recency prior, shown in conversation order. The heading reaches the LLM prompt: byte-identical in both SDKs. Each hit brings the turn before it (its last previous_turn_words words). Irregular past forms are mapped to the base form before stemming (English; history search only). A turn needs min_matched_terms query words, or one word found in at most rare_term_max_share of the turns. Passages (passages, passages_per_turn): a turn longer than max_words_per_excerpt is scored as passages (table rows with their header, a few lines of a log, a few sentences), ranks by its best passage, and shows its best passages; measured 20 Oct with multilingual MiniLM, answer text found 117 of 148 probes vs 93 by whole turns. Tool results the host agent reports are archived here too, never sent to the server, as \\\"[tool result: <name>] <result>\\\" cut to tool_result_max_chars: searchable like turns, not counted as server turns.\",\n \"max_excerpts\": 6,\n \"max_words_per_excerpt\": 60,\n \"min_matched_terms\": 2,\n \"rrf_k\": 60,\n \"recency_weight\": 0.5,\n \"heading\": \"### From the earlier turns (verbatim excerpts matched to the latest message; later turns may have changed them)\",\n \"store_limits\": {\n \"max_conversations\": 200,\n \"max_turns_per_conversation\": 5000,\n \"max_bytes_per_conversation\": 8388608,\n \"idle_ttl_seconds\": 604800,\n \"max_file_bytes\": 209715200\n },\n \"previous_turns\": 1,\n \"previous_turn_words\": 40,\n \"rare_term_max_share\": 0.02,\n \"tool_result_max_chars\": 16000,\n \"passages\": true,\n \"passages_per_turn\": 2,\n \"early_query\": {\n \"pattern\": \"\\\\b(original|originally|initial|initially|at first|at the start|used to|previous|previously|first (draft|version|proposal|quote|offer|reply|round|email|contract))\\\\b\",\n \"flags\": \"i\",\n \"note\": \"A question about how things started: no recency prior for it, or later turns on the same subject push the early one out (20 Oct, documents case).\"\n },\n \"asked_kinds\": {\n \"$comment\": \"A question that asks for a kind of value (a number to call, an email, an address, an amount, a date, a percentage) also ranks the passages that hold one, among those sharing a query word: 'the best number to reach Renata' found her email but showed 'Best, Renata', not the line with the number (20 Oct).\",\n \"phone\": {\"query\": \"\\\\b(phone|mobile|cell|telephone|call|reach|contact|direct line|best number)\\\\b\", \"value\": \"(?:\\\\+\\\\d{1,3}[\\\\s.-]?)?\\\\(?\\\\d{2,4}\\\\)?[\\\\s.-]\\\\d{3,4}[\\\\s.-]\\\\d{3,4}\\\\b\"},\n \"email\": {\"query\": \"\\\\b(e-?mail|email address|write to)\\\\b\", \"value\": \"[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(?:\\\\.[A-Za-z0-9-]+)+\"},\n \"address\": {\"query\": \"\\\\b(address|street|located|postal|where (is|are|do|does|should))\\\\b\", \"value\": \"\\\\b\\\\d{1,5}\\\\s+[A-Z][a-z]+(?:\\\\s[A-Z][a-z]+)*\\\\s(?:Street|St|Avenue|Ave|Road|Rd|Boulevard|Blvd|Lane|Ln|Drive|Dr|Way|Court|Ct|Place|Pl|Rua|Avenida)\\\\b\"},\n \"money\": {\"query\": \"\\\\b(how much|cost|costs|price|fee|fees|charge|charges|pay|paid|budget|amount|quote|quoted|spend)\\\\b\", \"value\": \"(?:[$\\u20ac\\u00a3\\u00a5\\u20b9]|\\\\b(?:USD|EUR|GBP|CAD|INR|AUD)\\\\s?)\\\\d\"},\n \"percent\": {\"query\": \"(percent|percentage|%)\", \"value\": \"\\\\d(?:\\\\.\\\\d+)?\\\\s?%\"},\n \"date\": {\"query\": \"\\\\b(when|what date|which day|deadline|due|by what date)\\\\b\", \"value\": \"\\\\b(?:\\\\d{4}-\\\\d{2}-\\\\d{2}|\\\\d{1,2}(?:st|nd|rd|th)?\\\\s+(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*|(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\\\\s+\\\\d{1,2})\\\\b\"}\n },\n \"irregular_forms\": {\n \"sold\": \"sell\",\n \"paid\": \"pay\",\n \"sent\": \"send\",\n \"bought\": \"buy\",\n \"made\": \"make\",\n \"got\": \"get\",\n \"gotten\": \"get\",\n \"took\": \"take\",\n \"taken\": \"take\",\n \"gave\": \"give\",\n \"given\": \"give\",\n \"went\": \"go\",\n \"gone\": \"go\",\n \"told\": \"tell\",\n \"left\": \"leave\",\n \"met\": \"meet\",\n \"kept\": \"keep\",\n \"found\": \"find\",\n \"held\": \"hold\",\n \"brought\": \"bring\",\n \"thought\": \"think\",\n \"flew\": \"fly\",\n \"flown\": \"fly\",\n \"wrote\": \"write\",\n \"written\": \"write\",\n \"chose\": \"choose\",\n \"chosen\": \"choose\",\n \"spoke\": \"speak\",\n \"spoken\": \"speak\",\n \"broke\": \"break\",\n \"broken\": \"break\",\n \"began\": \"begin\",\n \"begun\": \"begin\",\n \"ran\": \"run\",\n \"came\": \"come\",\n \"saw\": \"see\",\n \"seen\": \"see\",\n \"knew\": \"know\",\n \"known\": \"know\",\n \"drove\": \"drive\",\n \"driven\": \"drive\",\n \"built\": \"build\",\n \"spent\": \"spend\",\n \"lost\": \"lose\",\n \"won\": \"win\",\n \"lent\": \"lend\",\n \"felt\": \"feel\",\n \"heard\": \"hear\",\n \"understood\": \"understand\",\n \"forgot\": \"forget\",\n \"forgotten\": \"forget\",\n \"ate\": \"eat\",\n \"eaten\": \"eat\",\n \"fell\": \"fall\",\n \"fallen\": \"fall\",\n \"grew\": \"grow\",\n \"grown\": \"grow\",\n \"hid\": \"hide\",\n \"hidden\": \"hide\",\n \"rode\": \"ride\",\n \"rang\": \"ring\",\n \"rose\": \"rise\",\n \"sang\": \"sing\",\n \"sat\": \"sit\",\n \"slept\": \"sleep\",\n \"stood\": \"stand\",\n \"stole\": \"steal\",\n \"swam\": \"swim\",\n \"taught\": \"teach\",\n \"threw\": \"throw\",\n \"thrown\": \"throw\",\n \"woke\": \"wake\",\n \"wore\": \"wear\",\n \"worn\": \"wear\",\n \"led\": \"lead\",\n \"dealt\": \"deal\",\n \"meant\": \"mean\",\n \"sought\": \"seek\",\n \"caught\": \"catch\",\n \"fought\": \"fight\",\n \"bent\": \"bend\",\n \"bit\": \"bite\",\n \"bled\": \"bleed\",\n \"blew\": \"blow\",\n \"bred\": \"breed\",\n \"drew\": \"draw\",\n \"drawn\": \"draw\",\n \"drank\": \"drink\",\n \"fed\": \"feed\",\n \"forgave\": \"forgive\",\n \"froze\": \"freeze\",\n \"hung\": \"hang\",\n \"laid\": \"lay\",\n \"lit\": \"light\",\n \"overpaid\": \"overpay\",\n \"rebuilt\": \"rebuild\",\n \"resent\": \"resend\",\n \"shook\": \"shake\",\n \"shot\": \"shoot\",\n \"shut\": \"shut\",\n \"sank\": \"sink\",\n \"slid\": \"slide\",\n \"spun\": \"spin\",\n \"struck\": \"strike\",\n \"swore\": \"swear\",\n \"swept\": \"sweep\",\n \"tore\": \"tear\",\n \"withdrew\": \"withdraw\"\n }\n }\n}\n","/**\n * Loader for the shared SDK behavior contract.\n *\n * The values here are tuned against measured eval failures and are shared,\n * byte-identical, with the Python SDK. They live in data rather than code\n * specifically so the two SDKs cannot drift. See\n * `synap/sdk/CONTRACT/README.md`.\n *\n * `anticipation.json` is vendored into `src/` by\n * `synap/sdk/scripts/sync-behavior.sh` and imported (not read from disk) so\n * that the bundler inlines it. Reading it with `node:fs` at runtime would work\n * in Node and break in every bundled target: Edge, Workers, and anything under\n * webpack/turbopack, where `dist/` is rewritten and relative paths do not\n * survive. It is the same failure mode as `@grpc/proto-loader` reading a\n * `.proto` off disk (gotcha G-P).\n */\n\nimport contractJson from './anticipation.json' with { type: 'json' };\n\nexport interface RecallPattern {\n pattern: string;\n flags: string;\n note: string;\n}\n\nexport interface BehaviorContract {\n version: number;\n recall_bypass: {\n note: string;\n env_flag: string;\n default: boolean;\n patterns: RecallPattern[];\n };\n thresholds: {\n bm25: { value: number; note: string };\n novel_term: { value: number; note: string };\n min_corpus_for_novel_gate: { value: number; note: string };\n effective_floor: { value: number; note: string };\n query_token_scale: { value: number; note: string };\n };\n coverage_gate: {\n note: string;\n env_flag: string;\n default: number | null;\n empty_query_coverage: number;\n };\n telemetry: {\n rejected_items_cap: { value: number; note: string };\n content_preview_chars: number;\n };\n bm25: {\n k1: number;\n b: number;\n idf_floor: number;\n min_token_len: number;\n min_stem_len: number;\n suffixes: string[];\n stop_words: string[];\n alias_token: { pattern: string; note: string };\n };\n semantic_match: {\n model: string;\n mode: 'bm25' | 'semantic';\n min_cosine: number;\n serve_min_cosine: number;\n asked_kind: boolean;\n max_queries: number;\n item_questions: boolean;\n canonical_query: 'off' | 'canon' | 'both';\n self_pronouns: Record<string, [string, string][] | string>;\n asked_details: Record<string, { query: string; value: string } | string>;\n bundle_serve_min_cosine: number;\n sure_min_cosine: number;\n race_deadline_ms: number;\n };\n history_search: {\n max_excerpts: number;\n max_words_per_excerpt: number;\n min_matched_terms: number;\n rrf_k: number;\n recency_weight: number;\n min_cosine?: number;\n previous_turns?: number;\n previous_turn_words?: number;\n rare_term_max_share?: number;\n tool_result_max_chars?: number;\n passages?: boolean;\n passages_per_turn?: number;\n early_query?: { pattern: string; flags?: string; note?: string };\n asked_kinds?: Record<string, { query: string; value: string } | string>;\n irregular_forms?: Record<string, string>;\n heading: string;\n store_limits: {\n max_conversations: number;\n max_turns_per_conversation: number;\n max_bytes_per_conversation: number;\n idle_ttl_seconds: number;\n max_file_bytes: number;\n };\n };\n}\n\nexport const CONTRACT = contractJson as unknown as BehaviorContract;\n\n/** ECMAScript flags we accept. Anything else is invalid in Python too. */\nconst ALLOWED_FLAGS = new Set(['i', 'm', 's']);\n\nfunction compile(pattern: string, flags: string): RegExp {\n for (const ch of flags) {\n if (!ALLOWED_FLAGS.has(ch)) {\n throw new Error(\n `Unsupported regex flag '${ch}' in behavior contract. Flags must be ` +\n `valid in both Python and ECMAScript.`,\n );\n }\n }\n return new RegExp(pattern, flags);\n}\n\n/**\n * Compiled recall-bypass patterns, in contract order.\n *\n * Deliberately NOT using the `g` flag: a global RegExp carries `lastIndex`\n * between calls, so a shared instance would match on one query and then miss\n * on the next. That failure is intermittent and traffic-dependent, which is\n * the worst kind to debug.\n */\nexport const RECALL_PATTERNS: readonly RegExp[] = CONTRACT.recall_bypass.patterns.map((p) =>\n compile(p.pattern, p.flags ?? ''),\n);\n\nexport const THRESHOLDS = {\n bm25: CONTRACT.thresholds.bm25.value,\n novelTerm: CONTRACT.thresholds.novel_term.value,\n minCorpusForNovelGate: CONTRACT.thresholds.min_corpus_for_novel_gate.value,\n /** Lower bound on the derived per-query threshold. */\n effectiveFloor: CONTRACT.thresholds.effective_floor.value,\n /** Points of score allowed per query token when deriving the threshold. */\n queryTokenScale: CONTRACT.thresholds.query_token_scale.value,\n} as const;\n\nexport const BM25_PARAMS = CONTRACT.bm25;\nexport const STOP_WORDS: ReadonlySet<string> = new Set(CONTRACT.bm25.stop_words);\n// Order is load-bearing: stem() returns on first match, so this must stay\n// longest-first exactly as the contract lists it.\nexport const SUFFIXES: readonly string[] = CONTRACT.bm25.suffixes;\nexport const ALIAS_TOKEN_SOURCE = CONTRACT.bm25.alias_token.pattern;\n/** Local search over a conversation's earlier turns (cache/history-search.ts). */\nexport const HISTORY_SEARCH = CONTRACT.history_search;\n/** Matching fetch queries to prefetched items by meaning (context/anticipation-cache.ts). */\nexport const SEMANTIC_MATCH = CONTRACT.semantic_match;\n\n/** True when any query string looks like an explicit memory-recall ask. */\nexport function isRecallQuery(searchQuery: readonly (string | null | undefined)[] | null | undefined): boolean {\n for (const q of searchQuery ?? []) {\n if (!q) continue;\n for (const pattern of RECALL_PATTERNS) {\n if (pattern.test(q)) return true;\n }\n }\n return false;\n}\n","/**\n * BM25 scorer for the anticipation cache.\n *\n * A faithful port of `maximem_synap/cache/bm25.py`. Every tuned value comes\n * from the shared behavior contract, so the two implementations cannot drift\n * on parameters. What they *can* still drift on is arithmetic, which is what\n * the shared conformance corpus is for.\n */\n\nimport { BM25_PARAMS, STOP_WORDS, SUFFIXES, ALIAS_TOKEN_SOURCE } from '../behavior/contract.js';\n\nconst MIN_STEM_LEN = BM25_PARAMS.min_stem_len;\nconst MIN_TOKEN_LEN = BM25_PARAMS.min_token_len;\n\nexport function stem(word: string): string {\n // Order is load-bearing: longest suffix first, return on first match.\n for (const suffix of SUFFIXES) {\n if (word.endsWith(suffix) && word.length - suffix.length >= MIN_STEM_LEN) {\n return word.slice(0, word.length - suffix.length);\n }\n }\n return word;\n}\n\n/**\n * An alias label, as it appears in text: `[[PERSON_PHONE_h2n7v5cx8m0d]]`.\n *\n * Kept whole rather than split into `person`, `phone` and the suffix, because\n * the first two are boilerplate shared by every label of that field type and\n * would make two different people's memories score alike.\n *\n * Mirrors `synap/cloud/shared/tokenizer.py`. Keep the two in step.\n */\nfunction aliasTokenRe(): RegExp {\n // Constructed per call because `g` regexes carry `lastIndex`, and this one\n // is used with replace(). A module-level shared instance would produce\n // different results on alternating calls.\n return new RegExp(ALIAS_TOKEN_SOURCE, 'g');\n}\n\nconst WORD_RE = /[a-z0-9]+/g;\n\n/** Tokenize, lowercase, remove stop words, and stem. Alias labels survive whole. */\nexport function tokenize(text: string | null | undefined): string[] {\n if (!text) return [];\n\n const labels: string[] = [];\n const remainder = text.replace(aliasTokenRe(), (_match, captured: string) => {\n labels.push(captured.toLowerCase());\n return ' ';\n });\n\n const words = remainder.toLowerCase().match(WORD_RE) ?? [];\n const out = labels.slice();\n for (const w of words) {\n if (w.length >= MIN_TOKEN_LEN && !STOP_WORDS.has(w)) out.push(stem(w));\n }\n return out;\n}\n\n/** Okapi BM25 scorer over a small in-memory corpus. */\nexport class BM25 {\n static readonly IDF_FLOOR = BM25_PARAMS.idf_floor;\n\n readonly k1: number;\n readonly b: number;\n readonly corpus: readonly (readonly string[])[];\n readonly docCount: number;\n readonly avgdl: number;\n readonly docLens: readonly number[];\n readonly idf: ReadonlyMap<string, number>;\n\n constructor(\n corpus: readonly (readonly string[])[],\n k1: number = BM25_PARAMS.k1,\n b: number = BM25_PARAMS.b,\n ) {\n this.k1 = k1;\n this.b = b;\n this.corpus = corpus;\n this.docCount = corpus.length;\n this.docLens = corpus.map((doc) => doc.length);\n // Python falls back to 1.0 rather than dividing by zero on an empty corpus.\n this.avgdl = this.docCount\n ? this.docLens.reduce((a, n) => a + n, 0) / this.docCount\n : 1.0;\n\n const idf = new Map<string, number>();\n const allTerms = new Set<string>();\n for (const doc of corpus) for (const t of doc) allTerms.add(t);\n for (const term of allTerms) {\n let df = 0;\n for (const doc of corpus) if (doc.includes(term)) df++;\n const raw = Math.log((this.docCount - df + 0.5) / (df + 0.5) + 1.0);\n idf.set(term, Math.max(raw, BM25.IDF_FLOOR));\n }\n this.idf = idf;\n }\n\n score(queryTokens: readonly string[], docIdx: number): number {\n const doc = this.corpus[docIdx];\n if (doc === undefined) return 0;\n const dl = this.docLens[docIdx] ?? 0;\n\n const tfMap = new Map<string, number>();\n for (const t of doc) tfMap.set(t, (tfMap.get(t) ?? 0) + 1);\n\n let s = 0;\n for (const term of queryTokens) {\n const tf = tfMap.get(term);\n if (tf === undefined) continue;\n const idf = this.idf.get(term) ?? BM25.IDF_FLOOR;\n const numerator = tf * (this.k1 + 1);\n const denominator = tf + this.k1 * (1 - this.b + (this.b * dl) / this.avgdl);\n s += (idf * numerator) / denominator;\n }\n return s;\n }\n\n scores(queryTokens: readonly string[]): number[] {\n return this.corpus.map((_, i) => this.score(queryTokens, i));\n }\n}\n","/**\n * In-memory TTL cache for context bundles pushed over gRPC.\n *\n * Port of `maximem_synap/cache/anticipation_cache.py`.\n *\n * ## Why this is a faithful port and not a simplification\n *\n * `@maximem/synap-vercel-adk` already contains a native TS anticipation cache,\n * and it is a simplification: a hardcoded 30-minute TTL, a plain BM25 threshold\n * with no per-query derivation, and none of the gates below. Shipping that as\n * the SDK's cache would mean two retrieval behaviors under one brand, which is\n * gotcha G-C. Every gate here exists because a specific eval failed without it:\n *\n * - **Recall bypass** runs BEFORE scoring. The measured false hit scored 2.86\n * at coverage 1.00, so no score-side gate could ever have caught it.\n * - **The novel-term gate** is suppressed below a 200-stem corpus, because at\n * cold start ordinary English trips it on nearly every query.\n * - **The per-query threshold** scales down for short queries, which otherwise\n * cannot reach the nominal score at all.\n * - **The coverage gate** catches a bundle that clears BM25 on one shared\n * token (\"account\") while lacking the asked-for fact.\n *\n * All thresholds come from the shared behavior contract.\n */\n\nimport { BM25, tokenize } from '../cache/bm25.js';\nimport { CONTRACT, SEMANTIC_MATCH, THRESHOLDS, isRecallQuery } from '../behavior/contract.js';\nimport type { Encoder } from '../cache/history-search.js';\nimport { getEnv, getEnvFlag, getEnvFloat } from '../util/env.js';\n\nconst EFFECTIVE_FLOOR = CONTRACT.thresholds.effective_floor.value;\nconst QUERY_TOKEN_SCALE = CONTRACT.thresholds.query_token_scale.value;\nconst REJECTED_CAP = CONTRACT.telemetry.rejected_items_cap.value;\nconst PREVIEW_CHARS = CONTRACT.telemetry.content_preview_chars;\n\n/** Sentinel scope that matches any requester. */\nexport const ANY_SCOPE = '_any';\n\n// ── the rung a request or a bundle stands at ───────────────────────────────\n//\n// A pushed bundle is stamped with the scope rung the server retrieved it at,\n// and a fetch names one through `scope_path`. Both are written the same way so\n// they can be compared as strings, because nothing in this process can resolve\n// anything: the server speaks in registry node ids and an SDK in a customer's\n// app has never seen one, so the comparison happens in the vocabulary the\n// CALLER uses, which is level keys and the client's own external ids.\n//\n// The recipe and its golden vectors live in the shared behavior contract\n// (CONTRACT/behavior/anticipation.json, `rung_key`). It is written three times\n// (here, the Python SDK, the server) because the producer is not an SDK, so\n// the usual \"port it once\" rule cannot apply. Do NOT re-tune it here.\nconst RUNG_SEPARATOR = '|';\nconst RUNG_ASSIGN = '=';\n\n/**\n * The rung a `scope_path` names, canonically, or null for no path.\n *\n * Sorted by level key because an object carries whatever order the caller\n * wrote it in, and the ladder's own order is not knowable here. Only the value\n * is escaped: level keys are `^[a-z][a-z0-9_]{0,63}$`, so they can hold\n * neither separator. Backslash is escaped first, or `a|b` and `a\\pb` would\n * encode to the same string, which is a false match between two rungs.\n */\nexport function rungKey(\n scopePath: Record<string, string> | null | undefined,\n): string | null {\n if (!scopePath) return null;\n const pairs: [string, string][] = [];\n for (const [k, v] of Object.entries(scopePath)) {\n if (k === '' || v === null || v === undefined || String(v) === '') continue;\n pairs.push([String(k), String(v)]);\n }\n if (pairs.length === 0) return null;\n pairs.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));\n return pairs\n .map(([k, v]) => {\n const escaped = v\n .split('\\\\').join('\\\\\\\\')\n .split(RUNG_SEPARATOR).join('\\\\p')\n .split(RUNG_ASSIGN).join('\\\\e');\n return `${k}${RUNG_ASSIGN}${escaped}`;\n })\n .join(RUNG_SEPARATOR);\n}\n\n/**\n * May a bundle stamped `bundleRung` answer a request at `requestRung`?\n *\n * Equality, and equality on purpose. A subset rule (\"the bundle names more\n * rungs than you did, close enough\") reads as the generous option and is the\n * dangerous one: the rung nobody named is exactly the rung that tells two\n * people sharing an external id apart, which is the case the whole ladder\n * exists for. Being wrong here costs a cold fetch; being wrong the other way\n * hands one person's context to another.\n *\n * null on either side means \"named no rung\". Both null is every request in\n * production today and it serves, so nothing changes for anyone who has not\n * turned nested scoping on. `''` normalises to null because a proto string\n * field has no null and an unstamped bundle arrives as the empty string.\n */\nexport function rungServes(\n bundleRung: string | null | undefined,\n requestRung: string | null | undefined,\n): boolean {\n return (bundleRung || null) === (requestRung || null);\n}\n\n/**\n * A bundle the server pushed with no user or customer stamp, tied to a\n * conversation other than the one this request names (or to one when the\n * request names none).\n *\n * Nothing on such a bundle says whose it is: it is stamped ANY_SCOPE because\n * the request that triggered it named no user. Its conversation is the only\n * owner it has, so outside that conversation it is served to nobody. Without\n * this, a user-scope or client-scope fetch, which names no conversation, could\n * be answered from another user's conversation.\n */\nexport function unownedElsewhere(\n entryEntity: string, entryConversation: string | null, conversationId: string | null,\n): boolean {\n return entryEntity === ANY_SCOPE && entryConversation !== null && entryConversation !== conversationId;\n}\n\nexport interface BundleItem {\n item_id?: string;\n content?: string;\n [key: string]: unknown;\n}\n\nexport interface ContextBundle {\n bundleId: string;\n entityId?: string | null;\n conversationId?: string | null;\n itemsByType: Record<string, BundleItem[]>;\n /** Server hint; may only SHORTEN the global TTL, never extend it. */\n ttlHintSeconds?: number | null;\n /** Which anticipation pattern produced this bundle. Diagnostics only. */\n bundleType?: string | null;\n /** The queries the server anticipated. Diagnostics only. */\n searchQueries?: readonly string[] | null;\n /**\n * The scope rung the server retrieved this bundle at, as `rungKey` writes\n * one. Absent from anything an older server sent, which reads as \"named no\n * rung\" rather than as \"matches anything\".\n */\n scopeRung?: string | null;\n}\n\ninterface ItemRecord {\n bundleId: string;\n itemType: string;\n content: string;\n tokens: string[];\n itemDict: BundleItem;\n /**\n * The item's vector under `vecModel`, filled off the lookup path once a model\n * is loaded. Null until then: the item is matched by BM25 alone.\n */\n vec: number[] | null;\n vecModel: string;\n /** The questions the memory answers (its HQEs), sent with the bundle item, and their vectors. */\n questions: string[];\n qvecs: number[][];\n}\n\n/** The asker's own pronouns in the third person memories are stored in. Python: `canonical_query`. */\nexport function canonicalQuery(query: string): string {\n let out = query;\n for (const [lang, rules] of Object.entries(SEMANTIC_MATCH.self_pronouns ?? {})) {\n if (lang.startsWith('$') || typeof rules === 'string') continue;\n for (const [pattern, repl] of rules) out = out.replace(new RegExp(pattern, 'gi'), repl);\n }\n return out;\n}\n\n/** Value patterns for the kinds of value the queries ask for. Python: `_asked_kind_patterns`. */\nfunction askedDetailPatterns(queries: readonly string[]): RegExp[] {\n const out: RegExp[] = [];\n for (const [name, spec] of Object.entries(SEMANTIC_MATCH.asked_details ?? {})) {\n if (name.startsWith('$') || typeof spec !== 'object') continue;\n const q = new RegExp(spec.query, 'i');\n if (queries.some((x) => q.test(x))) out.push(new RegExp(spec.value, 'i'));\n }\n return out;\n}\n\nfunction itemQuestions(item: BundleItem): string[] {\n const qs = (item as { questions?: unknown }).questions;\n return Array.isArray(qs) ? qs.filter((q): q is string => typeof q === 'string' && q.trim() !== '').map((q) => q.trim()) : [];\n}\n\nexport const MATCHERS = ['bm25', 'semantic'] as const;\nexport type Matcher = (typeof MATCHERS)[number];\n\n/** `SYNAP_SDK_CACHE_MATCHER` (bm25 or semantic) over the contract's mode. */\nfunction matcherSetting(): Matcher {\n const raw = (getEnv('SYNAP_SDK_CACHE_MATCHER') ?? '').trim().toLowerCase();\n return (MATCHERS as readonly string[]).includes(raw) ? (raw as Matcher) : SEMANTIC_MATCH.mode;\n}\n\nfunction cosine(a: readonly number[], b: readonly number[]): number {\n let dot = 0;\n let na = 0;\n let nb = 0;\n for (let i = 0; i < a.length && i < b.length; i++) {\n dot += a[i]! * b[i]!;\n na += a[i]! * a[i]!;\n nb += b[i]! * b[i]!;\n }\n return na && nb ? dot / Math.sqrt(na * nb) : 0;\n}\n\ninterface EntryRecord {\n bundleId: string;\n entityId: string;\n conversationId: string | null;\n bundleType: string | null;\n searchQueries: string[];\n /** Sliding lease, refreshed on hit unless TTL hints are honoured. */\n storedAt: number;\n /** Absolute creation time; the max-entry-age cap uses this, not the lease. */\n createdAt: number;\n ttlHintSeconds: number | null;\n scopeRung: string | null;\n}\n\nexport interface LookupResult {\n itemsByType: Record<string, BundleItem[]>;\n bundleIds: string[];\n score: number;\n coverage: number;\n /**\n * A semantic match below the sure bar is \"borderline\": the caller may still\n * ask the server, with a short deadline. Python: `_match_band`.\n */\n band?: 'sure' | 'borderline';\n}\n\nexport interface LookupParams {\n searchQuery?: readonly string[] | null;\n entityId?: string | null;\n customerId?: string | null;\n clientId?: string | null;\n conversationId?: string | null;\n /**\n * The rung this request names, from its `scope_path`. A SECOND, independent\n * dimension and NOT another tier of the entity funnel: the funnel widens and\n * the rung does not, because a rung is a boundary and a boundary that widens\n * is not one.\n */\n scopeRung?: string | null;\n maxItems?: number;\n /**\n * The search queries' vectors under the cache's encoder, from `lookupAsync`.\n * With them (and matcher \"semantic\") the lookup matches by meaning.\n */\n queryVectors?: readonly (readonly number[])[] | null;\n}\n\nexport type ExitReason =\n | 'empty'\n | 'recall_bypass'\n | 'no_query_tokens'\n | 'novel_term_gate'\n | 'empty_corpus'\n | 'no_items_above_threshold'\n | 'coverage_gate'\n | 'asked_kind_missing'\n | 'hit';\n\nexport interface LookupTelemetry {\n searchQuery: string[];\n entityId: string | null;\n customerId: string | null;\n clientId: string | null;\n conversationId: string | null;\n scopeRung: string | null;\n scopeFilterAccepted: string[];\n novelTermRatio: number | null;\n bm25Threshold: number | null;\n bm25QueryTokens: string[];\n itemsPicked: unknown[];\n itemsRejected: unknown[];\n coverage: number | null;\n hit: boolean;\n exitReason: ExitReason;\n}\n\nexport interface SnapshotItemPreview {\n type: string;\n scope: string | null;\n content: string;\n}\n\nexport interface SnapshotItemRecord {\n bundle_id: string;\n item_type: string;\n tokens: string[];\n content: string;\n}\n\nexport interface SnapshotBundle {\n bundle_id: string;\n entity_id: string;\n conversation_id: string | null;\n bundle_type: string | null;\n search_queries: string[];\n scope_counts: Record<string, number>;\n total_items: number;\n items: SnapshotItemPreview[];\n}\n\nexport interface AnticipationCacheSnapshot {\n total_entries: number;\n total_item_records: number;\n scope_breakdown_overall: Record<string, number>;\n corpus_vocab_size: number;\n corpus_vocab_sample: string[];\n item_records: SnapshotItemRecord[];\n bundles: SnapshotBundle[];\n}\n\nexport interface AnticipationCacheOptions {\n ttlSeconds?: number;\n maxBundles?: number;\n /** Injected in tests. Defaults to a monotonic clock. */\n now?: () => number;\n onLookup?: (telemetry: LookupTelemetry) => void;\n /**\n * The local embedding model, usually handed over later with `setEncoder`\n * once it has loaded. Until then, and with matcher \"bm25\", lookups are BM25.\n */\n encoder?: Encoder | null;\n matcher?: Matcher;\n minCosine?: number;\n serveMinCosine?: number;\n askedKind?: boolean;\n itemQuestions?: boolean;\n canonical?: 'off' | 'canon' | 'both';\n bundleServeMinCosine?: number;\n sureMinCosine?: number;\n}\n\nconst DEFAULT_TTL_SECONDS = 1800;\nconst DEFAULT_MAX_BUNDLES = 50;\n\nexport class AnticipationCache {\n private readonly ttlSeconds: number;\n private readonly maxBundles: number;\n private readonly now: () => number;\n private readonly onLookup: ((t: LookupTelemetry) => void) | undefined;\n\n private entries = new Map<string, EntryRecord>();\n private items: ItemRecord[] = [];\n private corpusVocab = new Set<string>();\n private bm25: BM25 | null = null;\n private bm25Dirty = true;\n\n // Semantic matching (CONTRACT.semantic_match). Python: the same fields.\n private encoder: Encoder | null;\n private readonly matcher: Matcher;\n private readonly minCosine: number;\n private readonly serveMinCosine: number;\n private readonly askedKind: boolean;\n private readonly itemQuestions: boolean;\n private readonly canonical: 'off' | 'canon' | 'both';\n private readonly bundleServeMinCosine: number;\n private readonly sureMinCosine: number;\n /** Embedding runs one batch after another; `whenEmbedded` awaits the queue. */\n private embedding: Promise<void> = Promise.resolve();\n\n constructor(options: AnticipationCacheOptions = {}) {\n this.ttlSeconds = options.ttlSeconds ?? DEFAULT_TTL_SECONDS;\n this.maxBundles = options.maxBundles ?? DEFAULT_MAX_BUNDLES;\n this.now = options.now ?? monotonicNow;\n this.onLookup = options.onLookup;\n this.encoder = options.encoder ?? null;\n this.matcher = options.matcher ?? matcherSetting();\n this.minCosine = options.minCosine ?? SEMANTIC_MATCH.min_cosine;\n this.serveMinCosine = options.serveMinCosine ?? SEMANTIC_MATCH.serve_min_cosine;\n this.askedKind = options.askedKind ?? SEMANTIC_MATCH.asked_kind;\n this.itemQuestions = options.itemQuestions ?? SEMANTIC_MATCH.item_questions;\n this.canonical = options.canonical ?? SEMANTIC_MATCH.canonical_query;\n this.bundleServeMinCosine = options.bundleServeMinCosine ?? SEMANTIC_MATCH.bundle_serve_min_cosine;\n this.sureMinCosine = options.sureMinCosine ?? SEMANTIC_MATCH.sure_min_cosine;\n }\n\n /**\n * Hand over the local embedding model once it has loaded. Items already\n * cached are embedded off the lookup path.\n */\n setEncoder(encoder: Encoder | null): void {\n this.encoder = encoder;\n this.embedItems(this.items);\n }\n\n /** True when lookups match by meaning: matcher \"semantic\" and a model loaded. */\n get semantic(): boolean {\n return this.matcher === 'semantic' && this.encoder !== null;\n }\n\n /** Resolves once every item stored so far has had its turn to be embedded. */\n whenEmbedded(): Promise<void> {\n return this.embedding;\n }\n\n /**\n * Give `items` their vectors, queued behind any earlier batch, so neither a\n * bundle arriving on the stream nor a lookup waits for the model. A lookup\n * before they are ready matches those items by BM25 alone.\n */\n private embedItems(items: readonly ItemRecord[]): void {\n const encoder = this.encoder;\n if (encoder === null || this.matcher !== 'semantic') return;\n const todo = items.filter((i) => i.vecModel !== encoder.model);\n if (todo.length === 0) return;\n this.embedding = this.embedding.then(async () => {\n try {\n const texts: string[] = [];\n for (const i of todo) {\n texts.push(i.content);\n if (this.itemQuestions) texts.push(...i.questions);\n }\n const vecs = await encoder.encode(texts);\n let k = 0;\n for (const item of todo) {\n const n = this.itemQuestions ? item.questions.length : 0;\n const v = vecs[k];\n if (v) {\n item.vec = Array.from(v);\n item.qvecs = vecs.slice(k + 1, k + 1 + n).map((x) => Array.from(x));\n item.vecModel = encoder.model;\n }\n k += 1 + n;\n }\n } catch {\n /* the items stay BM25-only */\n }\n });\n }\n\n get size(): number {\n return this.entries.size;\n }\n\n get itemCount(): number {\n return this.items.length;\n }\n\n store(bundle: ContextBundle): void {\n if (!bundle.bundleId) return;\n this.evictExpired();\n\n // Client-scope knowledge (FAQs, policies) arrives with no entity, and the\n // sentinel makes it visible to every requester.\n const entityId = bundle.entityId || ANY_SCOPE;\n\n if (this.entries.has(bundle.bundleId)) this.dropBundle(bundle.bundleId);\n\n const now = this.now();\n this.entries.set(bundle.bundleId, {\n bundleId: bundle.bundleId,\n entityId,\n conversationId: bundle.conversationId ?? null,\n bundleType: bundle.bundleType ?? null,\n searchQueries: [...(bundle.searchQueries ?? [])],\n storedAt: now,\n createdAt: now,\n ttlHintSeconds: bundle.ttlHintSeconds ?? null,\n scopeRung: bundle.scopeRung || null,\n });\n\n for (const [itemType, list] of Object.entries(bundle.itemsByType ?? {})) {\n for (const item of list ?? []) {\n const content = String(item.content ?? '');\n const tokens = tokenize(content);\n this.items.push({\n bundleId: bundle.bundleId, itemType, content, tokens, itemDict: item,\n vec: null, vecModel: '', questions: itemQuestions(item), qvecs: [],\n });\n for (const t of tokens) this.corpusVocab.add(t);\n }\n }\n this.bm25Dirty = true;\n this.embedItems(this.items.filter((i) => i.bundleId === bundle.bundleId));\n\n // LRU by lease age.\n while (this.entries.size > this.maxBundles) {\n let oldest: EntryRecord | null = null;\n for (const e of this.entries.values()) {\n if (oldest === null || e.storedAt < oldest.storedAt) oldest = e;\n }\n if (oldest === null) break;\n this.dropBundle(oldest.bundleId);\n }\n }\n\n /**\n * `lookup`, matching by meaning when a model is loaded: embeds the queries,\n * then looks up with their vectors. A query the model cannot embed is looked\n * up by BM25, as without a model. Python does this inside `lookup`.\n */\n async lookupAsync(params: LookupParams): Promise<LookupResult | null> {\n const encoder = this.encoder;\n let queries = (params.searchQuery ?? [])\n .filter((q): q is string => typeof q === 'string' && q.trim() !== '')\n .map((q) => q.trim())\n .slice(0, SEMANTIC_MATCH.max_queries);\n const recall = getEnvFlag(CONTRACT.recall_bypass.env_flag) && isRecallQuery(params.searchQuery ?? null);\n if (!this.semantic || encoder === null || queries.length === 0 || recall || this.items.length === 0) {\n return this.lookup(params);\n }\n // The query as asked, and/or with the asker's pronouns in the third person\n // the memories are written in (canonical_query). Python: the same.\n if (this.canonical === 'canon' || this.canonical === 'both') {\n const canon = queries.map(canonicalQuery);\n const changed = canon.filter((c, i) => c !== queries[i]);\n queries = this.canonical === 'both' ? [...queries, ...changed] : canon;\n }\n let queryVectors: number[][] | null = null;\n try {\n queryVectors = await (encoder.encodeQuery ?? encoder.encode).call(encoder, queries);\n } catch {\n queryVectors = null;\n }\n return this.lookup({ ...params, queryVectors });\n }\n\n lookup(params: LookupParams): LookupResult | null {\n const {\n searchQuery = null, entityId = null, customerId = null,\n clientId = null, conversationId = null, scopeRung = null, maxItems = 10,\n queryVectors = null,\n } = params;\n\n this.evictExpired();\n\n const accepted = this.buildAcceptedScope(entityId, customerId, clientId);\n const telemetry: LookupTelemetry = {\n searchQuery: (searchQuery ?? []).filter((q): q is string => Boolean(q)),\n entityId, customerId, clientId, conversationId,\n scopeRung: scopeRung ?? null,\n scopeFilterAccepted: [...accepted].sort(),\n novelTermRatio: null, bm25Threshold: null, bm25QueryTokens: [],\n itemsPicked: [], itemsRejected: [], coverage: null,\n hit: false, exitReason: 'empty',\n };\n\n if (this.entries.size === 0 || this.items.length === 0) {\n return this.miss(telemetry, 'empty');\n }\n\n const hasQuery = (searchQuery ?? []).some((q) => q && q.trim() !== '');\n if (!hasQuery) {\n return this.freshnessLookup(\n entityId ?? customerId, conversationId, scopeRung ?? null, maxItems, telemetry,\n );\n }\n\n // Recall bypass fires BEFORE scoring: a stale false-hit on a recall-shaped\n // question makes the agent deny a fact the store holds, and the measured\n // failure cleared every score-side gate.\n if (getEnvFlag(CONTRACT.recall_bypass.env_flag) && isRecallQuery(searchQuery)) {\n return this.miss(telemetry, 'recall_bypass');\n }\n\n const queryTokens = (searchQuery ?? []).flatMap((q) => tokenize(q));\n telemetry.bm25QueryTokens = queryTokens;\n if (this.semantic && queryVectors !== null && queryVectors.length > 0) {\n return this.semanticLookup(queryTokens, queryVectors, accepted, conversationId, scopeRung ?? null, maxItems, telemetry);\n }\n if (queryTokens.length === 0) {\n return this.miss(telemetry, 'no_query_tokens');\n }\n\n const uniqueStems = new Set(queryTokens);\n let novel = 0;\n for (const s of uniqueStems) if (!this.corpusVocab.has(s)) novel++;\n const novelRatio = uniqueStems.size ? novel / uniqueStems.size : 0;\n telemetry.novelTermRatio = round4(novelRatio);\n\n // Only meaningful once the corpus has real vocabulary. Below the floor the\n // ratio is dominated by ordinary English and would miss on nearly anything.\n if (\n this.corpusVocab.size >= THRESHOLDS.minCorpusForNovelGate &&\n novelRatio >= THRESHOLDS.novelTerm\n ) {\n return this.miss(telemetry, 'novel_term_gate');\n }\n\n if (this.bm25Dirty || this.bm25 === null) {\n if (this.items.length === 0) return this.miss(telemetry, 'empty_corpus');\n this.bm25 = new BM25(this.items.map((i) => i.tokens));\n this.bm25Dirty = false;\n }\n\n const scores = this.bm25.scores(queryTokens);\n const validBundles = this.getValidBundleIds(accepted, conversationId, scopeRung ?? null);\n\n const effectiveThreshold = Math.max(\n EFFECTIVE_FLOOR,\n Math.min(THRESHOLDS.bm25, QUERY_TOKEN_SCALE * queryTokens.length),\n );\n telemetry.bm25Threshold = round4(effectiveThreshold);\n\n const picked: { score: number; item: ItemRecord }[] = [];\n const rejected: { score: number; payload: Record<string, unknown> }[] = [];\n\n for (let idx = 0; idx < scores.length; idx++) {\n const item = this.items[idx]!;\n const score = scores[idx]!;\n const reject = (reason: string) =>\n rejected.push({\n score,\n payload: {\n item_index: idx, bundle_id: item.bundleId, item_type: item.itemType,\n content_first_120: item.content.slice(0, PREVIEW_CHARS),\n bm25_score: round4(score), reason,\n },\n });\n if (score < effectiveThreshold) { reject('below_threshold'); continue; }\n if (!validBundles.has(item.bundleId)) { reject('scope_filter_excluded'); continue; }\n picked.push({ score, item });\n }\n\n rejected.sort((a, b) => b.score - a.score);\n telemetry.itemsRejected = rejected.slice(0, REJECTED_CAP).map((r) => r.payload);\n\n if (picked.length === 0) {\n return this.miss(telemetry, 'no_items_above_threshold');\n }\n\n picked.sort((a, b) => b.score - a.score);\n const top = picked.slice(0, maxItems);\n\n const pickedStems = new Set<string>();\n for (const { item } of top) for (const t of item.tokens) pickedStems.add(t);\n let overlap = 0;\n for (const s of uniqueStems) if (pickedStems.has(s)) overlap++;\n const coverage = uniqueStems.size ? overlap / uniqueStems.size : 1.0;\n telemetry.coverage = round4(coverage);\n\n // Observe-only unless explicitly configured. A malformed setting stays\n // observe-only rather than failing closed on every lookup.\n const coverageMin = getEnvFloat('SYNAP_SDK_CACHE_COVERAGE_MIN');\n if (coverageMin !== null && coverage < coverageMin) {\n // Gate BEFORE any side effect, so a rejected lookup leaves cache state\n // untouched and does not refresh a lease.\n return this.miss(telemetry, 'coverage_gate');\n }\n\n return this.serve(top, telemetry, coverage, 'bm25_score');\n }\n\n /**\n * Match by meaning: a lookup is a hit when an item's cosine to a query\n * reaches `minCosine`, and the hit also serves the items at `serveMinCosine`\n * or above. An item with no vector yet is matched by BM25 with its novel-term\n * gate, as before. Python: `_semantic_lookup`, which explains the choices.\n */\n private semanticLookup(\n queryTokens: string[],\n queryVectors: readonly (readonly number[])[],\n accepted: Set<string>,\n conversationId: string | null,\n scopeRung: string | null,\n maxItems: number,\n telemetry: LookupTelemetry,\n ): LookupResult | null {\n const model = this.encoder?.model ?? '';\n if (queryTokens.length > 0 && (this.bm25Dirty || this.bm25 === null) && this.items.length > 0) {\n this.bm25 = new BM25(this.items.map((i) => i.tokens));\n this.bm25Dirty = false;\n }\n const bm25Scores = queryTokens.length > 0 && this.bm25 ? this.bm25.scores(queryTokens) : [];\n const effectiveThreshold = Math.max(\n EFFECTIVE_FLOOR,\n Math.min(THRESHOLDS.bm25, QUERY_TOKEN_SCALE * Math.max(queryTokens.length, 1)),\n );\n telemetry.bm25Threshold = round4(effectiveThreshold);\n const uniqueStems = new Set(queryTokens);\n let novel = 0;\n for (const s of uniqueStems) if (!this.corpusVocab.has(s)) novel++;\n const novelRatio = uniqueStems.size ? novel / uniqueStems.size : 1;\n telemetry.novelTermRatio = round4(novelRatio);\n const wordRoute = queryTokens.length > 0 && !(\n this.corpusVocab.size >= THRESHOLDS.minCorpusForNovelGate && novelRatio >= THRESHOLDS.novelTerm\n );\n\n const validBundles = this.getValidBundleIds(accepted, conversationId, scopeRung);\n const picked: { score: number; item: ItemRecord }[] = [];\n const companions: { score: number; item: ItemRecord }[] = [];\n const rejected: { score: number; payload: Record<string, unknown> }[] = [];\n this.items.forEach((item, idx) => {\n const b = bm25Scores[idx] ?? 0;\n const reject = (score: number, cos: number | null, reason: string) =>\n rejected.push({\n score,\n payload: {\n item_index: idx, bundle_id: item.bundleId, item_type: item.itemType,\n content_first_120: item.content.slice(0, PREVIEW_CHARS),\n bm25_score: round4(b), cosine: cos === null ? null : round4(cos), reason,\n },\n });\n // Scope first: another user's items are never scored.\n if (!validBundles.has(item.bundleId)) return reject(b, null, 'scope_filter_excluded');\n let passes: boolean;\n let rank: number;\n let cos: number | null = null;\n if (item.vec !== null && item.vecModel === model) {\n // The memory's own text, and the questions it answers.\n const vecs = [item.vec, ...item.qvecs];\n cos = Math.max(...queryVectors.flatMap((q) => vecs.map((v) => cosine(q, v))));\n passes = cos >= this.minCosine;\n rank = cos;\n } else {\n passes = wordRoute && b >= effectiveThreshold;\n rank = this.minCosine; // not embedded yet: ranked at the threshold\n }\n if (!passes && cos !== null && cos >= Math.min(this.serveMinCosine, this.bundleServeMinCosine)) {\n companions.push({ score: cos, item });\n return;\n }\n if (!passes) return reject(rank, cos, 'below_threshold');\n picked.push({ score: rank, item });\n });\n\n rejected.sort((a, b) => b.score - a.score);\n telemetry.itemsRejected = rejected.slice(0, REJECTED_CAP).map((r) => r.payload);\n if (picked.length === 0) return this.miss(telemetry, 'no_items_above_threshold');\n picked.sort((a, b) => b.score - a.score);\n // Served with a hit: items above the serve bar, and items from a bundle\n // that holds a hit above the lower same-bundle bar.\n const hitBundles = new Set(picked.map((p) => p.item.bundleId));\n const served = companions.filter(\n (c) => c.score >= this.serveMinCosine || (hitBundles.has(c.item.bundleId) && c.score >= this.bundleServeMinCosine),\n );\n served.sort((a, b) => b.score - a.score);\n const top = [...picked, ...served].slice(0, maxItems);\n\n // A question for a kind of value (a number to call, an amount, a date) is\n // not answered by items on its topic that hold no such value.\n if (this.askedKind) {\n const patterns = askedDetailPatterns(telemetry.searchQuery);\n if (patterns.length > 0 && !top.some(({ item }) => patterns.some((p) => p.test(item.content)))) {\n return this.miss(telemetry, 'asked_kind_missing');\n }\n }\n const hit = this.serve(top, telemetry, null, 'cosine');\n // Below the sure bar a hit is borderline: the caller may still ask the\n // server, with a short deadline, and serve this only if it is late.\n hit.band = picked[0]!.score >= this.sureMinCosine ? 'sure' : 'borderline';\n return hit;\n }\n\n /** A hit: renew the leases of the bundles used, report, merge the items. */\n private serve(\n top: { score: number; item: ItemRecord }[],\n telemetry: LookupTelemetry,\n coverage: number | null,\n scoreKey: 'bm25_score' | 'cosine',\n ): LookupResult {\n const itemsByType: Record<string, BundleItem[]> = {};\n const bundleIds = new Set<string>();\n for (const { item } of top) {\n (itemsByType[item.itemType] ??= []).push(item.itemDict);\n bundleIds.add(item.bundleId);\n }\n\n // Honouring a TTL hint also means a hit stops renewing the lease, otherwise\n // a hinted bundle under steady traffic would outlive its hint.\n const refreshOnHit = !getEnvFlag('SYNAP_SDK_CACHE_HONOR_TTL_HINT');\n if (refreshOnHit) {\n const now = this.now();\n for (const id of bundleIds) {\n const entry = this.entries.get(id);\n if (entry) entry.storedAt = now;\n }\n }\n\n telemetry.hit = true;\n telemetry.exitReason = 'hit';\n telemetry.itemsPicked = top.map(({ score, item }) => ({\n bundle_id: item.bundleId, item_type: item.itemType,\n content_first_120: item.content.slice(0, PREVIEW_CHARS), [scoreKey]: round4(score),\n }));\n this.fire(telemetry);\n\n return {\n itemsByType,\n bundleIds: [...bundleIds],\n score: top[0]!.score,\n coverage: coverage ?? 1.0,\n };\n }\n\n /**\n * Drop this entity's bundles after a write, so the cache stops serving a\n * pre-write view. Gated on the flag: this is the write-path hook.\n */\n invalidateEntity(entityId: string): number {\n if (!getEnvFlag('SYNAP_SDK_CACHE_INVALIDATE_ON_WRITE')) return 0;\n return this.dropEntity(entityId);\n }\n\n /**\n * Unconditionally drop this entity's bundles. Backs `cache.clear_user` and\n * `cache.clear_customer`, which are explicit user requests and so are not\n * gated on the invalidate-on-write flag.\n */\n dropEntity(entityId: string): number {\n let dropped = 0;\n for (const entry of [...this.entries.values()]) {\n // Never drop shared client-scope knowledge on one entity's behalf.\n if (entry.entityId === ANY_SCOPE) continue;\n if (entry.entityId === entityId) { this.dropBundle(entry.bundleId); dropped++; }\n }\n return dropped;\n }\n\n /**\n * A read-only diagnostic projection of the cache.\n *\n * Backs `client.anticipation_cache_snapshot()` and mirrors Python's shape\n * key for key, including snake_case keys: this output gets logged, diffed\n * and pasted into issues across both SDKs, so it is a wire format.\n *\n * Note the 140-character content previews. That is Python's literal, and it\n * is deliberately NOT the 120 of `telemetry.content_preview_chars` from the\n * contract, which caps lookup telemetry rather than this snapshot.\n */\n snapshot(): AnticipationCacheSnapshot {\n const SNAPSHOT_PREVIEW_CHARS = 140;\n const overallScope = new Map<string, number>();\n const itemsByBundle = new Map<string, ItemRecord[]>();\n for (const record of this.items) {\n const list = itemsByBundle.get(record.bundleId);\n if (list === undefined) itemsByBundle.set(record.bundleId, [record]);\n else list.push(record);\n }\n\n const bundles: SnapshotBundle[] = [];\n for (const [bundleId, entry] of this.entries) {\n const perBundle = new Map<string, number>();\n const itemPreviews: SnapshotItemPreview[] = [];\n for (const record of itemsByBundle.get(bundleId) ?? []) {\n // `scope` is absent on plenty of items; Python's Counter happily keys\n // on None, which JSON-serialises to null, so preserve the distinction\n // rather than coercing to a string.\n const scope = (record.itemDict['scope'] ?? null) as string | null;\n const key = scope === null ? '' : scope;\n overallScope.set(key, (overallScope.get(key) ?? 0) + 1);\n perBundle.set(key, (perBundle.get(key) ?? 0) + 1);\n itemPreviews.push({\n type: record.itemType,\n scope,\n content: record.content.slice(0, SNAPSHOT_PREVIEW_CHARS),\n });\n }\n let totalItems = 0;\n for (const n of perBundle.values()) totalItems += n;\n bundles.push({\n bundle_id: bundleId,\n entity_id: entry.entityId,\n conversation_id: entry.conversationId,\n bundle_type: entry.bundleType,\n search_queries: [...entry.searchQueries],\n scope_counts: Object.fromEntries(perBundle),\n total_items: totalItems,\n items: itemPreviews,\n });\n }\n\n return {\n total_entries: this.entries.size,\n total_item_records: this.items.length,\n scope_breakdown_overall: Object.fromEntries(overallScope),\n corpus_vocab_size: this.corpusVocab.size,\n corpus_vocab_sample: [...this.corpusVocab].sort().slice(0, 80),\n item_records: this.items.slice(0, 20).map((rec) => ({\n bundle_id: rec.bundleId,\n item_type: rec.itemType,\n tokens: rec.tokens.slice(0, 25),\n content: rec.content.slice(0, SNAPSHOT_PREVIEW_CHARS),\n })),\n bundles,\n };\n }\n\n /**\n * The freshest `user_summary` bundle for this entity, or null.\n *\n * `entityId` is REQUIRED, and a missing one refuses the lookup rather than\n * falling back to the freshest summary across all users. Python hardened this\n * for exactly that reason: the permissive version could splice user A's\n * summary into user B's response on the conversation-scope path.\n */\n lookupUserSummary(\n entityId: string | null | undefined,\n scopeRung: string | null = null,\n ): Record<string, unknown> | null {\n if (!entityId) return null;\n this.evictExpired();\n\n let freshest: EntryRecord | null = null;\n for (const entry of this.entries.values()) {\n if (entry.bundleType !== 'user_summary') continue;\n if (!rungServes(entry.scopeRung, scopeRung)) continue;\n // ANY_SCOPE covers client-shared summaries, which are safe for everyone.\n if (entry.entityId !== entityId && entry.entityId !== ANY_SCOPE) continue;\n if (unownedElsewhere(entry.entityId, entry.conversationId, null)) continue;\n if (freshest === null || entry.storedAt > freshest.storedAt) freshest = entry;\n }\n if (freshest === null) return null;\n\n // Serving refreshes the sliding lease, unless the server's TTL hint is\n // being honoured, in which case the hint owns expiry.\n if (!getEnvFlag('SYNAP_SDK_CACHE_HONOR_TTL_HINT')) {\n freshest.storedAt = this.now();\n }\n return this.#bundleFor(freshest.bundleId);\n }\n\n /** Rebuild a bundle's items_by_type from the flat item records. */\n #bundleFor(bundleId: string): Record<string, unknown> {\n const itemsByType: Record<string, BundleItem[]> = {};\n for (const record of this.items) {\n if (record.bundleId !== bundleId) continue;\n (itemsByType[record.itemType] ??= []).push(record.itemDict);\n }\n return { bundle_id: bundleId, items_by_type: itemsByType };\n }\n\n /**\n * Drop every bundle belonging to one conversation.\n *\n * Backs the `compaction_update` signal on the Listen stream: a compaction\n * rewrites the conversation's summary, so any bundle built from the previous\n * state is stale and must not keep serving for the rest of its TTL.\n *\n * Unlike `dropEntity`, client-scope bundles are NOT spared: a bundle is only\n * dropped here if it is bound to this specific conversation, and a shared\n * client-scope bundle has no conversation id to match.\n */\n dropConversation(conversationId: string): number {\n if (!conversationId) return 0;\n let dropped = 0;\n for (const entry of [...this.entries.values()]) {\n if (entry.conversationId === conversationId) { this.dropBundle(entry.bundleId); dropped++; }\n }\n return dropped;\n }\n\n clear(): void {\n this.entries.clear();\n this.items = [];\n this.corpusVocab.clear();\n this.bm25 = null;\n this.bm25Dirty = true;\n }\n\n // ── internals ───────────────────────────────────────────────────────────────\n\n private miss(telemetry: LookupTelemetry, reason: ExitReason): null {\n telemetry.exitReason = reason;\n telemetry.hit = false;\n this.fire(telemetry);\n return null;\n }\n\n private fire(telemetry: LookupTelemetry): void {\n // Exception-safe by contract: a buggy hook must never break the SDK.\n try {\n this.onLookup?.(telemetry);\n } catch {\n /* ignored on purpose */\n }\n }\n\n private freshnessLookup(\n owner: string | null,\n conversationId: string | null,\n scopeRung: string | null,\n maxItems: number,\n telemetry: LookupTelemetry,\n ): LookupResult | null {\n // This path serves a WHOLE bundle on no query at all, so it is the least\n // filtered way out of this cache and needs the rung as much as the scored\n // path does. `owner` is the user, or the customer on a customer fetch. With\n // neither named it serves only ANY_SCOPE bundles: it used to serve the\n // newest bundle of ANY user, so a customer or client fetch with no query\n // got a stranger's items.\n const candidates = [...this.entries.values()]\n .filter((e) => rungServes(e.scopeRung, scopeRung))\n .filter((e) => e.entityId === ANY_SCOPE || (owner !== null && owner !== '' && e.entityId === owner))\n .filter((e) => !unownedElsewhere(e.entityId, e.conversationId, conversationId))\n .sort((a, b) => b.storedAt - a.storedAt);\n if (candidates.length === 0) return this.miss(telemetry, 'empty');\n\n const newest = candidates[0]!;\n const itemsByType: Record<string, BundleItem[]> = {};\n let n = 0;\n for (const item of this.items) {\n if (item.bundleId !== newest.bundleId) continue;\n if (n >= maxItems) break;\n (itemsByType[item.itemType] ??= []).push(item.itemDict);\n n++;\n }\n if (n === 0) return this.miss(telemetry, 'empty');\n\n telemetry.hit = true;\n telemetry.exitReason = 'hit';\n this.fire(telemetry);\n return { itemsByType, bundleIds: [newest.bundleId], score: 0, coverage: 1.0 };\n }\n\n /**\n * The widened scope-match set for a request.\n *\n * A narrower-scope request also matches broader-scope bundles for the same\n * customer or client. Falsy ids are dropped so an empty customer_id cannot\n * accidentally match bundles keyed at \"\".\n */\n private buildAcceptedScope(\n entityId: string | null, customerId: string | null, clientId: string | null,\n ): Set<string> {\n const accepted = new Set<string>([ANY_SCOPE]);\n if (entityId) accepted.add(entityId);\n if (customerId) accepted.add(customerId);\n if (clientId) accepted.add(clientId);\n return accepted;\n }\n\n /**\n * The rung is checked FIRST and it never widens. Everything else here widens\n * deliberately: a user-scope request reaches customer-shared bundles, and a\n * conversation-scope request reaches user-scope ones. A rung is the opposite\n * kind of thing, so it is checked before the tiers and no amount of widening\n * can put a cross-rung bundle back in.\n */\n private getValidBundleIds(\n accepted: Set<string>, conversationId: string | null, scopeRung: string | null,\n ): Set<string> {\n const valid = new Set<string>();\n for (const entry of this.entries.values()) {\n if (!rungServes(entry.scopeRung, scopeRung)) continue;\n if (!accepted.has(entry.entityId)) continue;\n // A conversation-scoped bundle is only valid inside its conversation.\n if (entry.conversationId && conversationId && entry.conversationId !== conversationId) continue;\n if (unownedElsewhere(entry.entityId, entry.conversationId, conversationId)) continue;\n valid.add(entry.bundleId);\n }\n return valid;\n }\n\n private effectiveTtl(entry: EntryRecord): number {\n if (!getEnvFlag('SYNAP_SDK_CACHE_HONOR_TTL_HINT')) return this.ttlSeconds;\n if (entry.ttlHintSeconds == null || entry.ttlHintSeconds <= 0) return this.ttlSeconds;\n // A hint may only shorten.\n return Math.min(this.ttlSeconds, entry.ttlHintSeconds);\n }\n\n private evictExpired(): void {\n const now = this.now();\n // Default cap is twice the sliding TTL; 0 disables. Without it a bundle\n // under steady traffic renews forever and can serve a retired value\n // indefinitely.\n const configuredMaxAge = getEnvFloat('SYNAP_SDK_CACHE_MAX_ENTRY_AGE');\n const maxAge = configuredMaxAge ?? this.ttlSeconds * 2;\n\n for (const entry of [...this.entries.values()]) {\n const idleExpired = now - entry.storedAt > this.effectiveTtl(entry);\n const ageExpired = maxAge > 0 && now - entry.createdAt > maxAge;\n if (idleExpired || ageExpired) this.dropBundle(entry.bundleId);\n }\n }\n\n private dropBundle(bundleId: string): void {\n if (!this.entries.delete(bundleId)) return;\n this.items = this.items.filter((i) => i.bundleId !== bundleId);\n // Vocabulary is rebuilt rather than decremented: a stem can be shared by\n // several bundles, so per-bundle subtraction would drop stems still in use.\n this.corpusVocab = new Set(this.items.flatMap((i) => i.tokens));\n this.bm25Dirty = true;\n }\n}\n\nfunction monotonicNow(): number {\n // performance.now() is monotonic and unaffected by wall-clock adjustments,\n // matching Python's time.monotonic(). Date.now() would let an NTP step\n // expire or resurrect every entry at once.\n return typeof performance !== 'undefined' && typeof performance.now === 'function'\n ? performance.now() / 1000\n : Date.now() / 1000;\n}\n\nfunction round4(n: number): number {\n return Number(n.toFixed(4));\n}\n"],"mappings":"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","names":["cos"]}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
var _chunkT372OL3Qcjs = require('./chunk-T372OL3Q.cjs');
|
|
6
6
|
|
|
7
7
|
// src/grpc/descriptor.ts
|
|
8
|
-
var PROTO_SHA = "
|
|
8
|
+
var PROTO_SHA = "9857182b0cf458b4";
|
|
9
9
|
var SYNAP_PROTO_DESCRIPTOR = {
|
|
10
10
|
"nested": {
|
|
11
11
|
"synap": {
|
|
@@ -284,7 +284,8 @@ var SYNAP_PROTO_DESCRIPTOR = {
|
|
|
284
284
|
"context_bundle",
|
|
285
285
|
"heartbeat_pong",
|
|
286
286
|
"signal",
|
|
287
|
-
"event_ack"
|
|
287
|
+
"event_ack",
|
|
288
|
+
"conversation_state"
|
|
288
289
|
]
|
|
289
290
|
}
|
|
290
291
|
},
|
|
@@ -304,6 +305,22 @@ var SYNAP_PROTO_DESCRIPTOR = {
|
|
|
304
305
|
"event_ack": {
|
|
305
306
|
"type": "EventAck",
|
|
306
307
|
"id": 4
|
|
308
|
+
},
|
|
309
|
+
"conversation_state": {
|
|
310
|
+
"type": "ConversationState",
|
|
311
|
+
"id": 5
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
"ConversationState": {
|
|
316
|
+
"fields": {
|
|
317
|
+
"conversation_id": {
|
|
318
|
+
"type": "string",
|
|
319
|
+
"id": 1
|
|
320
|
+
},
|
|
321
|
+
"message_count": {
|
|
322
|
+
"type": "int64",
|
|
323
|
+
"id": 2
|
|
307
324
|
}
|
|
308
325
|
}
|
|
309
326
|
},
|
|
@@ -480,6 +497,18 @@ var SYNAP_PROTO_DESCRIPTOR = {
|
|
|
480
497
|
"conversation_id": {
|
|
481
498
|
"type": "string",
|
|
482
499
|
"id": 7
|
|
500
|
+
},
|
|
501
|
+
"compacted_message_count": {
|
|
502
|
+
"type": "int32",
|
|
503
|
+
"id": 8
|
|
504
|
+
},
|
|
505
|
+
"total_message_count": {
|
|
506
|
+
"type": "int32",
|
|
507
|
+
"id": 9
|
|
508
|
+
},
|
|
509
|
+
"context_block": {
|
|
510
|
+
"type": "string",
|
|
511
|
+
"id": 10
|
|
483
512
|
}
|
|
484
513
|
}
|
|
485
514
|
},
|
|
@@ -509,6 +538,13 @@ var SYNAP_PROTO_DESCRIPTOR = {
|
|
|
509
538
|
}
|
|
510
539
|
},
|
|
511
540
|
"ContextItemProto": {
|
|
541
|
+
"oneofs": {
|
|
542
|
+
"_rank_score": {
|
|
543
|
+
"oneof": [
|
|
544
|
+
"rank_score"
|
|
545
|
+
]
|
|
546
|
+
}
|
|
547
|
+
},
|
|
512
548
|
"fields": {
|
|
513
549
|
"item_id": {
|
|
514
550
|
"type": "string",
|
|
@@ -569,6 +605,18 @@ var SYNAP_PROTO_DESCRIPTOR = {
|
|
|
569
605
|
"said_at": {
|
|
570
606
|
"type": "string",
|
|
571
607
|
"id": 15
|
|
608
|
+
},
|
|
609
|
+
"rank_score": {
|
|
610
|
+
"type": "double",
|
|
611
|
+
"id": 16,
|
|
612
|
+
"options": {
|
|
613
|
+
"proto3_optional": true
|
|
614
|
+
}
|
|
615
|
+
},
|
|
616
|
+
"questions": {
|
|
617
|
+
"rule": "repeated",
|
|
618
|
+
"type": "string",
|
|
619
|
+
"id": 17
|
|
572
620
|
}
|
|
573
621
|
}
|
|
574
622
|
},
|
|
@@ -1270,8 +1318,16 @@ var GrpcStreamClient = class {
|
|
|
1270
1318
|
this.#missedHeartbeats = 0;
|
|
1271
1319
|
return;
|
|
1272
1320
|
}
|
|
1321
|
+
const state = msg["conversation_state"];
|
|
1322
|
+
if (state) {
|
|
1323
|
+
const count = Number(_nullishCoalesce(state.message_count, () => ( 0)));
|
|
1324
|
+
if (state.conversation_id && Number.isFinite(count)) {
|
|
1325
|
+
_optionalChain([this, 'access', _23 => _23.#options, 'access', _24 => _24.onConversationState, 'optionalCall', _25 => _25(state.conversation_id, count)]);
|
|
1326
|
+
}
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1273
1329
|
const signal = msg["signal"];
|
|
1274
|
-
if (_optionalChain([signal, 'optionalAccess',
|
|
1330
|
+
if (_optionalChain([signal, 'optionalAccess', _26 => _26.signal_type]) === "closing") {
|
|
1275
1331
|
void this.#handleDisconnect("server_signal_closing");
|
|
1276
1332
|
}
|
|
1277
1333
|
}
|
|
@@ -1280,14 +1336,14 @@ var GrpcStreamClient = class {
|
|
|
1280
1336
|
const conversationId = String(_nullishCoalesce(bundle["anticipation_conversation_id"], () => ( "")));
|
|
1281
1337
|
if (bundleType !== UNCACHEABLE_BUNDLE_TYPE) this.#store(bundle, bundleType, conversationId);
|
|
1282
1338
|
if (bundleType === "compaction_update" && conversationId !== "") {
|
|
1283
|
-
_optionalChain([this, 'access',
|
|
1339
|
+
_optionalChain([this, 'access', _27 => _27.#options, 'access', _28 => _28.onCompactionUpdate, 'optionalCall', _29 => _29(conversationId, bundle)]);
|
|
1284
1340
|
}
|
|
1285
|
-
_optionalChain([this, 'access',
|
|
1341
|
+
_optionalChain([this, 'access', _30 => _30.#options, 'access', _31 => _31.onContext, 'optionalCall', _32 => _32(bundle)]);
|
|
1286
1342
|
}
|
|
1287
1343
|
#store(bundle, bundleType, conversationId) {
|
|
1288
1344
|
const itemsByType = {};
|
|
1289
1345
|
for (const [type, list] of Object.entries(_nullishCoalesce(bundle["items_by_type"], () => ( {})))) {
|
|
1290
|
-
const items = _optionalChain([list, 'optionalAccess',
|
|
1346
|
+
const items = _optionalChain([list, 'optionalAccess', _33 => _33.items]);
|
|
1291
1347
|
itemsByType[type] = _nullishCoalesce(items, () => ( []));
|
|
1292
1348
|
}
|
|
1293
1349
|
const ttlHint = Number(_nullishCoalesce(bundle["ttl_hint_seconds"], () => ( 0)));
|
|
@@ -1368,7 +1424,7 @@ var GrpcStreamClient = class {
|
|
|
1368
1424
|
void this.#handleDisconnect("heartbeat_write_error");
|
|
1369
1425
|
}
|
|
1370
1426
|
}, HEARTBEAT_INTERVAL_MS);
|
|
1371
|
-
_optionalChain([this, 'access',
|
|
1427
|
+
_optionalChain([this, 'access', _34 => _34.#heartbeatTimer, 'access', _35 => _35.unref, 'optionalCall', _36 => _36()]);
|
|
1372
1428
|
}
|
|
1373
1429
|
#stopHeartbeat() {
|
|
1374
1430
|
if (this.#heartbeatTimer !== null) {
|
|
@@ -1378,14 +1434,14 @@ var GrpcStreamClient = class {
|
|
|
1378
1434
|
}
|
|
1379
1435
|
#closeCall() {
|
|
1380
1436
|
try {
|
|
1381
|
-
_optionalChain([this, 'access',
|
|
1437
|
+
_optionalChain([this, 'access', _37 => _37.#call, 'optionalAccess', _38 => _38.end, 'call', _39 => _39()]);
|
|
1382
1438
|
} catch (e11) {
|
|
1383
1439
|
}
|
|
1384
1440
|
this.#call = null;
|
|
1385
1441
|
}
|
|
1386
1442
|
#setState(state) {
|
|
1387
1443
|
this.#state = state;
|
|
1388
|
-
_optionalChain([this, 'access',
|
|
1444
|
+
_optionalChain([this, 'access', _40 => _40.#options, 'access', _41 => _41.onStateChange, 'optionalCall', _42 => _42(state)]);
|
|
1389
1445
|
}
|
|
1390
1446
|
};
|
|
1391
1447
|
|
|
@@ -1397,4 +1453,4 @@ var GrpcStreamClient = class {
|
|
|
1397
1453
|
|
|
1398
1454
|
|
|
1399
1455
|
exports.PROTO_SHA = PROTO_SHA; exports.CREDIT_ABORT_DETAILS = CREDIT_ABORT_DETAILS; exports.CREDIT_STOP_REASONS = CREDIT_STOP_REASONS; exports.creditErrorFromRpc = creditErrorFromRpc; exports.isCreditStop = isCreditStop; exports.GrpcStreamClient = GrpcStreamClient;
|
|
1400
|
-
//# sourceMappingURL=chunk-
|
|
1456
|
+
//# sourceMappingURL=chunk-RE4DDIWO.cjs.map
|