@maximem/synap-js-sdk 0.4.8 → 0.5.1

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 (39) hide show
  1. package/dist/anticipation-cache-LRE3TLK4.cjs.map +1 -1
  2. package/dist/{chunk-T5QQBJ6T.js → chunk-ASM2OIIG.js} +292 -7
  3. package/dist/chunk-ASM2OIIG.js.map +1 -0
  4. package/dist/{chunk-OCMM2Z5P.js → chunk-BWMPKLFG.js} +40 -2
  5. package/dist/chunk-BWMPKLFG.js.map +1 -0
  6. package/dist/{chunk-7CWBRHUO.cjs → chunk-IA2N5NUY.cjs} +302 -17
  7. package/dist/chunk-IA2N5NUY.cjs.map +1 -0
  8. package/dist/{chunk-2TVL5IPQ.cjs → chunk-T372OL3Q.cjs} +41 -3
  9. package/dist/chunk-T372OL3Q.cjs.map +1 -0
  10. package/dist/chunk-T6U5PVAD.cjs.map +1 -1
  11. package/dist/chunk-ZKOJDUN4.cjs.map +1 -1
  12. package/dist/env-55ULHNJZ.cjs.map +1 -1
  13. package/dist/errors-Bl3vY1eb.d.cts +382 -0
  14. package/dist/errors-Bl3vY1eb.d.ts +382 -0
  15. package/dist/grpc/index.cjs +7 -7
  16. package/dist/grpc/index.cjs.map +1 -1
  17. package/dist/grpc/index.d.cts +44 -4
  18. package/dist/grpc/index.d.ts +44 -4
  19. package/dist/grpc/index.js +5 -5
  20. package/dist/index.cjs +278 -161
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +97 -148
  23. package/dist/index.d.ts +97 -148
  24. package/dist/index.js +171 -54
  25. package/dist/index.js.map +1 -1
  26. package/dist/stream-client-6OWRYOA4.js +16 -0
  27. package/dist/stream-client-Y5JGSF2R.cjs +16 -0
  28. package/dist/stream-client-Y5JGSF2R.cjs.map +1 -0
  29. package/package.json +1 -1
  30. package/dist/anticipation-cache-C1rUEpaW.d.cts +0 -211
  31. package/dist/anticipation-cache-C1rUEpaW.d.ts +0 -211
  32. package/dist/chunk-2TVL5IPQ.cjs.map +0 -1
  33. package/dist/chunk-7CWBRHUO.cjs.map +0 -1
  34. package/dist/chunk-OCMM2Z5P.js.map +0 -1
  35. package/dist/chunk-T5QQBJ6T.js.map +0 -1
  36. package/dist/stream-client-2C5KIOWP.js +0 -7
  37. package/dist/stream-client-GPU37E6F.cjs +0 -7
  38. package/dist/stream-client-GPU37E6F.cjs.map +0 -1
  39. /package/dist/{stream-client-2C5KIOWP.js.map → stream-client-6OWRYOA4.js.map} +0 -0
