@maximem/synap-js-sdk 0.3.0 → 0.4.0

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.
Files changed (55) hide show
  1. package/MIGRATING.md +147 -0
  2. package/README.md +95 -82
  3. package/bin/synap-js-sdk.js +45 -122
  4. package/dist/anticipation-cache-BgkqcTPJ.d.cts +180 -0
  5. package/dist/anticipation-cache-BgkqcTPJ.d.ts +180 -0
  6. package/dist/anticipation-cache-D5YHEPV3.js +10 -0
  7. package/dist/anticipation-cache-D5YHEPV3.js.map +1 -0
  8. package/dist/anticipation-cache-JBZHTJ3O.cjs +10 -0
  9. package/dist/anticipation-cache-JBZHTJ3O.cjs.map +1 -0
  10. package/dist/chunk-5SC3ELNK.js +856 -0
  11. package/dist/chunk-5SC3ELNK.js.map +1 -0
  12. package/dist/chunk-HR5Y3KXS.cjs +172 -0
  13. package/dist/chunk-HR5Y3KXS.cjs.map +1 -0
  14. package/dist/chunk-NIDPTBNC.cjs +800 -0
  15. package/dist/chunk-NIDPTBNC.cjs.map +1 -0
  16. package/dist/chunk-QBCVMPGK.js +28 -0
  17. package/dist/chunk-QBCVMPGK.js.map +1 -0
  18. package/dist/chunk-T3NZBIV2.js +172 -0
  19. package/dist/chunk-T3NZBIV2.js.map +1 -0
  20. package/dist/chunk-TCEP5OVR.js +800 -0
  21. package/dist/chunk-TCEP5OVR.js.map +1 -0
  22. package/dist/chunk-VXRCBMCY.cjs +856 -0
  23. package/dist/chunk-VXRCBMCY.cjs.map +1 -0
  24. package/dist/chunk-ZKOJDUN4.cjs +28 -0
  25. package/dist/chunk-ZKOJDUN4.cjs.map +1 -0
  26. package/dist/env-55ULHNJZ.cjs +11 -0
  27. package/dist/env-55ULHNJZ.cjs.map +1 -0
  28. package/dist/env-QCUKKW36.js +11 -0
  29. package/dist/env-QCUKKW36.js.map +1 -0
  30. package/dist/grpc/index.cjs +87 -0
  31. package/dist/grpc/index.cjs.map +1 -0
  32. package/dist/grpc/index.d.cts +162 -0
  33. package/dist/grpc/index.d.ts +162 -0
  34. package/dist/grpc/index.js +87 -0
  35. package/dist/grpc/index.js.map +1 -0
  36. package/dist/index.cjs +2111 -0
  37. package/dist/index.cjs.map +1 -0
  38. package/dist/index.d.cts +1477 -0
  39. package/dist/index.d.ts +1477 -0
  40. package/dist/index.js +2111 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/stream-client-ME5N5NK4.js +7 -0
  43. package/dist/stream-client-ME5N5NK4.js.map +1 -0
  44. package/dist/stream-client-ZN6ADZBR.cjs +7 -0
  45. package/dist/stream-client-ZN6ADZBR.cjs.map +1 -0
  46. package/package.json +68 -23
  47. package/.env.example +0 -39
  48. package/bridge/synap_bridge.py +0 -768
  49. package/src/bridge-manager.js +0 -262
  50. package/src/errors.js +0 -168
  51. package/src/index.js +0 -13
  52. package/src/runtime.js +0 -161
  53. package/src/setup-typescript.js +0 -161
  54. package/src/synap-client.js +0 -464
  55. package/types/index.d.ts +0 -475
