@maximem/synap-js-sdk 0.5.9 → 0.5.11

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 (36) hide show
  1. package/dist/anticipation-cache-LPNKMRBH.cjs.map +1 -1
  2. package/dist/{chunk-BWMPKLFG.js → chunk-4GE7SORD.js} +17 -1
  3. package/dist/chunk-4GE7SORD.js.map +1 -0
  4. package/dist/chunk-ICUFS3B5.cjs.map +1 -1
  5. package/dist/{chunk-I7TWR4S6.js → chunk-LJ66WCV3.js} +2 -2
  6. package/dist/{chunk-RE4DDIWO.cjs → chunk-QHPVLTQ7.cjs} +6 -6
  7. package/dist/chunk-QHPVLTQ7.cjs.map +1 -0
  8. package/dist/{chunk-T372OL3Q.cjs → chunk-UXNVTP4D.cjs} +33 -17
  9. package/dist/chunk-UXNVTP4D.cjs.map +1 -0
  10. package/dist/chunk-ZKOJDUN4.cjs.map +1 -1
  11. package/dist/env-55ULHNJZ.cjs.map +1 -1
  12. package/dist/{errors-Bdayqlzy.d.cts → errors-BvUA5EW8.d.cts} +14 -2
  13. package/dist/{errors-Bdayqlzy.d.ts → errors-BvUA5EW8.d.ts} +14 -2
  14. package/dist/grpc/index.cjs +7 -7
  15. package/dist/grpc/index.cjs.map +1 -1
  16. package/dist/grpc/index.d.cts +1 -1
  17. package/dist/grpc/index.d.ts +1 -1
  18. package/dist/grpc/index.js +3 -3
  19. package/dist/index.cjs +200 -149
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +28 -11
  22. package/dist/index.d.ts +28 -11
  23. package/dist/index.js +76 -25
  24. package/dist/index.js.map +1 -1
  25. package/dist/onnx-encoder-4RSL6ID4.cjs.map +1 -1
  26. package/dist/stream-client-5GYUWETN.cjs +16 -0
  27. package/dist/stream-client-5GYUWETN.cjs.map +1 -0
  28. package/dist/{stream-client-3WHXGW4U.js → stream-client-ZBCKRU7S.js} +3 -3
  29. package/package.json +1 -1
  30. package/dist/chunk-BWMPKLFG.js.map +0 -1
  31. package/dist/chunk-RE4DDIWO.cjs.map +0 -1
  32. package/dist/chunk-T372OL3Q.cjs.map +0 -1
  33. package/dist/stream-client-L6OQPZN7.cjs +0 -16
  34. package/dist/stream-client-L6OQPZN7.cjs.map +0 -1
  35. /package/dist/{chunk-I7TWR4S6.js.map → chunk-LJ66WCV3.js.map} +0 -0
  36. /package/dist/{stream-client-3WHXGW4U.js.map → stream-client-ZBCKRU7S.js.map} +0 -0
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { E as Encoder, a as AnticipationCacheSnapshot } from './errors-Bdayqlzy.cjs';
2
- export { b as ANY_SCOPE, c as AgentUnavailableError, A as AnticipationCache, d as AnticipationCacheOptions, e as AuthenticationError, B as BundleItem, C as ConflictError, f as ConnectionError, g as ContextBundle, h as ContextNotFoundError, i as ExitReason, I as InsufficientCreditsError, j as InvalidConversationIdError, k as InvalidInputError, l as InvalidInstanceIdError, L as ListeningAlreadyActiveError, m as ListeningNotActiveError, n as LookupParams, o as LookupResult, p as LookupTelemetry, N as NetworkTimeoutError, P as PermanentError, R as RateLimitError, q as SDKError, r as ServiceUnavailableError, s as SessionExpiredError, t as SnapshotBundle, u as SnapshotItemPreview, v as SnapshotItemRecord, S as SynapError, w as SynapErrorCode, x as SynapErrorOptions, y as SynapPermanentError, z as SynapTransientError, T as TranscriptConflictError, D as TransientError, F as isSynapError } from './errors-Bdayqlzy.cjs';
1
+ import { E as Encoder, a as AnticipationCacheSnapshot } from './errors-BvUA5EW8.cjs';
2
+ export { b as ANY_SCOPE, c as AgentUnavailableError, A as AnticipationCache, d as AnticipationCacheOptions, e as AuthenticationError, B as BundleItem, C as ConflictError, f as ConnectionError, g as ContextBundle, h as ContextNotFoundError, i as CustomerIdNotAcceptedError, j as ExitReason, I as InsufficientCreditsError, k as InvalidConversationIdError, l as InvalidInputError, m as InvalidInstanceIdError, L as ListeningAlreadyActiveError, n as ListeningNotActiveError, o as LookupParams, p as LookupResult, q as LookupTelemetry, N as NetworkTimeoutError, P as PermanentError, R as RateLimitError, r as SDKError, s as ServiceUnavailableError, t as SessionExpiredError, u as SnapshotBundle, v as SnapshotItemPreview, w as SnapshotItemRecord, S as SynapError, x as SynapErrorCode, y as SynapErrorOptions, z as SynapPermanentError, D as SynapTransientError, T as TranscriptConflictError, F as TransientError, G as isSynapError } from './errors-BvUA5EW8.cjs';
3
3
 