@@ -0,0 +1,382 @@
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
+ * The scope rung the server retrieved this bundle at, as `rungKey` writes
45
+ * one. Absent from anything an older server sent, which reads as "named no
46
+ * rung" rather than as "matches anything".
47
+ */
48
+ scopeRung?: string | null;
49
+ }
50
+ interface LookupResult {
51
+ itemsByType: Record<string, BundleItem[]>;
52
+ bundleIds: string[];
53
+ score: number;
54
+ coverage: number;
55
+ }
56
+ interface LookupParams {
57
+ searchQuery?: readonly string[] | null;
58
+ entityId?: string | null;
59
+ customerId?: string | null;
60
+ clientId?: string | null;
61
+ conversationId?: string | null;
62
+ /**
63
+ * The rung this request names, from its `scope_path`. A SECOND, independent
64
+ * dimension and NOT another tier of the entity funnel: the funnel widens and
65
+ * the rung does not, because a rung is a boundary and a boundary that widens
66
+ * is not one.
67
+ */
68
+ scopeRung?: string | null;
69
+ maxItems?: number;
70
+ }
71
+ type ExitReason = 'empty' | 'recall_bypass' | 'no_query_tokens' | 'novel_term_gate' | 'empty_corpus' | 'no_items_above_threshold' | 'coverage_gate' | 'hit';
72
+ interface LookupTelemetry {
73
+ searchQuery: string[];
74
+ entityId: string | null;
75
+ customerId: string | null;
76
+ clientId: string | null;
77
+ conversationId: string | null;
78
+ scopeRung: string | null;
79
+ scopeFilterAccepted: string[];
80
+ novelTermRatio: number | null;
81
+ bm25Threshold: number | null;
82
+ bm25QueryTokens: string[];
83
+ itemsPicked: unknown[];
84
+ itemsRejected: unknown[];
85
+ coverage: number | null;
86
+ hit: boolean;
87
+ exitReason: ExitReason;
88
+ }
89
+ interface SnapshotItemPreview {
90
+ type: string;
91
+ scope: string | null;
92
+ content: string;
93
+ }
94
+ interface SnapshotItemRecord {
95
+ bundle_id: string;
96
+ item_type: string;
97
+ tokens: string[];
98
+ content: string;
99
+ }
100
+ interface SnapshotBundle {
101
+ bundle_id: string;
102
+ entity_id: string;
103
+ conversation_id: string | null;
104
+ bundle_type: string | null;
105
+ search_queries: string[];
106
+ scope_counts: Record<string, number>;
107
+ total_items: number;
108
+ items: SnapshotItemPreview[];
109
+ }
110
+ interface AnticipationCacheSnapshot {
111
+ total_entries: number;
112
+ total_item_records: number;
113
+ scope_breakdown_overall: Record<string, number>;
114
+ corpus_vocab_size: number;
115
+ corpus_vocab_sample: string[];
116
+ item_records: SnapshotItemRecord[];
117
+ bundles: SnapshotBundle[];
118
+ }
119
+ interface AnticipationCacheOptions {
120
+ ttlSeconds?: number;
121
+ maxBundles?: number;
122
+ /** Injected in tests. Defaults to a monotonic clock. */
123
+ now?: () => number;
124
+ onLookup?: (telemetry: LookupTelemetry) => void;
125
+ }
126
+ declare class AnticipationCache {
127
+ #private;
128
+ private readonly ttlSeconds;
129
+ private readonly maxBundles;
130
+ private readonly now;
131
+ private readonly onLookup;
132
+ private entries;
133
+ private items;
134
+ private corpusVocab;
135
+ private bm25;
136
+ private bm25Dirty;
137
+ constructor(options?: AnticipationCacheOptions);
138
+ get size(): number;
139
+ get itemCount(): number;
140
+ store(bundle: ContextBundle): void;
141
+ lookup(params: LookupParams): LookupResult | null;
142
+ /**
143
+ * Drop this entity's bundles after a write, so the cache stops serving a
144
+ * pre-write view. Gated on the flag: this is the write-path hook.
145
+ */
146
+ invalidateEntity(entityId: string): number;
147
+ /**
148
+ * Unconditionally drop this entity's bundles. Backs `cache.clear_user` and
149
+ * `cache.clear_customer`, which are explicit user requests and so are not
150
+ * gated on the invalidate-on-write flag.
151
+ */
152
+ dropEntity(entityId: string): number;
153
+ /**
154
+ * A read-only diagnostic projection of the cache.
155
+ *
156
+ * Backs `client.anticipation_cache_snapshot()` and mirrors Python's shape
157
+ * key for key, including snake_case keys: this output gets logged, diffed
158
+ * and pasted into issues across both SDKs, so it is a wire format.
159
+ *
160
+ * Note the 140-character content previews. That is Python's literal, and it
161
+ * is deliberately NOT the 120 of `telemetry.content_preview_chars` from the
162
+ * contract, which caps lookup telemetry rather than this snapshot.
163
+ */
164
+ snapshot(): AnticipationCacheSnapshot;
165
+ /**
166
+ * The freshest `user_summary` bundle for this entity, or null.
167
+ *
168
+ * `entityId` is REQUIRED, and a missing one refuses the lookup rather than
169
+ * falling back to the freshest summary across all users. Python hardened this
170
+ * for exactly that reason: the permissive version could splice user A's
171
+ * summary into user B's response on the conversation-scope path.
172
+ */
173
+ lookupUserSummary(entityId: string | null | undefined, scopeRung?: string | null): Record<string, unknown> | null;
174
+ /**
175
+ * Drop every bundle belonging to one conversation.
176
+ *
177
+ * Backs the `compaction_update` signal on the Listen stream: a compaction
178
+ * rewrites the conversation's summary, so any bundle built from the previous
179
+ * state is stale and must not keep serving for the rest of its TTL.
180
+ *
181
+ * Unlike `dropEntity`, client-scope bundles are NOT spared: a bundle is only
182
+ * dropped here if it is bound to this specific conversation, and a shared
183
+ * client-scope bundle has no conversation id to match.
184
+ */
185
+ dropConversation(conversationId: string): number;
186
+ clear(): void;
187
+ private miss;
188
+ private fire;
189
+ private freshnessLookup;
190
+ /**
191
+ * The widened scope-match set for a request.
192
+ *
193
+ * A narrower-scope request also matches broader-scope bundles for the same
194
+ * customer or client. Falsy ids are dropped so an empty customer_id cannot
195
+ * accidentally match bundles keyed at "".
196
+ */
197
+ private buildAcceptedScope;
198
+ /**
199
+ * The rung is checked FIRST and it never widens. Everything else here widens
200
+ * deliberately: a user-scope request reaches customer-shared bundles, and a
201
+ * conversation-scope request reaches user-scope ones. A rung is the opposite
202
+ * kind of thing, so it is checked before the tiers and no amount of widening
203
+ * can put a cross-rung bundle back in.
204
+ */
205
+ private getValidBundleIds;
206
+ private effectiveTtl;
207
+ private evictExpired;
208
+ private dropBundle;
209
+ }
210
+
211
+ /**
212
+ * Synap SDK error taxonomy.
213
+ *
214
+ * Mirrors `maximem_synap/models/errors.py` 1:1, including the four
215
+ * backward-compatibility aliases at the bottom. If you add an error to one
216
+ * SDK, add it to the other in the same change.
217
+ *
218
+ * ## Why every error carries a `.code`
219
+ *
220
+ * This package ships dual ESM + CJS. A consumer whose dependency graph pulls
221
+ * in both formats gets *two* copies of every class in this file, and then:
222
+ *
223
+ * try { ... } catch (e) { if (e instanceof SynapError) { ... } }
224
+ *
225
+ * silently evaluates to `false` against the copy from the other format. Error
226
+ * handling stops working and nothing throws to tell you. `.code` is a plain
227
+ * string, so it is immune. Prefer it at package boundaries:
228
+ *
229
+ * if (isSynapError(e) && e.code === 'rate_limit') { ... }
230
+ *
231
+ * `Symbol.hasInstance` is also wired up on the base class, so `instanceof`
232
+ * works across realms too -- but `.code` is the documented contract.
233
+ */
234
+ /** Stable, format-independent discriminators. Never renumber or reuse. */
235
+ type SynapErrorCode = 'synap_error' | 'transient' | 'permanent' | 'network_timeout' | 'rate_limit' | 'insufficient_credits' | 'service_unavailable' | 'invalid_input' | 'invalid_instance_id' | 'invalid_conversation_id' | 'authentication' | 'context_not_found' | 'conflict' | 'transcript_conflict' | 'session_expired' | 'listening_already_active' | 'listening_not_active' | 'agent_unavailable';
236
+ interface SynapErrorOptions {
237
+ correlationId?: string | null;
238
+ cause?: unknown;
239
+ }
240
+ declare class SynapError extends Error {
241
+ /**
242
+ * The single declaration site for each subclass's code.
243
+ *
244
+ * It is `static` rather than a class field on purpose. Under
245
+ * `useDefineForClassFields` (the default at our ES2022 target) a class field
246
+ * is an *instance* property, so it is invisible from the constructor object.
247
+ * `Symbol.hasInstance` below only has the constructor to work with, so a
248
+ * field would leave it unable to tell `AuthenticationError` from
249
+ * `RateLimitError` -- it would report every Synap error as an instance of
250
+ * every other one.
251
+ */
252
+ static readonly errorCode: SynapErrorCode;
253
+ /** True for errors where a retry may succeed. Static for the same reason. */
254
+ static readonly isTransient: boolean;
255
+ /** Own, enumerable, so logs and JSON.stringify include it. */
256
+ readonly code: SynapErrorCode;
257
+ readonly correlationId: string | null;
258
+ readonly transient: boolean;
259
+ constructor(message: string, options?: SynapErrorOptions);
260
+ static [Symbol.hasInstance](instance: unknown): boolean;
261
+ }
262
+ /** Type guard that works across ESM/CJS copies and realms. */
263
+ declare function isSynapError(e: unknown): e is SynapError;
264
+ declare class SynapTransientError extends SynapError {
265
+ static readonly errorCode: SynapErrorCode;
266
+ static readonly isTransient: boolean;
267
+ }
268
+ declare class SynapPermanentError extends SynapError {
269
+ static readonly errorCode: SynapErrorCode;
270
+ static readonly isTransient: boolean;
271
+ }
272
+ declare class NetworkTimeoutError extends SynapTransientError {
273
+ static readonly errorCode: SynapErrorCode;
274
+ }
275
+ /**
276
+ * Rate limit exceeded (HTTP 429, gRPC RESOURCE_EXHAUSTED).
277
+ *
278
+ * Also carries the two credit stops the server reports as 429, because both
279
+ * recover with time or a plan change rather than with a retry of this request:
280
+ * `reason: 'trial_limit_reached'` (the Trial cap is spent, see `upgradeUrl`)
281
+ * and `reason: 'subscription_inactive'` (a paid plan whose subscription is past
282
+ * due, unpaid or cancelled, see `manageUrl`). `reason` is null for an ordinary
283
+ * rate limit and against a server too old to send one.
284
+ */
285
+ declare class RateLimitError extends SynapTransientError {
286
+ static readonly errorCode: SynapErrorCode;
287
+ readonly retryAfterSeconds: number | null;
288
+ /** Machine-readable cause, when the stop was a credit stop. */
289
+ readonly reason: string | null;
290
+ /** Where a Trial customer upgrades. Python calls this `upgrade_url`. */
291
+ readonly upgradeUrl: string | null;
292
+ /** Where billing is managed. Python calls this `manage_url`. */
293
+ readonly manageUrl: string | null;
294
+ constructor(message: string, options?: SynapErrorOptions & {
295
+ retryAfterSeconds?: number | null;
296
+ reason?: string | null;
297
+ upgradeUrl?: string | null;
298
+ manageUrl?: string | null;
299
+ });
300
+ }
301
+ declare class ServiceUnavailableError extends SynapTransientError {
302
+ static readonly errorCode: SynapErrorCode;
303
+ }
304
+ declare class AgentUnavailableError extends SynapTransientError {
305
+ static readonly errorCode: SynapErrorCode;
306
+ constructor(message?: string, options?: SynapErrorOptions);
307
+ }
308
+ /**
309
+ * The caller's credit wallet cannot cover this request.
310
+ *
311
+ * HTTP 402, and gRPC RESOURCE_EXHAUSTED with credit-related trailing metadata.
312
+ * Permanent by design: retrying without topping up burns quota and never
313
+ * succeeds. This is the error behind "the agent forgot" reports where retrieval
314
+ * silently returns nothing.
315
+ */
316
+ declare class InsufficientCreditsError extends SynapPermanentError {
317
+ static readonly errorCode: SynapErrorCode;
318
+ readonly balanceCredits: number | null;
319
+ readonly requiredCredits: number | null;
320
+ /** Where the balance can be viewed. Python calls this `recovery_url`. */
321
+ readonly recoveryUrl: string | null;
322
+ /** Where a redeem code can be entered. Python calls this `redeem_url`. */
323
+ readonly redeemUrl: string | null;
324
+ /**
325
+ * Machine-readable cause. `'overages_disabled'` means the plan is paid and
326
+ * the balance is spent, but the account has not allowed usage past zero, so
327
+ * the work stops until someone allows overages or adds credits. Null when an
328
+ * older server did not send one.
329
+ */
330
+ readonly reason: string | null;
331
+ /** Where that setting and billing are managed. Python calls this `manage_url`. */
332
+ readonly manageUrl: string | null;
333
+ constructor(message: string, options?: SynapErrorOptions & {
334
+ balanceCredits?: number | null;
335
+ requiredCredits?: number | null;
336
+ recoveryUrl?: string | null;
337
+ redeemUrl?: string | null;
338
+ reason?: string | null;
339
+ manageUrl?: string | null;
340
+ });
341
+ }
342
+ declare class InvalidInputError extends SynapPermanentError {
343
+ static readonly errorCode: SynapErrorCode;
344
+ }
345
+ /**
346
+ * Raised by `validateInstanceId` before any network call, matching Python's
347
+ * `utils/validators.py`. The message text is identical in both SDKs.
348
+ */
349
+ declare class InvalidInstanceIdError extends InvalidInputError {
350
+ static readonly errorCode: SynapErrorCode;
351
+ }
352
+ /** See the note on InvalidInstanceIdError. Raised by `validateConversationId`. */
353
+ declare class InvalidConversationIdError extends InvalidInputError {
354
+ static readonly errorCode: SynapErrorCode;
355
+ }
356
+ declare class AuthenticationError extends SynapPermanentError {
357
+ static readonly errorCode: SynapErrorCode;
358
+ }
359
+ declare class ContextNotFoundError extends SynapPermanentError {
360
+ static readonly errorCode: SynapErrorCode;
361
+ }
362
+ declare class ConflictError extends SynapPermanentError {
363
+ static readonly errorCode: SynapErrorCode;
364
+ }
365
+ declare class TranscriptConflictError extends ConflictError {
366
+ static readonly errorCode: SynapErrorCode;
367
+ }
368
+ declare class SessionExpiredError extends SynapPermanentError {
369
+ static readonly errorCode: SynapErrorCode;
370
+ }
371
+ declare class ListeningAlreadyActiveError extends SynapPermanentError {
372
+ static readonly errorCode: SynapErrorCode;
373
+ }
374
+ declare class ListeningNotActiveError extends SynapPermanentError {
375
+ static readonly errorCode: SynapErrorCode;
376
+ }
377
+ declare const SDKError: typeof SynapError;
378
+ declare const TransientError: typeof SynapTransientError;
379
+ declare const PermanentError: typeof SynapPermanentError;
380
+ declare const ConnectionError: typeof NetworkTimeoutError;
381
+
382
+ export { AnticipationCache as A, type BundleItem as B, ConflictError as C, isSynapError as D, type ExitReason as E, InsufficientCreditsError as I, ListeningAlreadyActiveError as L, NetworkTimeoutError as N, PermanentError as P, RateLimitError as R, SynapError as S, TranscriptConflictError as T, type AnticipationCacheSnapshot as a, ANY_SCOPE as b, AgentUnavailableError as c, type AnticipationCacheOptions as d, AuthenticationError as e, ConnectionError as f, type ContextBundle as g, ContextNotFoundError as h, InvalidConversationIdError as i, InvalidInputError as j, InvalidInstanceIdError as k, ListeningNotActiveError as l, type LookupParams as m, type LookupResult as n, type LookupTelemetry as o, SDKError as p, ServiceUnavailableError as q, SessionExpiredError as r, type SnapshotBundle as s, type SnapshotItemPreview as t, type SnapshotItemRecord as u, type SynapErrorCode as v, type SynapErrorOptions as w, SynapPermanentError as x, SynapTransientError as y, TransientError as z };
@@ -1,10 +1,10 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunk2TVL5IPQcjs = require('../chunk-2TVL5IPQ.cjs');
4
3
 