@@ -0,0 +1,180 @@
1
+ /**
2
+ * In-memory TTL cache for context bundles pushed over gRPC.
3
+ *
4
+ * Port of `maximem_synap/cache/anticipation_cache.py`.
5
+ *
6
+ * ## Why this is a faithful port and not a simplification
7
+ *
8
+ * `@maximem/synap-vercel-adk` already contains a native TS anticipation cache,
9
+ * and it is a simplification: a hardcoded 30-minute TTL, a plain BM25 threshold
10
+ * with no per-query derivation, and none of the gates below. Shipping that as
11
+ * the SDK's cache would mean two retrieval behaviors under one brand, which is
12
+ * gotcha G-C. Every gate here exists because a specific eval failed without it:
13
+ *
14
+ * - **Recall bypass** runs BEFORE scoring. The measured false hit scored 2.86
15
+ * at coverage 1.00, so no score-side gate could ever have caught it.
16
+ * - **The novel-term gate** is suppressed below a 200-stem corpus, because at
17
+ * cold start ordinary English trips it on nearly every query.
18
+ * - **The per-query threshold** scales down for short queries, which otherwise
19
+ * cannot reach the nominal score at all.
20
+ * - **The coverage gate** catches a bundle that clears BM25 on one shared
21
+ * token ("account") while lacking the asked-for fact.
22
+ *
23
+ * All thresholds come from the shared behavior contract.
24
+ */
25
+ /** Sentinel scope that matches any requester. */
26
+ declare const ANY_SCOPE = "_any";
27
+ interface BundleItem {
28
+ item_id?: string;
29
+ content?: string;
30
+ [key: string]: unknown;
31
+ }
32
+ interface ContextBundle {
33
+ bundleId: string;
34
+ entityId?: string | null;
35
+ conversationId?: string | null;
36
+ itemsByType: Record<string, BundleItem[]>;
37
+ /** Server hint; may only SHORTEN the global TTL, never extend it. */
38
+ ttlHintSeconds?: number | null;
39
+ /** Which anticipation pattern produced this bundle. Diagnostics only. */
40
+ bundleType?: string | null;
41
+ /** The queries the server anticipated. Diagnostics only. */
42
+ searchQueries?: readonly string[] | null;
43
+ }
44
+ interface LookupResult {
45
+ itemsByType: Record<string, BundleItem[]>;
46
+ bundleIds: string[];
47
+ score: number;
48
+ coverage: number;
49
+ }
50
+ interface LookupParams {
51
+ searchQuery?: readonly string[] | null;
52
+ entityId?: string | null;
53
+ customerId?: string | null;
54
+ clientId?: string | null;
55
+ conversationId?: string | null;
56
+ maxItems?: number;
57
+ }
58
+ type ExitReason = 'empty' | 'recall_bypass' | 'no_query_tokens' | 'novel_term_gate' | 'empty_corpus' | 'no_items_above_threshold' | 'coverage_gate' | 'hit';
59
+ interface LookupTelemetry {
60
+ searchQuery: string[];
61
+ entityId: string | null;
62
+ customerId: string | null;
63
+ clientId: string | null;
64
+ conversationId: string | null;
65
+ scopeFilterAccepted: string[];
66
+ novelTermRatio: number | null;
67
+ bm25Threshold: number | null;
68
+ bm25QueryTokens: string[];
69
+ itemsPicked: unknown[];
70
+ itemsRejected: unknown[];
71
+ coverage: number | null;
72
+ hit: boolean;
73
+ exitReason: ExitReason;
74
+ }
75
+ interface SnapshotItemPreview {
76
+ type: string;
77
+ scope: string | null;
78
+ content: string;
79
+ }
80
+ interface SnapshotItemRecord {
81
+ bundle_id: string;
82
+ item_type: string;
83
+ tokens: string[];
84
+ content: string;
85
+ }
86
+ interface SnapshotBundle {
87
+ bundle_id: string;
88
+ entity_id: string;
89
+ conversation_id: string | null;
90
+ bundle_type: string | null;
91
+ search_queries: string[];
92
+ scope_counts: Record<string, number>;
93
+ total_items: number;
94
+ items: SnapshotItemPreview[];
95
+ }
96
+ interface AnticipationCacheSnapshot {
97
+ total_entries: number;
98
+ total_item_records: number;
99
+ scope_breakdown_overall: Record<string, number>;
100
+ corpus_vocab_size: number;
101
+ corpus_vocab_sample: string[];
102
+ item_records: SnapshotItemRecord[];
103
+ bundles: SnapshotBundle[];
104
+ }
105
+ interface AnticipationCacheOptions {
106
+ ttlSeconds?: number;
107
+ maxBundles?: number;
108
+ /** Injected in tests. Defaults to a monotonic clock. */
109
+ now?: () => number;
110
+ onLookup?: (telemetry: LookupTelemetry) => void;
111
+ }
112
+ declare class AnticipationCache {
113
+ private readonly ttlSeconds;
114
+ private readonly maxBundles;
115
+ private readonly now;
116
+ private readonly onLookup;
117
+ private entries;
118
+ private items;
119
+ private corpusVocab;
120
+ private bm25;
121
+ private bm25Dirty;
122
+ constructor(options?: AnticipationCacheOptions);
123
+ get size(): number;
124
+ get itemCount(): number;
125
+ store(bundle: ContextBundle): void;
126
+ lookup(params: LookupParams): LookupResult | null;
127
+ /**
128
+ * Drop this entity's bundles after a write, so the cache stops serving a
129
+ * pre-write view. Gated on the flag: this is the write-path hook.
130
+ */
131
+ invalidateEntity(entityId: string): number;
132
+ /**
133
+ * Unconditionally drop this entity's bundles. Backs `cache.clear_user` and
134
+ * `cache.clear_customer`, which are explicit user requests and so are not
135
+ * gated on the invalidate-on-write flag.
136
+ */
137
+ dropEntity(entityId: string): number;
138
+ /**
139
+ * A read-only diagnostic projection of the cache.
140
+ *
141
+ * Backs `client.anticipation_cache_snapshot()` and mirrors Python's shape
142
+ * key for key, including snake_case keys: this output gets logged, diffed
143
+ * and pasted into issues across both SDKs, so it is a wire format.
144
+ *
145
+ * Note the 140-character content previews. That is Python's literal, and it
146
+ * is deliberately NOT the 120 of `telemetry.content_preview_chars` from the
147
+ * contract, which caps lookup telemetry rather than this snapshot.
148
+ */
149
+ snapshot(): AnticipationCacheSnapshot;
150
+ /**
151
+ * Drop every bundle belonging to one conversation.
152
+ *
153
+ * Backs the `compaction_update` signal on the Listen stream: a compaction
154
+ * rewrites the conversation's summary, so any bundle built from the previous
155
+ * state is stale and must not keep serving for the rest of its TTL.
156
+ *
157
+ * Unlike `dropEntity`, client-scope bundles are NOT spared: a bundle is only
158
+ * dropped here if it is bound to this specific conversation, and a shared
159
+ * client-scope bundle has no conversation id to match.
160
+ */
161
+ dropConversation(conversationId: string): number;
162
+ clear(): void;
163
+ private miss;
164
+ private fire;
165
+ private freshnessLookup;
166
+ /**
167
+ * The widened scope-match set for a request.
168
+ *
169
+ * A narrower-scope request also matches broader-scope bundles for the same
170
+ * customer or client. Falsy ids are dropped so an empty customer_id cannot
171
+ * accidentally match bundles keyed at "".
172
+ */
173
+ private buildAcceptedScope;
174
+ private getValidBundleIds;
175
+ private effectiveTtl;
176
+ private evictExpired;
177
+ private dropBundle;
178
+ }
179
+
180
+ export { AnticipationCache as A, type BundleItem as B, type ContextBundle as C, type ExitReason as E, type LookupParams as L, type SnapshotBundle as S, type AnticipationCacheSnapshot as a, ANY_SCOPE as b, type AnticipationCacheOptions as c, type LookupResult as d, type LookupTelemetry as e, type SnapshotItemPreview as f, type SnapshotItemRecord as g };
@@ -0,0 +1,180 @@
1
+ /**
2
+ * In-memory TTL cache for context bundles pushed over gRPC.
3
+ *
4
+ * Port of `maximem_synap/cache/anticipation_cache.py`.
5
+ *
6
+ * ## Why this is a faithful port and not a simplification
7
+ *
8
+ * `@maximem/synap-vercel-adk` already contains a native TS anticipation cache,
9
+ * and it is a simplification: a hardcoded 30-minute TTL, a plain BM25 threshold
10
+ * with no per-query derivation, and none of the gates below. Shipping that as
11
+ * the SDK's cache would mean two retrieval behaviors under one brand, which is
12
+ * gotcha G-C. Every gate here exists because a specific eval failed without it:
13
+ *
14
+ * - **Recall bypass** runs BEFORE scoring. The measured false hit scored 2.86
15
+ * at coverage 1.00, so no score-side gate could ever have caught it.
16
+ * - **The novel-term gate** is suppressed below a 200-stem corpus, because at
17
+ * cold start ordinary English trips it on nearly every query.
18
+ * - **The per-query threshold** scales down for short queries, which otherwise
19
+ * cannot reach the nominal score at all.
20
+ * - **The coverage gate** catches a bundle that clears BM25 on one shared
21
+ * token ("account") while lacking the asked-for fact.
22
+ *
23
+ * All thresholds come from the shared behavior contract.
24
+ */
25
+ /** Sentinel scope that matches any requester. */
26
+ declare const ANY_SCOPE = "_any";
27
+ interface BundleItem {
28
+ item_id?: string;
29
+ content?: string;
30
+ [key: string]: unknown;
31
+ }
32
+ interface ContextBundle {
33
+ bundleId: string;
34
+ entityId?: string | null;
35
+ conversationId?: string | null;
36
+ itemsByType: Record<string, BundleItem[]>;
37
+ /** Server hint; may only SHORTEN the global TTL, never extend it. */
38
+ ttlHintSeconds?: number | null;
39
+ /** Which anticipation pattern produced this bundle. Diagnostics only. */
40
+ bundleType?: string | null;
41
+ /** The queries the server anticipated. Diagnostics only. */
42
+ searchQueries?: readonly string[] | null;
43
+ }
44
+ interface LookupResult {
45
+ itemsByType: Record<string, BundleItem[]>;
46
+ bundleIds: string[];
47
+ score: number;
48
+ coverage: number;
49
+ }
50
+ interface LookupParams {
51
+ searchQuery?: readonly string[] | null;
52
+ entityId?: string | null;
53
+ customerId?: string | null;
54
+ clientId?: string | null;
55
+ conversationId?: string | null;
56
+ maxItems?: number;
57
+ }
58
+ type ExitReason = 'empty' | 'recall_bypass' | 'no_query_tokens' | 'novel_term_gate' | 'empty_corpus' | 'no_items_above_threshold' | 'coverage_gate' | 'hit';
59
+ interface LookupTelemetry {
60
+ searchQuery: string[];
61
+ entityId: string | null;
62
+ customerId: string | null;
63
+ clientId: string | null;
64
+ conversationId: string | null;
65
+ scopeFilterAccepted: string[];
66
+ novelTermRatio: number | null;
67
+ bm25Threshold: number | null;
68
+ bm25QueryTokens: string[];
69
+ itemsPicked: unknown[];
70
+ itemsRejected: unknown[];
71
+ coverage: number | null;
72
+ hit: boolean;
73
+ exitReason: ExitReason;
74
+ }
75
+ interface SnapshotItemPreview {
76
+ type: string;
77
+ scope: string | null;
78
+ content: string;
79
+ }
80
+ interface SnapshotItemRecord {
81
+ bundle_id: string;
82
+ item_type: string;
83
+ tokens: string[];
84
+ content: string;
85
+ }
86
+ interface SnapshotBundle {
87
+ bundle_id: string;
88
+ entity_id: string;
89
+ conversation_id: string | null;
90
+ bundle_type: string | null;
91
+ search_queries: string[];
92
+ scope_counts: Record<string, number>;
93
+ total_items: number;
94
+ items: SnapshotItemPreview[];
95
+ }
96
+ interface AnticipationCacheSnapshot {
97
+ total_entries: number;
98
+ total_item_records: number;
99
+ scope_breakdown_overall: Record<string, number>;
100
+ corpus_vocab_size: number;
101
+ corpus_vocab_sample: string[];
102
+ item_records: SnapshotItemRecord[];
103
+ bundles: SnapshotBundle[];
104
+ }
105
+ interface AnticipationCacheOptions {
106
+ ttlSeconds?: number;
107
+ maxBundles?: number;
108
+ /** Injected in tests. Defaults to a monotonic clock. */
109
+ now?: () => number;
110
+ onLookup?: (telemetry: LookupTelemetry) => void;
111
+ }
112
+ declare class AnticipationCache {
113
+ private readonly ttlSeconds;
114
+ private readonly maxBundles;
115
+ private readonly now;
116
+ private readonly onLookup;
117
+ private entries;
118
+ private items;
119
+ private corpusVocab;
120
+ private bm25;
121
+ private bm25Dirty;
122
+ constructor(options?: AnticipationCacheOptions);
123
+ get size(): number;
124
+ get itemCount(): number;
125
+ store(bundle: ContextBundle): void;
126
+ lookup(params: LookupParams): LookupResult | null;
127
+ /**
128
+ * Drop this entity's bundles after a write, so the cache stops serving a
129
+ * pre-write view. Gated on the flag: this is the write-path hook.
130
+ */
131
+ invalidateEntity(entityId: string): number;
132
+ /**
133
+ * Unconditionally drop this entity's bundles. Backs `cache.clear_user` and
134
+ * `cache.clear_customer`, which are explicit user requests and so are not
135
+ * gated on the invalidate-on-write flag.
136
+ */
137
+ dropEntity(entityId: string): number;
138
+ /**
139
+ * A read-only diagnostic projection of the cache.
140
+ *
141
+ * Backs `client.anticipation_cache_snapshot()` and mirrors Python's shape
142
+ * key for key, including snake_case keys: this output gets logged, diffed
143
+ * and pasted into issues across both SDKs, so it is a wire format.
144
+ *
145
+ * Note the 140-character content previews. That is Python's literal, and it
146
+ * is deliberately NOT the 120 of `telemetry.content_preview_chars` from the
147
+ * contract, which caps lookup telemetry rather than this snapshot.
148
+ */
149
+ snapshot(): AnticipationCacheSnapshot;
150
+ /**
151
+ * Drop every bundle belonging to one conversation.
152
+ *
153
+ * Backs the `compaction_update` signal on the Listen stream: a compaction
154
+ * rewrites the conversation's summary, so any bundle built from the previous
155
+ * state is stale and must not keep serving for the rest of its TTL.
156
+ *
157
+ * Unlike `dropEntity`, client-scope bundles are NOT spared: a bundle is only
158
+ * dropped here if it is bound to this specific conversation, and a shared
159
+ * client-scope bundle has no conversation id to match.
160
+ */
161
+ dropConversation(conversationId: string): number;
162
+ clear(): void;
163
+ private miss;
164
+ private fire;
165
+ private freshnessLookup;
166
+ /**
167
+ * The widened scope-match set for a request.
168
+ *
169
+ * A narrower-scope request also matches broader-scope bundles for the same
170
+ * customer or client. Falsy ids are dropped so an empty customer_id cannot
171
+ * accidentally match bundles keyed at "".
172
+ */
173
+ private buildAcceptedScope;
174
+ private getValidBundleIds;
175
+ private effectiveTtl;
176
+ private evictExpired;
177
+ private dropBundle;
178
+ }
179
+
180
+ export { AnticipationCache as A, type BundleItem as B, type ContextBundle as C, type ExitReason as E, type LookupParams as L, type SnapshotBundle as S, type AnticipationCacheSnapshot as a, ANY_SCOPE as b, type AnticipationCacheOptions as c, type LookupResult as d, type LookupTelemetry as e, type SnapshotItemPreview as f, type SnapshotItemRecord as g };
@@ -0,0 +1,10 @@
1
+ import {
2
+ ANY_SCOPE,
3
+ AnticipationCache
4
+ } from "./chunk-TCEP5OVR.js";
5
+ import "./chunk-QBCVMPGK.js";
6
+ export {
7
+ ANY_SCOPE,
8
+ AnticipationCache
9
+ };
10
+ //# sourceMappingURL=anticipation-cache-D5YHEPV3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,10 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+ var _chunkNIDPTBNCcjs = require('./chunk-NIDPTBNC.cjs');
5
+ require('./chunk-ZKOJDUN4.cjs');
6
+
7
+
8
+
9
+ exports.ANY_SCOPE = _chunkNIDPTBNCcjs.ANY_SCOPE; exports.AnticipationCache = _chunkNIDPTBNCcjs.AnticipationCache;
10
+ //# sourceMappingURL=anticipation-cache-JBZHTJ3O.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/anish/maximemai/maximem_synap/synap/sdk/js/dist/anticipation-cache-JBZHTJ3O.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACF,iHAAC","file":"/Users/anish/maximemai/maximem_synap/synap/sdk/js/dist/anticipation-cache-JBZHTJ3O.cjs"}