4
4
  /**
5
5
  * AUTOGENERATED FROM synap/sdk/CONTRACT/endpoints.yaml -- DO NOT EDIT.
@@ -326,8 +326,21 @@ declare class HttpTransport {
326
326
  * deployment older than the field, and must mean "enforce nothing".
327
327
  */
328
328
  userContextIsolation: string | undefined;
329
- /** Refuse a customer_id on a B2C instance, at the call site. */
330
- checkCustomerId(customerId: unknown, where: string): void;
329
+ /**
330
+ * Set by the client to its identity resolution (`initialize()`'s whoami).
331
+ * Python refuses every call before `initialize()`; here the first request
332
+ * runs it instead, so a caller who skips it gets the same identity-dependent
333
+ * behaviour (client-scope cache hits, the B2C check) rather than an error.
334
+ */
335
+ identityResolver: (() => Promise<void>) | undefined;
336
+ /** Resolve identity once, before the first request. Never throws. */
337
+ ready(): Promise<void>;
338
+ /**
339
+ * Refuse a customer_id on a B2C instance, at the call site. Resolves identity
340
+ * first: the isolation comes from whoami, so on the first call of a client
341
+ * that skipped initialize() the check would otherwise see nothing and pass.
342
+ */
343
+ checkCustomerId(customerId: unknown, where: string): Promise<void>;
331
344
  setTimeouts(timeouts: Partial<TimeoutConfig>): void;
332
345
  /**
333
346
  * `null` DISABLES retries, matching Python.
@@ -1759,12 +1772,16 @@ declare class SynapClient {
1759
1772
  /**
1760
1773
  * Resolve `client_id` and `instance_id` from the API key.
1761
1774
  *
1762
- * Mirrors Python's `initialize()`. Calling it is optional here (unlike
1763
- * Python, which raises "SDK not initialized" from `_ensure_initialized`)
1764
- * because every HTTP route authenticates on the API key alone. It matters
1765
- * for the anticipation cache: a bundle shared at client scope is filtered on
1766
- * `client_id`, and an empty one fails that filter for every bundle. That was
1767
- * a silent cache-scope bug in Python before whoami bootstrapping existed.
1775
+ * Mirrors Python's `initialize()`. Python raises "SDK not initialized" on
1776
+ * any call made before it; here the first request runs the same resolution
1777
+ * instead (`identityResolver` on the transport), so skipping this call costs
1778
+ * nothing and breaks no existing caller. It matters for the anticipation
1779
+ * cache: a bundle shared at client scope is filtered on `client_id`, and an
1780
+ * empty one fails that filter for every bundle.
1781
+ *
1782
+ * Calling it explicitly still locks `configure()`, as in Python. Resolving
1783
+ * on first use does not, because a caller who never called it could always
1784
+ * configure after a request, and that keeps working.
1768
1785
  *
1769
1786
  * Best-effort and idempotent. A whoami failure leaves whatever the
1770
1787
  * constructor resolved and is not fatal.
@@ -1856,7 +1873,7 @@ declare class SynapClient {
1856
1873
  * build when the two disagree, which is the only thing that makes a manual
1857
1874
  * publish safe.
1858
1875
  */