4
+ var _chunkIA2N5NUYcjs = require('../chunk-IA2N5NUY.cjs');
5
5
 
6
6
 
7
- var _chunk7CWBRHUOcjs = require('../chunk-7CWBRHUO.cjs');
7
+ var _chunkT372OL3Qcjs = require('../chunk-T372OL3Q.cjs');
8
8
 
9
9
  // src/grpc/index.ts
10
10
  async function checkGrpcAvailability() {
@@ -32,20 +32,20 @@ async function checkGrpcAvailability() {
32
32
  }
33
33
  async function listen(options) {
34
34
  if (!options.conversationId) {
35
- throw new (0, _chunk2TVL5IPQcjs.ListeningNotActiveError)("conversation_id is required to start a listening stream");
35
+ throw new (0, _chunkT372OL3Qcjs.ListeningNotActiveError)("conversation_id is required to start a listening stream");
36
36
  }
37
37
  const availability = await checkGrpcAvailability();
38
38
  if (!availability.available) {
39
- throw new (0, _chunk2TVL5IPQcjs.ListeningNotActiveError)(availability.detail);
39
+ throw new (0, _chunkT372OL3Qcjs.ListeningNotActiveError)(availability.detail);
40
40
  }
41
41
  const [{ GrpcStreamClient: StreamClient }, { AnticipationCache }, { getEnv }] = await Promise.all([
42
- Promise.resolve().then(() => _interopRequireWildcard(require("../stream-client-GPU37E6F.cjs"))),
42
+ Promise.resolve().then(() => _interopRequireWildcard(require("../stream-client-Y5JGSF2R.cjs"))),
43
43
  Promise.resolve().then(() => _interopRequireWildcard(require("../anticipation-cache-LRE3TLK4.cjs"))),
44
44
  Promise.resolve().then(() => _interopRequireWildcard(require("../env-55ULHNJZ.cjs")))
45
45
  ]);
46
46
  const apiKey = _nullishCoalesce(_nullishCoalesce(options.apiKey, () => ( getEnv("SYNAP_API_KEY"))), () => ( ""));
47
47
  if (apiKey === "") {
48
- throw new (0, _chunk2TVL5IPQcjs.ListeningNotActiveError)(
48
+ throw new (0, _chunkT372OL3Qcjs.ListeningNotActiveError)(
49
49
  "No Synap API key found. Set SYNAP_API_KEY or pass apiKey."
50
50
  );
51
51
  }
@@ -83,5 +83,5 @@ async function listen(options) {
83
83
 
84
84
 
85
85
 
86
- exports.GrpcStreamClient = _chunk7CWBRHUOcjs.GrpcStreamClient; exports.PROTO_SHA = _chunk7CWBRHUOcjs.PROTO_SHA; exports.checkGrpcAvailability = checkGrpcAvailability; exports.listen = listen;
86
+ exports.GrpcStreamClient = _chunkIA2N5NUYcjs.GrpcStreamClient; exports.PROTO_SHA = _chunkIA2N5NUYcjs.PROTO_SHA; exports.checkGrpcAvailability = checkGrpcAvailability; exports.listen = listen;
87
87
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/private/tmp/ndeep-main/synap/sdk/js/dist/grpc/index.cjs","../../src/grpc/index.ts"],"names":[],"mappings":"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","file":"/private/tmp/ndeep-main/synap/sdk/js/dist/grpc/index.cjs","sourcesContent":[null,"/**\n * gRPC anticipation stream: availability and loading.\n *\n * This is a SUBPATH export (`@maximem/synap-js-sdk/grpc`) and is never\n * imported by the main entry point. Three reasons, all of which have bitten\n * someone already:\n *\n * 1. `@grpc/grpc-js` plus its protos is roughly 5MB. HTTP-only and Edge users\n * should not pay for it (gotcha G-Q).\n * 2. grpc-js is built on `node:http2`, `node:net` and `node:tls`. Merely\n * importing it on Edge or Workers fails the *build*, not the call. Keeping\n * it behind a subpath plus a lazy `import()` means importing the SDK on\n * Edge stays safe.\n * 3. It is an `optionalDependency`, so it may legitimately be absent.\n *\n * ## Proto loading\n *\n * The stream is live. The proto ships as an INLINED JSON descriptor\n * (./descriptor.ts) loaded through `proto-loader.fromJSON`, not through\n * `loadSync(path)`. proto-loader's file-based path reads the `.proto` off disk\n * at runtime, which breaks under webpack/turbopack bundling and Vercel's `nft`\n * file tracing: at deploy time rather than at test time (gotcha G-P). Inlining\n * gets the same result as ts-proto codegen without a codegen toolchain.\n */\n\nimport { ListeningNotActiveError } from '../errors.js';\nexport { GrpcStreamClient } from './stream-client.js';\nexport type {\n StreamState, StreamCredentials, StreamClientOptions, ConversationEventInput,\n} from './stream-client.js';\nexport { PROTO_SHA } from './descriptor.js';\n\nexport type GrpcAvailability =\n | { available: true }\n | { available: false; reason: 'not-node' | 'module-missing'; detail: string };\n\n/**\n * Whether a gRPC stream could run here.\n *\n * Deliberately does not throw: callers use this to decide whether to offer the\n * stream at all, and a hard failure at that point is unhelpful.\n */\nexport async function checkGrpcAvailability(): Promise<GrpcAvailability> {\n const isNode =\n typeof process !== 'undefined' &&\n process.versions?.node !== undefined &&\n typeof (globalThis as { EdgeRuntime?: unknown }).EdgeRuntime === 'undefined';\n\n if (!isNode) {\n return {\n available: false,\n reason: 'not-node',\n detail:\n 'gRPC needs raw TCP and node:http2, which Edge runtimes and Workers do ' +\n 'not provide. HTTP context fetching works normally here; only the ' +\n 'anticipation stream is unavailable. Reaching Edge would require a ' +\n 'gRPC-Web or Connect endpoint on the server, which does not exist yet.',\n };\n }\n\n try {\n await import(/* @vite-ignore */ '@grpc/grpc-js');\n return { available: true };\n } catch (error) {\n return {\n available: false,\n reason: 'module-missing',\n detail:\n '@grpc/grpc-js is an optional dependency and is not installed. Run ' +\n '`npm install @grpc/grpc-js` to enable the anticipation stream. ' +\n `(${(error as Error)?.message ?? String(error)})`,\n };\n }\n}\n\nexport interface ListenOptions {\n conversationId: string;\n userId?: string;\n customerId?: string;\n onBundle?: (bundle: unknown) => void;\n}\n\nexport interface ListeningSession {\n stop(): Promise<void>;\n readonly active: boolean;\n}\n\n/**\n * Start the anticipation stream directly, without a SynapClient.\n *\n * Most callers want `client.instance.listen()` instead, which shares the\n * client's credentials and anticipation cache. This exists for the case where\n * only the stream is wanted.\n *\n * **Opt-in, and off by default.** The removed wrapper called `listen()`\n * unconditionally during init, so every user had a stream whether they asked\n * for one or not. Making it opt-in is the right default, but it is not a\n * neutral change: fewer streams means fewer anticipation hits and therefore\n * MORE billed cloud fetches for existing users. That has to be announced\n * rather than discovered (gotcha G-Y).\n */\nexport async function listen(\n options: ListenOptions & {\n apiKey?: string;\n clientId?: string;\n instanceId?: string;\n host?: string;\n port?: number;\n useTls?: boolean;\n },\n): Promise<ListeningSession> {\n if (!options.conversationId) {\n // conversation_id is not a scope tier, but the stream genuinely requires\n // one: it is what the server keys the anticipation session on.\n throw new ListeningNotActiveError('conversation_id is required to start a listening stream');\n }\n\n const availability = await checkGrpcAvailability();\n if (!availability.available) {\n throw new ListeningNotActiveError(availability.detail);\n }\n\n const [{ GrpcStreamClient: StreamClient }, { AnticipationCache }, { getEnv }] = await Promise.all([\n import('./stream-client.js'),\n import('../context/anticipation-cache.js'),\n import('../util/env.js'),\n ]);\n\n const apiKey = options.apiKey ?? getEnv('SYNAP_API_KEY') ?? '';\n if (apiKey === '') {\n throw new ListeningNotActiveError(\n 'No Synap API key found. Set SYNAP_API_KEY or pass apiKey.',\n );\n }\n\n const cache = new AnticipationCache();\n const client = new StreamClient(\n {\n apiKey,\n clientId: options.clientId ?? getEnv('SYNAP_CLIENT_ID') ?? '',\n instanceId: options.instanceId ?? getEnv('SYNAP_INSTANCE_ID') ?? '',\n },\n cache,\n {\n ...(options.host !== undefined ? { host: options.host } : {}),\n ...(options.port !== undefined ? { port: options.port } : {}),\n ...(options.useTls !== undefined ? { useTls: options.useTls } : {}),\n ...(options.onBundle !== undefined ? { onContext: options.onBundle } : {}),\n },\n );\n await client.connect();\n client.sendSessionControl({\n action: 'start',\n conversation_id: options.conversationId,\n ...(options.userId !== undefined ? { user_id: options.userId } : {}),\n ...(options.customerId !== undefined ? { customer_id: options.customerId } : {}),\n });\n\n return {\n stop: () => client.disconnect(),\n get active() { return client.isConnected; },\n };\n}\n"]}
1
+ {"version":3,"sources":["/Users/scaler/maximem_synap/synap/sdk/js/dist/grpc/index.cjs","../../src/grpc/index.ts"],"names":[],"mappings":"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","file":"/Users/scaler/maximem_synap/synap/sdk/js/dist/grpc/index.cjs","sourcesContent":[null,"/**\n * gRPC anticipation stream: availability and loading.\n *\n * This is a SUBPATH export (`@maximem/synap-js-sdk/grpc`) and is never\n * imported by the main entry point. Three reasons, all of which have bitten\n * someone already:\n *\n * 1. `@grpc/grpc-js` plus its protos is roughly 5MB. HTTP-only and Edge users\n * should not pay for it (gotcha G-Q).\n * 2. grpc-js is built on `node:http2`, `node:net` and `node:tls`. Merely\n * importing it on Edge or Workers fails the *build*, not the call. Keeping\n * it behind a subpath plus a lazy `import()` means importing the SDK on\n * Edge stays safe.\n * 3. It is an `optionalDependency`, so it may legitimately be absent.\n *\n * ## Proto loading\n *\n * The stream is live. The proto ships as an INLINED JSON descriptor\n * (./descriptor.ts) loaded through `proto-loader.fromJSON`, not through\n * `loadSync(path)`. proto-loader's file-based path reads the `.proto` off disk\n * at runtime, which breaks under webpack/turbopack bundling and Vercel's `nft`\n * file tracing: at deploy time rather than at test time (gotcha G-P). Inlining\n * gets the same result as ts-proto codegen without a codegen toolchain.\n */\n\nimport { ListeningNotActiveError } from '../errors.js';\nexport { GrpcStreamClient } from './stream-client.js';\nexport type {\n StreamState, StreamCredentials, StreamClientOptions, ConversationEventInput,\n} from './stream-client.js';\nexport { PROTO_SHA } from './descriptor.js';\n\nexport type GrpcAvailability =\n | { available: true }\n | { available: false; reason: 'not-node' | 'module-missing'; detail: string };\n\n/**\n * Whether a gRPC stream could run here.\n *\n * Deliberately does not throw: callers use this to decide whether to offer the\n * stream at all, and a hard failure at that point is unhelpful.\n */\nexport async function checkGrpcAvailability(): Promise<GrpcAvailability> {\n const isNode =\n typeof process !== 'undefined' &&\n process.versions?.node !== undefined &&\n typeof (globalThis as { EdgeRuntime?: unknown }).EdgeRuntime === 'undefined';\n\n if (!isNode) {\n return {\n available: false,\n reason: 'not-node',\n detail:\n 'gRPC needs raw TCP and node:http2, which Edge runtimes and Workers do ' +\n 'not provide. HTTP context fetching works normally here; only the ' +\n 'anticipation stream is unavailable. Reaching Edge would require a ' +\n 'gRPC-Web or Connect endpoint on the server, which does not exist yet.',\n };\n }\n\n try {\n await import(/* @vite-ignore */ '@grpc/grpc-js');\n return { available: true };\n } catch (error) {\n return {\n available: false,\n reason: 'module-missing',\n detail:\n '@grpc/grpc-js is an optional dependency and is not installed. Run ' +\n '`npm install @grpc/grpc-js` to enable the anticipation stream. ' +\n `(${(error as Error)?.message ?? String(error)})`,\n };\n }\n}\n\nexport interface ListenOptions {\n conversationId: string;\n userId?: string;\n customerId?: string;\n onBundle?: (bundle: unknown) => void;\n}\n\nexport interface ListeningSession {\n stop(): Promise<void>;\n readonly active: boolean;\n}\n\n/**\n * Start the anticipation stream directly, without a SynapClient.\n *\n * Most callers want `client.instance.listen()` instead, which shares the\n * client's credentials and anticipation cache. This exists for the case where\n * only the stream is wanted.\n *\n * **Opt-in, and off by default.** The removed wrapper called `listen()`\n * unconditionally during init, so every user had a stream whether they asked\n * for one or not. Making it opt-in is the right default, but it is not a\n * neutral change: fewer streams means fewer anticipation hits and therefore\n * MORE billed cloud fetches for existing users. That has to be announced\n * rather than discovered (gotcha G-Y).\n */\nexport async function listen(\n options: ListenOptions & {\n apiKey?: string;\n clientId?: string;\n instanceId?: string;\n host?: string;\n port?: number;\n useTls?: boolean;\n },\n): Promise<ListeningSession> {\n if (!options.conversationId) {\n // conversation_id is not a scope tier, but the stream genuinely requires\n // one: it is what the server keys the anticipation session on.\n throw new ListeningNotActiveError('conversation_id is required to start a listening stream');\n }\n\n const availability = await checkGrpcAvailability();\n if (!availability.available) {\n throw new ListeningNotActiveError(availability.detail);\n }\n\n const [{ GrpcStreamClient: StreamClient }, { AnticipationCache }, { getEnv }] = await Promise.all([\n import('./stream-client.js'),\n import('../context/anticipation-cache.js'),\n import('../util/env.js'),\n ]);\n\n const apiKey = options.apiKey ?? getEnv('SYNAP_API_KEY') ?? '';\n if (apiKey === '') {\n throw new ListeningNotActiveError(\n 'No Synap API key found. Set SYNAP_API_KEY or pass apiKey.',\n );\n }\n\n const cache = new AnticipationCache();\n const client = new StreamClient(\n {\n apiKey,\n clientId: options.clientId ?? getEnv('SYNAP_CLIENT_ID') ?? '',\n instanceId: options.instanceId ?? getEnv('SYNAP_INSTANCE_ID') ?? '',\n },\n cache,\n {\n ...(options.host !== undefined ? { host: options.host } : {}),\n ...(options.port !== undefined ? { port: options.port } : {}),\n ...(options.useTls !== undefined ? { useTls: options.useTls } : {}),\n ...(options.onBundle !== undefined ? { onContext: options.onBundle } : {}),\n },\n );\n await client.connect();\n client.sendSessionControl({\n action: 'start',\n conversation_id: options.conversationId,\n ...(options.userId !== undefined ? { user_id: options.userId } : {}),\n ...(options.customerId !== undefined ? { customer_id: options.customerId } : {}),\n });\n\n return {\n stop: () => client.disconnect(),\n get active() { return client.isConnected; },\n };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { A as AnticipationCache } from '../anticipation-cache-C1rUEpaW.cjs';
1
+ import { A as AnticipationCache, S as SynapError } from '../errors-Bl3vY1eb.cjs';
2
2
 
3
3
  /**
4
4
  * Bidirectional gRPC stream client for the Synap anticipation channel.
@@ -51,8 +51,24 @@ interface ConversationEventInput {
51
51
  tool_name?: string;
52
52
  /** proto: ConversationEvent.tool_args_json. Python sends json.dumps(tool_args). */
53
53
  tool_args_json?: string;
54
+ /**
55
+ * proto: ConversationEvent.tool_result_json. A result is not a message, so
56
+ * it does not ride in `content`; a string is sent as itself rather than as a
57
+ * quoted JSON string, matching Python.
58
+ */
59
+ tool_result_json?: string;
60
+ /** proto: ConversationEvent.tool_call_id. Ties a result to its call. */
61
+ tool_call_id?: string;
54
62
  search_queries?: string[];
55
63
  context_types?: string[];
64
+ /**
65
+ * proto: ConversationEvent.event_id. Minted here when absent, and the SAME
66
+ * id travels on every replay — that is what the server's dedupe matches, so
67
+ * a retry cannot double a turn.
68
+ */
69
+ event_id?: string;
70
+ /** proto: ConversationEvent.sent_at_ms. When we FIRST tried, not when it happened. */
71
+ sent_at_ms?: number;
56
72
  }
57
73
  declare class GrpcStreamClient {
58
74
  #private;
@@ -62,9 +78,33 @@ declare class GrpcStreamClient {
62
78
  constructor(credentials: StreamCredentials, cache: AnticipationCache, options?: StreamClientOptions);
63
79
  get currentState(): StreamState;
64
80
  get isConnected(): boolean;
81
+ /**
82
+ * The error that ended the stream, if one did.
83
+ *
84
+ * Set when the server refused the stream for credits, so an `onDisconnect`
85
+ * handler can read the balance, the reason and the URL to fix it. Null for an
86
+ * ordinary disconnect.
87
+ */
88
+ get lastError(): SynapError | null;
65
89
  connect(): Promise<void>;
66
90
  disconnect(): Promise<void>;
67
- /** Fire-and-forget: a dropped event must never fail the caller's turn. */
91
+ /**
92
+ * What this client could not get to the server.
93
+ *
94
+ * Every send path returns void, so a caller had no way to tell a delivered
95
+ * turn from a lost one. Mirrors Python's `undelivered()`.
96
+ */
97
+ undelivered(): {
98
+ queued: number;
99
+ unacknowledged: number;
100
+ };
101
+ /**
102
+ * Never fails the caller's turn, and no longer loses it either.
103
+ *
104
+ * A write that cannot go out is buffered and replayed on reconnect, and a
105
+ * write that does go out is held until the server acknowledges it. Before
106
+ * this, both cases ended at a silent `return`.
107
+ */
68
108
  sendConversationEvent(event: ConversationEventInput): void;
69
109
  sendSessionControl(input: {
70
110
  action: string;
@@ -82,14 +122,14 @@ declare class GrpcStreamClient {
82
122
  *
83
123
  * Source: synap/proto/synap_service.proto
84
124
  * Generator: synap/sdk/scripts/gen_proto_descriptor.mjs
85
- * Proto SHA: a58c9b031b41530d
125
+ * Proto SHA: c1d834043608ad4f
86
126
  *
87
127
  * The proto is inlined as a JSON descriptor rather than loaded from disk so
88
128
  * that bundlers and Vercel file tracing cannot lose it. Regenerate with
89
129
  * `npm run sync-behavior` in synap/sdk/js.
90
130
  */
91
131
  /** sha256 (first 16 hex chars) of the proto this descriptor was built from. */
92
- declare const PROTO_SHA = "a58c9b031b41530d";
132
+ declare const PROTO_SHA = "c1d834043608ad4f";
93
133
 
94
134
  /**
95
135
  * gRPC anticipation stream: availability and loading.
@@ -1,4 +1,4 @@
1
- import { A as AnticipationCache } from '../anticipation-cache-C1rUEpaW.js';
1
+ import { A as AnticipationCache, S as SynapError } from '../errors-Bl3vY1eb.js';
2
2
 
3
3
  /**
4
4
  * Bidirectional gRPC stream client for the Synap anticipation channel.
@@ -51,8 +51,24 @@ interface ConversationEventInput {
51
51
  tool_name?: string;
52
52
  /** proto: ConversationEvent.tool_args_json. Python sends json.dumps(tool_args). */
53
53
  tool_args_json?: string;
54
+ /**
55
+ * proto: ConversationEvent.tool_result_json. A result is not a message, so
56
+ * it does not ride in `content`; a string is sent as itself rather than as a
57
+ * quoted JSON string, matching Python.
58
+ */
59
+ tool_result_json?: string;
60
+ /** proto: ConversationEvent.tool_call_id. Ties a result to its call. */
61
+ tool_call_id?: string;
54
62
  search_queries?: string[];
55
63
  context_types?: string[];
64
+ /**
65
+ * proto: ConversationEvent.event_id. Minted here when absent, and the SAME
66
+ * id travels on every replay — that is what the server's dedupe matches, so
67
+ * a retry cannot double a turn.
68
+ */
69
+ event_id?: string;
70
+ /** proto: ConversationEvent.sent_at_ms. When we FIRST tried, not when it happened. */
71
+ sent_at_ms?: number;
56
72
  }
57
73
  declare class GrpcStreamClient {
58
74
  #private;
@@ -62,9 +78,33 @@ declare class GrpcStreamClient {
62
78
  constructor(credentials: StreamCredentials, cache: AnticipationCache, options?: StreamClientOptions);
63
79
  get currentState(): StreamState;
64
80
  get isConnected(): boolean;
81
+ /**
82
+ * The error that ended the stream, if one did.
83
+ *
84
+ * Set when the server refused the stream for credits, so an `onDisconnect`
85
+ * handler can read the balance, the reason and the URL to fix it. Null for an
86
+ * ordinary disconnect.
87
+ */
88
+ get lastError(): SynapError | null;
65
89
  connect(): Promise<void>;
66
90
  disconnect(): Promise<void>;
67
- /** Fire-and-forget: a dropped event must never fail the caller's turn. */
91
+ /**
92
+ * What this client could not get to the server.
93
+ *
94
+ * Every send path returns void, so a caller had no way to tell a delivered
95
+ * turn from a lost one. Mirrors Python's `undelivered()`.
96
+ */
97
+ undelivered(): {
98
+ queued: number;
99
+ unacknowledged: number;
100
+ };
101
+ /**
102
+ * Never fails the caller's turn, and no longer loses it either.
103
+ *
104
+ * A write that cannot go out is buffered and replayed on reconnect, and a
105
+ * write that does go out is held until the server acknowledges it. Before
106
+ * this, both cases ended at a silent `return`.
107
+ */
68
108
  sendConversationEvent(event: ConversationEventInput): void;
69
109
  sendSessionControl(input: {
70
110
  action: string;
@@ -82,14 +122,14 @@ declare class GrpcStreamClient {
82
122
  *
83
123
  * Source: synap/proto/synap_service.proto
84
124
  * Generator: synap/sdk/scripts/gen_proto_descriptor.mjs
85
- * Proto SHA: a58c9b031b41530d
125
+ * Proto SHA: c1d834043608ad4f
86
126
  *
87
127
  * The proto is inlined as a JSON descriptor rather than loaded from disk so
88
128
  * that bundlers and Vercel file tracing cannot lose it. Regenerate with
89
129
  * `npm run sync-behavior` in synap/sdk/js.
90
130
  */
91
131
  /** sha256 (first 16 hex chars) of the proto this descriptor was built from. */
92
- declare const PROTO_SHA = "a58c9b031b41530d";
132
+ declare const PROTO_SHA = "c1d834043608ad4f";
93
133
 
94
134
  /**
95
135
  * gRPC anticipation stream: availability and loading.
@@ -1,10 +1,10 @@
1
- import {
2
- ListeningNotActiveError
3
- } from "../chunk-OCMM2Z5P.js";
4
1
  import {
5
2
  GrpcStreamClient,
6
3
  PROTO_SHA
7
- } from "../chunk-T5QQBJ6T.js";
4
+ } from "../chunk-ASM2OIIG.js";
5
+ import {
6
+ ListeningNotActiveError
7
+ } from "../chunk-BWMPKLFG.js";
8
8
 
9
9
  // src/grpc/index.ts
10
10
  async function checkGrpcAvailability() {
@@ -39,7 +39,7 @@ async function listen(options) {
39
39
  throw new ListeningNotActiveError(availability.detail);
40
40
  }
41
41
  const [{ GrpcStreamClient: StreamClient }, { AnticipationCache }, { getEnv }] = await Promise.all([
42
- import("../stream-client-2C5KIOWP.js"),
42
+ import("../stream-client-6OWRYOA4.js"),
43
43
  import("../anticipation-cache-WLU4NMD5.js"),
44
44
  import("../env-QCUKKW36.js")
45
45
  ]);