1859
- declare const SDK_VERSION = "0.5.9";
1876
+ declare const SDK_VERSION = "0.5.11";
1860
1877
 
1861
1878
  /**
1862
1879
  * `response_shape: "ranked"`: one list in the order the server ranked it.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { E as Encoder, a as AnticipationCacheSnapshot } from './errors-Bdayqlzy.js';
2
- export { b as ANY_SCOPE, c as AgentUnavailableError, A as AnticipationCache, d as AnticipationCacheOptions, e as AuthenticationError, B as BundleItem, C as ConflictError, f as ConnectionError, g as ContextBundle, h as ContextNotFoundError, i as ExitReason, I as InsufficientCreditsError, j as InvalidConversationIdError, k as InvalidInputError, l as InvalidInstanceIdError, L as ListeningAlreadyActiveError, m as ListeningNotActiveError, n as LookupParams, o as LookupResult, p as LookupTelemetry, N as NetworkTimeoutError, P as PermanentError, R as RateLimitError, q as SDKError, r as ServiceUnavailableError, s as SessionExpiredError, t as SnapshotBundle, u as SnapshotItemPreview, v as SnapshotItemRecord, S as SynapError, w as SynapErrorCode, x as SynapErrorOptions, y as SynapPermanentError, z as SynapTransientError, T as TranscriptConflictError, D as TransientError, F as isSynapError } from './errors-Bdayqlzy.js';
1
+ import { E as Encoder, a as AnticipationCacheSnapshot } from './errors-BvUA5EW8.js';
2
+ export { b as ANY_SCOPE, c as AgentUnavailableError, A as AnticipationCache, d as AnticipationCacheOptions, e as AuthenticationError, B as BundleItem, C as ConflictError, f as ConnectionError, g as ContextBundle, h as ContextNotFoundError, i as CustomerIdNotAcceptedError, j as ExitReason, I as InsufficientCreditsError, k as InvalidConversationIdError, l as InvalidInputError, m as InvalidInstanceIdError, L as ListeningAlreadyActiveError, n as ListeningNotActiveError, o as LookupParams, p as LookupResult, q as LookupTelemetry, N as NetworkTimeoutError, P as PermanentError, R as RateLimitError, r as SDKError, s as ServiceUnavailableError, t as SessionExpiredError, u as SnapshotBundle, v as SnapshotItemPreview, w as SnapshotItemRecord, S as SynapError, x as SynapErrorCode, y as SynapErrorOptions, z as SynapPermanentError, D as SynapTransientError, T as TranscriptConflictError, F as TransientError, G as isSynapError } from './errors-BvUA5EW8.js';
3
3
 
4
4
  /**
5
5
  * AUTOGENERATED FROM synap/sdk/CONTRACT/endpoints.yaml -- DO NOT EDIT.
@@ -326,8 +326,21 @@ declare class HttpTransport {
326
326
  * deployment older than the field, and must mean "enforce nothing".
327
327
  */
328
328
  userContextIsolation: string | undefined;
329
- /** Refuse a customer_id on a B2C instance, at the call site. */
330
- checkCustomerId(customerId: unknown, where: string): void;
329
+ /**
330
+ * Set by the client to its identity resolution (`initialize()`'s whoami).
331
+ * Python refuses every call before `initialize()`; here the first request
332
+ * runs it instead, so a caller who skips it gets the same identity-dependent
333
+ * behaviour (client-scope cache hits, the B2C check) rather than an error.
334
+ */
335
+ identityResolver: (() => Promise<void>) | undefined;
336
+ /** Resolve identity once, before the first request. Never throws. */
337
+ ready(): Promise<void>;
338
+ /**
339
+ * Refuse a customer_id on a B2C instance, at the call site. Resolves identity
340
+ * first: the isolation comes from whoami, so on the first call of a client
341
+ * that skipped initialize() the check would otherwise see nothing and pass.
342
+ */
343
+ checkCustomerId(customerId: unknown, where: string): Promise<void>;
331
344
  setTimeouts(timeouts: Partial<TimeoutConfig>): void;
332
345
  /**
333
346
  * `null` DISABLES retries, matching Python.
@@ -1759,12 +1772,16 @@ declare class SynapClient {
1759
1772
  /**
1760
1773
  * Resolve `client_id` and `instance_id` from the API key.
1761
1774
  *
1762
- * Mirrors Python's `initialize()`. Calling it is optional here (unlike
1763
- * Python, which raises "SDK not initialized" from `_ensure_initialized`)
1764
- * because every HTTP route authenticates on the API key alone. It matters
1765
- * for the anticipation cache: a bundle shared at client scope is filtered on
1766
- * `client_id`, and an empty one fails that filter for every bundle. That was
1767
- * a silent cache-scope bug in Python before whoami bootstrapping existed.
1775
+ * Mirrors Python's `initialize()`. Python raises "SDK not initialized" on
1776
+ * any call made before it; here the first request runs the same resolution
1777
+ * instead (`identityResolver` on the transport), so skipping this call costs
1778
+ * nothing and breaks no existing caller. It matters for the anticipation
1779
+ * cache: a bundle shared at client scope is filtered on `client_id`, and an
1780
+ * empty one fails that filter for every bundle.
1781
+ *
1782
+ * Calling it explicitly still locks `configure()`, as in Python. Resolving
1783
+ * on first use does not, because a caller who never called it could always
1784
+ * configure after a request, and that keeps working.
1768
1785
  *
1769
1786
  * Best-effort and idempotent. A whoami failure leaves whatever the
1770
1787
  * constructor resolved and is not fatal.
@@ -1856,7 +1873,7 @@ declare class SynapClient {
1856
1873
  * build when the two disagree, which is the only thing that makes a manual
1857
1874
  * publish safe.
1858
1875
  */
1859
- declare const SDK_VERSION = "0.5.9";
1876
+ declare const SDK_VERSION = "0.5.11";
1860
1877
 
1861
1878
  /**
1862
1879
  * `response_shape: "ranked"`: one list in the order the server ranked it.
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  ConflictError,
21
21
  ConnectionError,
22
22
  ContextNotFoundError,
23
+ CustomerIdNotAcceptedError,
23
24
  InsufficientCreditsError,
24
25
  InvalidConversationIdError,
25
26
  InvalidInputError,
@@ -39,7 +40,7 @@ import {
39
40
  TransientError,
40
41
  isSynapError,
41
42
  newCorrelationId
42
- } from "./chunk-BWMPKLFG.js";
43
+ } from "./chunk-4GE7SORD.js";
43
44
 
44
45
  // src/transport/endpoints.ts
45
46
  var ENDPOINTS = {
@@ -194,9 +195,7 @@ var B2C_ISOLATION = "equals_customer";
194
195
  function checkCustomerId(isolation, customerId, where, instanceId) {
195
196
  if (customerId === void 0 || customerId === null || customerId === "") return;
196
197
  if (isolation !== B2C_ISOLATION) return;
197
- throw new InvalidInputError(
198
- `${where}: customer_id is not accepted on this instance${instanceId ? ` (${instanceId})` : ""}, which is B2C (user_context_isolation='${B2C_ISOLATION}'). Send user_id only; it is the whole identity, and the server files and reads your data under it. Received customer_id=${JSON.stringify(customerId)}.`
199
- );
198
+ throw new CustomerIdNotAcceptedError(where, String(customerId), instanceId);
200
199
  }
201
200
 
202
201
  // src/transport/http.ts
@@ -267,8 +266,24 @@ var HttpTransport = class {
267
266
  * deployment older than the field, and must mean "enforce nothing".
268
267
  */
269
268
  userContextIsolation = void 0;
270
- /** Refuse a customer_id on a B2C instance, at the call site. */
271
- checkCustomerId(customerId, where) {
269
+ /**
270
+ * Set by the client to its identity resolution (`initialize()`'s whoami).
271
+ * Python refuses every call before `initialize()`; here the first request
272
+ * runs it instead, so a caller who skips it gets the same identity-dependent
273
+ * behaviour (client-scope cache hits, the B2C check) rather than an error.
274
+ */
275
+ identityResolver = void 0;
276
+ /** Resolve identity once, before the first request. Never throws. */
277
+ async ready() {
278
+ if (this.identityResolver !== void 0) await this.identityResolver();
279
+ }
280
+ /**
281
+ * Refuse a customer_id on a B2C instance, at the call site. Resolves identity
282
+ * first: the isolation comes from whoami, so on the first call of a client
283
+ * that skipped initialize() the check would otherwise see nothing and pass.
284
+ */
285
+ async checkCustomerId(customerId, where) {
286
+ await this.ready();
272
287
  checkCustomerId(
273
288
  this.userContextIsolation,
274
289
  customerId,
@@ -298,6 +313,7 @@ var HttpTransport = class {
298
313
  const spec = ENDPOINTS[name];
299
314
  const correlationId = options.correlationId ?? newCorrelationId();
300
315
  const idempotent = options.idempotent ?? spec.idempotent;
316
+ if (name !== "whoami") await this.ready();
301
317
  let attempt = 0;
302
318
  let last = null;
303
319
  while (attempt < this.retryPolicy.maxAttempts) {
@@ -619,11 +635,10 @@ function buildFetchRequest(scope, options) {
619
635
  const conversationId = opt(options, "conversation_id", "conversationId");
620
636
  const searchQuery = opt(options, "search_query", "searchQuery");
621
637
  const maxResults = opt(options, "max_results", "maxResults") ?? 10;
622
- const precisionLevel = opt(options, "precision_level", "precisionLevel") ?? "high";
623
638
  const queryDate = opt(options, "query_date", "queryDate");
624
639
  const perQuery = opt(options, "max_results_per_query", "maxResultsPerQuery");
625
640
  const responseShape = responseShapeOf(options);
626
- const mode = options.mode ?? "fast";
641
+ const { mode, precisionLevel } = retrievalOptionsOf(options);
627
642
  if (searchQuery !== void 0 && !Array.isArray(searchQuery)) {
628
643
  throw new InvalidInputError("search_query must be an array when provided");
629
644
  }
@@ -644,13 +659,15 @@ function buildFetchRequest(scope, options) {
644
659
  if (options.include_conversation_context === false) {
645
660
  body["include_conversation_context"] = false;
646
661
  }
662
+ const scopePath = opt(options, "scope_path", "scopePath");
663
+ if (scopePath !== void 0 && scopePath !== null && Object.keys(scopePath).length > 0) {
664
+ body["scope"] = scopePath;
665
+ }
647
666
  switch (scope) {
648
667
  case "user":
649
668
  {
650
669
  if (!userId) throw new InvalidInputError("user_id is required");
651
670
  body["user_id"] = userId;
652
- const scopePath = opt(options, "scope_path", "scopePath");
653
- if (scopePath !== void 0) body["scope"] = scopePath;
654
671
  const contextMode = opt(options, "context_mode", "contextMode") ?? "in-conversation";
655
672
  if (!VALID_CONTEXT_MODES.includes(contextMode)) {
656
673
  throw new InvalidInputError(
@@ -697,13 +714,32 @@ function responseShapeOf(options) {
697
714
  }
698
715
  return shape;
699
716
  }
717
+ var VALID_MODES = ["fast", "accurate"];
718
+ var VALID_PRECISION = ["high", "medium"];
719
+ function retrievalOptionsOf(options) {
720
+ const mode = options.mode ?? "fast";
721
+ if (!VALID_MODES.includes(mode)) {
722
+ throw new InvalidInputError(
723
+ `Invalid mode '${mode}'. Must be one of: ${VALID_MODES.join(", ")}`
724
+ );
725
+ }
726
+ const precisionLevel = opt(options, "precision_level", "precisionLevel") ?? "high";
727
+ if (!VALID_PRECISION.includes(precisionLevel)) {
728
+ throw new InvalidInputError(
729
+ `Invalid precision_level '${precisionLevel}'. Must be one of: ${VALID_PRECISION.join(", ")}`
730
+ );
731
+ }
732
+ return { mode, precisionLevel };
733
+ }
700
734
  async function fetchContext(transport, scope, options = {}, hooks = {}) {
701
735
  const startedAt = Date.now();
702
- transport.checkCustomerId(
736
+ await transport.ready?.();
737
+ await transport.checkCustomerId(
703
738
  opt(options, "customer_id", "customerId"),
704
739
  `${scope}.context.fetch`
705
740
  );
706
741
  const responseShape = responseShapeOf(options);
742
+ retrievalOptionsOf(options);
707
743
  const network = async () => {
708
744
  const { endpoint, body } = buildFetchRequest(scope, options);
709
745
  const result = await transport.request(endpoint, { body });
@@ -1046,7 +1082,7 @@ function createMemoriesInterface(transport, onForgotten) {
1046
1082
  return {
1047
1083
  async create(options) {
1048
1084
  const o2 = options;
1049
- transport.checkCustomerId(o2["customer_id"] ?? o2["customerId"], "memories.create");
1085
+ await transport.checkCustomerId(o2["customer_id"] ?? o2["customerId"], "memories.create");
1050
1086
  return transport.request("memories_create", { body: buildCreateBody(options) });
1051
1087
  },
1052
1088
  async batch_create(options) {
@@ -1063,7 +1099,7 @@ function createMemoriesInterface(transport, onForgotten) {
1063
1099
  const userId = options.user_id ?? options.userId;
1064
1100
  const customerId = options.customer_id ?? options.customerId;
1065
1101
  if (!userId) throw new InvalidInputError("user_id is required");
1066
- transport.checkCustomerId(customerId, "memories.create_from_file");
1102
+ await transport.checkCustomerId(customerId, "memories.create_from_file");
1067
1103
  const filePath = options.file_path ?? options.filePath;
1068
1104
  const sources = [filePath, options.file, options.text].filter((v) => v !== void 0);
1069
1105
  if (sources.length === 0) {
@@ -1130,7 +1166,7 @@ function createMemoriesInterface(transport, onForgotten) {
1130
1166
  },
1131
1167
  async forget(options) {
1132
1168
  const o2 = options;
1133
- transport.checkCustomerId(o2["customer_id"] ?? o2["customerId"], "memories.forget");
1169
+ await transport.checkCustomerId(o2["customer_id"] ?? o2["customerId"], "memories.forget");
1134
1170
  const body = buildForgetBody(options);
1135
1171
  const result = await transport.request("memories_forget", { body });
1136
1172
  const removed = !!result?.deleted_ids?.length || result?.status === "deleted" || result?.status === "partial";
@@ -1401,7 +1437,7 @@ function createConversationNamespace(transport, subscribers = new CompactionSubs
1401
1437
  };
1402
1438
  return {
1403
1439
  async record_message(options) {
1404
- transport.checkCustomerId(
1440
+ await transport.checkCustomerId(
1405
1441
  options["customer_id"] ?? options["customerId"],
1406
1442
  "conversation.record_message"
1407
1443
  );
@@ -1433,7 +1469,7 @@ function createConversationNamespace(transport, subscribers = new CompactionSubs
1433
1469
  }
1434
1470
  for (const m of messages) {
1435
1471
  const mo = m;
1436
- transport.checkCustomerId(
1472
+ await transport.checkCustomerId(
1437
1473
  mo["customer_id"] ?? mo["customerId"],
1438
1474
  "conversation.record_messages_batch"
1439
1475
  );
@@ -1743,7 +1779,7 @@ function createInstanceNamespace(credentials, cache, onTurn, agentModel = () =>
1743
1779
  onListen?.();
1744
1780
  } catch {
1745
1781
  }
1746
- const { GrpcStreamClient } = await import("./stream-client-3WHXGW4U.js");
1782
+ const { GrpcStreamClient } = await import("./stream-client-ZBCKRU7S.js");
1747
1783
  const onContext = options.on_context ?? options.onContext;
1748
1784
  const onDisconnect = options.on_disconnect ?? options.onDisconnect;
1749
1785
  const onReconnect = options.on_reconnect ?? options.onReconnect;
@@ -1922,7 +1958,7 @@ function createInstanceNamespace(credentials, cache, onTurn, agentModel = () =>
1922
1958
  }
1923
1959
 
1924
1960
  // src/version.ts
1925
- var SDK_VERSION = "0.5.9";
1961
+ var SDK_VERSION = "0.5.11";
1926
1962
 
1927
1963
  // src/registry.ts
1928
1964
  var instances = /* @__PURE__ */ new Map();
@@ -3648,6 +3684,7 @@ var SynapClient = class _SynapClient {
3648
3684
  instance;
3649
3685
  #closed = false;
3650
3686
  #initialized = false;
3687
+ #identity;
3651
3688
  #registryKeys;
3652
3689
  #compactionSubscribers;
3653
3690
  #shortTerm;
@@ -3698,6 +3735,7 @@ var SynapClient = class _SynapClient {
3698
3735
  userAgent: `@maximem/synap-js-sdk/${SDK_VERSION}`
3699
3736
  });
3700
3737
  if (options.heartbeat) this.#transport.startHeartbeat();
3738
+ this.#transport.identityResolver = () => this.#resolveIdentity();
3701
3739
  this.#anticipationCache = new AnticipationCache();
3702
3740
  this.#fetchHooks = {
3703
3741
  beforeFetch: async (scope, options2) => {
@@ -3903,19 +3941,30 @@ var SynapClient = class _SynapClient {
3903
3941
  /**
3904
3942
  * Resolve `client_id` and `instance_id` from the API key.
3905
3943
  *
3906
- * Mirrors Python's `initialize()`. Calling it is optional here (unlike
3907
- * Python, which raises "SDK not initialized" from `_ensure_initialized`)
3908
- * because every HTTP route authenticates on the API key alone. It matters
3909
- * for the anticipation cache: a bundle shared at client scope is filtered on
3910
- * `client_id`, and an empty one fails that filter for every bundle. That was
3911
- * a silent cache-scope bug in Python before whoami bootstrapping existed.
3944
+ * Mirrors Python's `initialize()`. Python raises "SDK not initialized" on
3945
+ * any call made before it; here the first request runs the same resolution
3946
+ * instead (`identityResolver` on the transport), so skipping this call costs
3947
+ * nothing and breaks no existing caller. It matters for the anticipation
3948
+ * cache: a bundle shared at client scope is filtered on `client_id`, and an
3949
+ * empty one fails that filter for every bundle.
3950
+ *
3951
+ * Calling it explicitly still locks `configure()`, as in Python. Resolving
3952
+ * on first use does not, because a caller who never called it could always
3953
+ * configure after a request, and that keeps working.
3912
3954
  *
3913
3955
  * Best-effort and idempotent. A whoami failure leaves whatever the
3914
3956
  * constructor resolved and is not fatal.
3915
3957
  */
3916
3958
  async initialize() {
3917
- if (this.#initialized) return;
3918
3959
  this.#initialized = true;
3960
+ await this.#resolveIdentity();
3961
+ }
3962
+ /** Runs whoami at most once, however many calls arrive together. */
3963
+ #resolveIdentity() {
3964
+ this.#identity ??= this.#whoami();
3965
+ return this.#identity;
3966
+ }
3967
+ async #whoami() {
3919
3968
  const credentials = this.#transport.currentCredentials();
3920
3969
  if (credentials.clientId !== "" && credentials.instanceId !== "") return;
3921
3970
  try {
@@ -4019,6 +4068,7 @@ var SynapClient = class _SynapClient {
4019
4068
  last_n_conversations: lastNConversations = 1,
4020
4069
  response_shape: responseShape = "by_type"
4021
4070
  } = options;
4071
+ await this.#transport.checkCustomerId(customerId, "sdk.fetch");
4022
4072
  validateConversationId(conversationId);
4023
4073
  responseShapeOf({ response_shape: responseShape });
4024
4074
  const wants = (scope) => scopes === void 0 || scopes.includes(scope);
@@ -4414,6 +4464,7 @@ export {
4414
4464
  ConflictError,
4415
4465
  ConnectionError,
4416
4466
  ContextNotFoundError,
4467
+ CustomerIdNotAcceptedError,
4417
4468
  DEFAULT_BASE_URL,
4418
4469
  DEFAULT_DOCUMENT_TYPE,
4419
4470
  DEFAULT_INGEST_MODE,