@telemai/sdk 0.1.1 → 0.2.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.
package/dist/index.cjs CHANGED
@@ -32,6 +32,7 @@ __export(index_exports, {
32
32
  fingerprint: () => fingerprint,
33
33
  sessionKey: () => sessionKey,
34
34
  snapshotNodeKey: () => snapshotNodeKey,
35
+ toolMarker: () => toolMarker,
35
36
  windowAnchor: () => windowAnchor,
36
37
  withTrajectory: () => withTrajectory
37
38
  });
@@ -58,7 +59,7 @@ var TimeoutError = class extends TelemError {
58
59
  };
59
60
 
60
61
  // src/version.ts
61
- var VERSION = "0.1.1";
62
+ var VERSION = "0.2.1";
62
63
 
63
64
  // src/config.ts
64
65
  var DEFAULT_BASE_URL = "https://router.telem.ai";
@@ -73,6 +74,9 @@ function csvEnv(raw) {
73
74
  const items = raw.split(",").map(trimBlank).filter((item) => item !== "");
74
75
  return items.length ? items : void 0;
75
76
  }
77
+ function intEnv(raw) {
78
+ return raw !== void 0 && /^\s*[+-]?\d+\s*$/.test(raw) ? Number(raw) : void 0;
79
+ }
76
80
  function readCredentials(env) {
77
81
  try {
78
82
  const proc = globalThis.process;
@@ -149,7 +153,10 @@ function resolveConfig(options) {
149
153
  fields: opts.fields ?? csvEnv(env.TELEM_FIELDS),
150
154
  providersInclude: opts.providersInclude ?? csvEnv(env.TELEM_PROVIDERS_INCLUDE),
151
155
  providersExclude: opts.providersExclude ?? csvEnv(env.TELEM_PROVIDERS_EXCLUDE),
152
- includeFullContent: opts.includeFullContent ?? (env.TELEM_FULL_CONTENT === "1" || void 0)
156
+ includeFullContent: opts.includeFullContent ?? (env.TELEM_FULL_CONTENT === "1" || void 0),
157
+ autoRouting: opts.autoRouting ?? (trimBlank(env.TELEM_AUTO_ROUTING ?? "") || void 0),
158
+ maxRoutingProviders: opts.maxRoutingProviders ?? intEnv(env.TELEM_MAX_ROUTING_PROVIDERS),
159
+ topic: opts.topic ?? (trimBlank(env.TELEM_TOPIC ?? "") || void 0)
153
160
  }
154
161
  };
155
162
  }
@@ -243,7 +250,7 @@ function retryDelay(retryAfter, attempt) {
243
250
  }
244
251
  return backoff(attempt);
245
252
  }
246
- async function request(config, method, path, body) {
253
+ async function request(config, method, path, body, abort) {
247
254
  const url = `${config.baseUrl}/${path.replace(/^\/+/, "")}`;
248
255
  const headers = { "User-Agent": config.userAgent };
249
256
  if (config.apiKey) headers.Authorization = `Bearer ${config.apiKey}`;
@@ -256,52 +263,71 @@ async function request(config, method, path, body) {
256
263
  throw invalidRequest(`request body is not JSON: ${cause?.message ?? String(cause)}`);
257
264
  }
258
265
  }
259
- for (let attempt = 0; ; attempt++) {
260
- const signal = Number.isFinite(config.timeoutMs) ? AbortSignal.timeout(Math.min(config.timeoutMs, MAX_TIMEOUT_MS)) : void 0;
261
- let onAbort = () => {
262
- };
263
- const deadline = signal && new Promise((_, reject) => {
264
- onAbort = () => reject(signal.reason);
265
- signal.addEventListener("abort", onAbort, { once: true });
266
- });
267
- const race = (work) => deadline ? Promise.race([work, deadline]) : work;
268
- let received;
269
- try {
270
- const response = await race(config.fetch(url, { ...init, signal }));
271
- if (!isResponseLike(response)) throw new NotAResponse();
272
- const text = await race(response.text());
273
- let parsed;
274
- try {
275
- parsed = JSON.parse(text);
276
- } catch {
277
- parsed = text;
278
- }
279
- received = {
280
- status: response.status,
281
- retryAfter: response.headers.get("retry-after"),
282
- body: parsed
266
+ let onAbort = () => {
267
+ };
268
+ const abortion = abort && new Promise((_, reject) => {
269
+ onAbort = () => reject(abort.reason ?? new Error("aborted"));
270
+ abort.addEventListener("abort", onAbort, { once: true });
271
+ });
272
+ abortion?.catch(() => {
273
+ });
274
+ const pause = (ms) => abortion ? Promise.race([config.sleep(ms), abortion]) : config.sleep(ms);
275
+ try {
276
+ return await attempts();
277
+ } finally {
278
+ abort?.removeEventListener("abort", onAbort);
279
+ }
280
+ async function attempts() {
281
+ for (let attempt = 0; ; attempt++) {
282
+ if (abort?.aborted) throw abort.reason ?? new Error("aborted");
283
+ const timeout = Number.isFinite(config.timeoutMs) ? AbortSignal.timeout(Math.min(config.timeoutMs, MAX_TIMEOUT_MS)) : void 0;
284
+ const signal = timeout && abort ? typeof AbortSignal.any === "function" ? AbortSignal.any([timeout, abort]) : timeout : timeout ?? abort;
285
+ let onAbort2 = () => {
283
286
  };
284
- } catch (cause) {
285
- if (cause instanceof NotAResponse) {
286
- throw new TelemError("the injected fetch returned something that is not a Response", {
287
- code: "connection_error"
288
- });
287
+ const deadline = signal && new Promise((_, reject) => {
288
+ onAbort2 = () => reject(signal.reason);
289
+ signal.addEventListener("abort", onAbort2, { once: true });
290
+ });
291
+ const race = (work) => deadline ? Promise.race([work, deadline]) : work;
292
+ let received;
293
+ try {
294
+ const response = await race(config.fetch(url, { ...init, signal }));
295
+ if (!isResponseLike(response)) throw new NotAResponse();
296
+ const text = await race(response.text());
297
+ let parsed;
298
+ try {
299
+ parsed = JSON.parse(text);
300
+ } catch {
301
+ parsed = text;
302
+ }
303
+ received = {
304
+ status: response.status,
305
+ retryAfter: response.headers.get("retry-after"),
306
+ body: parsed
307
+ };
308
+ } catch (cause) {
309
+ if (cause instanceof NotAResponse) {
310
+ throw new TelemError("the injected fetch returned something that is not a Response", {
311
+ code: "connection_error"
312
+ });
313
+ }
314
+ if (abort?.aborted) throw abort.reason ?? cause;
315
+ if (signal?.aborted) {
316
+ throw new TimeoutError(`request to ${url} timed out after ${config.timeoutMs}ms`, cause);
317
+ }
318
+ if (attempt >= config.maxRetries) throw connectionError(url, cause);
319
+ await pause(backoff(attempt));
320
+ continue;
321
+ } finally {
322
+ signal?.removeEventListener("abort", onAbort2);
289
323
  }
290
- if (signal?.aborted) {
291
- throw new TimeoutError(`request to ${url} timed out after ${config.timeoutMs}ms`, cause);
324
+ if (received.status >= 200 && received.status < 300) return received.body;
325
+ if (isRetryable(received.status) && attempt < config.maxRetries) {
326
+ await pause(retryDelay(received.retryAfter, attempt));
327
+ continue;
292
328
  }
293
- if (attempt >= config.maxRetries) throw connectionError(url, cause);
294
- await config.sleep(backoff(attempt));
295
- continue;
296
- } finally {
297
- signal?.removeEventListener("abort", onAbort);
298
- }
299
- if (received.status >= 200 && received.status < 300) return received.body;
300
- if (isRetryable(received.status) && attempt < config.maxRetries) {
301
- await config.sleep(retryDelay(received.retryAfter, attempt));
302
- continue;
329
+ throw statusError(received.status, received.body);
303
330
  }
304
- throw statusError(received.status, received.body);
305
331
  }
306
332
  }
307
333
 
@@ -372,10 +398,23 @@ async function contentAnchor(...parts) {
372
398
  return await sha256(parts.map(lp).join(""));
373
399
  }
374
400
  var HISTORY_TEXT_CAP = 128e3;
401
+ var TOOL_INPUT_CAP = 128e3;
375
402
  function cap(value) {
376
403
  if (value.length <= HISTORY_TEXT_CAP) return value;
377
404
  return Array.from(value).slice(0, HISTORY_TEXT_CAP).join("");
378
405
  }
406
+ function toolMarker(name, status, args) {
407
+ let serialized = "";
408
+ try {
409
+ const text = JSON.stringify(args);
410
+ if (typeof text === "string") serialized = text;
411
+ } catch {
412
+ serialized = "";
413
+ }
414
+ if (serialized.length > TOOL_INPUT_CAP) serialized = Array.from(serialized).slice(0, TOOL_INPUT_CAP).join("");
415
+ const suffix = serialized ? ` ${serialized}` : "";
416
+ return `[tool ${name || "?"}: ${status}${suffix}]`;
417
+ }
379
418
  function pyStr(value) {
380
419
  if (typeof value === "string") return value;
381
420
  if (value === null) return "None";
@@ -505,7 +544,7 @@ function buildFetchResponse(interaction) {
505
544
  }
506
545
  async function fetchUrls(config, url, options) {
507
546
  const body = fetchBody(url, options);
508
- return buildFetchResponse(await request(config, "POST", "/v1/fetch", body));
547
+ return buildFetchResponse(await request(config, "POST", "/v1/fetch", body, options.signal));
509
548
  }
510
549
 
511
550
  // src/methods/providers.ts
@@ -554,6 +593,12 @@ function searchBlock(defaults, options) {
554
593
  if (options.includeRaw !== void 0) block.include_raw = options.includeRaw;
555
594
  if (options.rerank !== void 0) block.rerank = options.rerank;
556
595
  if (includeFullContent !== void 0) block.include_full_content = includeFullContent;
596
+ const autoRouting = options.autoRouting ?? defaults.autoRouting;
597
+ if (autoRouting !== void 0) block.auto_routing = autoRouting;
598
+ const maxRoutingProviders = options.maxRoutingProviders ?? defaults.maxRoutingProviders;
599
+ if (maxRoutingProviders !== void 0) block.max_routing_providers = maxRoutingProviders;
600
+ const topic = options.topic ?? defaults.topic;
601
+ if (topic !== void 0) block.topic = topic;
557
602
  return block;
558
603
  }
559
604
  function userInput(query) {
@@ -654,7 +699,7 @@ function buildSearchResponse(interaction) {
654
699
  }
655
700
  async function search(config, query, options) {
656
701
  const body = searchBody(config, query, options);
657
- const data = await request(config, "POST", "/v1/interactions", body);
702
+ const data = await request(config, "POST", "/v1/interactions", body, options.signal);
658
703
  assertV2(data);
659
704
  return buildSearchResponse(data);
660
705
  }
@@ -722,7 +767,7 @@ var Telem = class {
722
767
  timeoutMs;
723
768
  /** Resolved retry budget (attempts after the first). */
724
769
  maxRetries;
725
- /** The session read surface: `list()` and `results(id)`. */
770
+ /** The session read surface: `list` and `results(id)`. */
726
771
  sessions;
727
772
  #config;
728
773
  /**
@@ -783,6 +828,7 @@ var index_default = Telem;
783
828
  fingerprint,
784
829
  sessionKey,
785
830
  snapshotNodeKey,
831
+ toolMarker,
786
832
  windowAnchor,
787
833
  withTrajectory
788
834
  });
package/dist/index.d.cts CHANGED
@@ -61,6 +61,9 @@ type SearchOptions = {
61
61
  numResults?: number;
62
62
  includeRaw?: boolean;
63
63
  includeFullContent?: boolean;
64
+ autoRouting?: string;
65
+ maxRoutingProviders?: number;
66
+ topic?: string;
64
67
  /** Opt this request into the server's rerank stage. It is the only path where
65
68
  * `context`, the reasoning on the last assistant entry of the lineage history
66
69
  * (that entry only), and `goal` change result ORDER (in that precedence) —
@@ -70,6 +73,9 @@ type SearchOptions = {
70
73
  goal?: string;
71
74
  context?: string;
72
75
  session?: string;
76
+ /** Abort this call: the request stops, no retry follows, and the call rejects
77
+ * with the signal's reason. Merged with the per-attempt timeout. */
78
+ signal?: AbortSignal;
73
79
  /** Freeform metadata for the wire. Reserved trajectory-lineage keys
74
80
  * (`RESERVED_METADATA`) are STRIPPED from here — lineage may only be attached
75
81
  * through `withTrajectory`, fed by `buildMetadata`, never smuggled in as data. */
@@ -77,13 +83,16 @@ type SearchOptions = {
77
83
  };
78
84
  /** The subset of search options that may also be set as a client default.
79
85
  * `numResults`/`includeRaw`/`rerank`/`providerOverrides` are per-call intent only. */
80
- type SearchDefaults = Pick<SearchOptions, "tier" | "fields" | "providersInclude" | "providersExclude" | "includeFullContent">;
86
+ type SearchDefaults = Pick<SearchOptions, "tier" | "fields" | "providersInclude" | "providersExclude" | "includeFullContent" | "autoRouting" | "maxRoutingProviders" | "topic">;
81
87
  type FetchOptions = {
82
88
  providers?: string[];
83
89
  inlineContent?: boolean;
84
90
  inlineMaxChars?: number;
85
91
  contentFormat?: string;
86
92
  session?: string;
93
+ /** Abort this call: the request stops, no retry follows, and the call rejects
94
+ * with the signal's reason. Merged with the per-attempt timeout. */
95
+ signal?: AbortSignal;
87
96
  metadata?: Json;
88
97
  };
89
98
  type TelemOptions = SearchDefaults & {
@@ -181,7 +190,7 @@ type ProviderInfo = {
181
190
  normalized: boolean;
182
191
  tiers: string[];
183
192
  };
184
- /** One session as `sessions.list()` reports it. */
193
+ /** One session as `sessions.list` reports it. */
185
194
  type SessionSummary = {
186
195
  id: string;
187
196
  createdAt: string | null;
@@ -258,6 +267,12 @@ declare function snapshotNodeKey(harness: string, conversationId: string, messag
258
267
  /** Stable anchor id for content a host did not assign an id to.
259
268
  * Length-prefixed like every other key component, so concatenation stays injective. */
260
269
  declare function contentAnchor(...parts: string[]): Promise<string>;
270
+ /** `tool_marker` from the Python SDK's _trajectory_v5.py: one tool call as the
271
+ * compact acting-trace marker every harness renders into history. The exporter
272
+ * cuts goals on `[tool telem_search:`, so the text is a contract. Compact JSON,
273
+ * key order kept, matches Python's `separators=(",", ":")`; an unserializable
274
+ * input drops the suffix rather than the marker. */
275
+ declare function toolMarker(name: string, status: string, args: unknown): string;
261
276
  /**
262
277
  * Identify this context-window generation by its first message.
263
278
  *
@@ -318,7 +333,7 @@ declare function withTrajectory(options: FetchOptions, envelope: TrajectoryEnvel
318
333
  *
319
334
  * @example
320
335
  * ```ts
321
- * const telem = new Telem() // env / credentials-file resolution
336
+ * const telem = new Telem // env / credentials-file resolution
322
337
  * const telem = new Telem("tlm_...") // bare API-key shorthand
323
338
  * const telem = new Telem({ apiKey, baseUrl, tier: "max" })
324
339
  * ```
@@ -333,7 +348,7 @@ declare class Telem {
333
348
  readonly timeoutMs: number;
334
349
  /** Resolved retry budget (attempts after the first). */
335
350
  readonly maxRetries: number;
336
- /** The session read surface: `list()` and `results(id)`. */
351
+ /** The session read surface: `list` and `results(id)`. */
337
352
  readonly sessions: SessionsResource;
338
353
  /**
339
354
  * @param options - An API key string, or an options object. Anything left unset
@@ -366,4 +381,4 @@ declare class Telem {
366
381
  providers(): Promise<ProviderInfo[]>;
367
382
  }
368
383
 
369
- export { type FetchOptions, type FetchResponse, type FetchResult, type Json, type ProviderInfo, type ProviderRun, RESERVED_METADATA, type SearchDefaults, type SearchOptions, type SearchResponse, type SearchResult, type SessionResults, type SessionSummary, type SessionsResource, type SnapshotArgs, Telem, type TelemClientErrorCode, TelemError, type TelemErrorOptions, type TelemOptions, TimeoutError, type TrajectoryArgs, type TrajectoryEnvelope, type TrajectorySnapshot, buildMetadata, buildSnapshot, contentAnchor, Telem as default, eventNodeKey, fingerprint, sessionKey, snapshotNodeKey, windowAnchor, withTrajectory };
384
+ export { type FetchOptions, type FetchResponse, type FetchResult, type Json, type ProviderInfo, type ProviderRun, RESERVED_METADATA, type SearchDefaults, type SearchOptions, type SearchResponse, type SearchResult, type SessionResults, type SessionSummary, type SessionsResource, type SnapshotArgs, Telem, type TelemClientErrorCode, TelemError, type TelemErrorOptions, type TelemOptions, TimeoutError, type TrajectoryArgs, type TrajectoryEnvelope, type TrajectorySnapshot, buildMetadata, buildSnapshot, contentAnchor, Telem as default, eventNodeKey, fingerprint, sessionKey, snapshotNodeKey, toolMarker, windowAnchor, withTrajectory };
package/dist/index.d.ts CHANGED
@@ -61,6 +61,9 @@ type SearchOptions = {
61
61
  numResults?: number;
62
62
  includeRaw?: boolean;
63
63
  includeFullContent?: boolean;
64
+ autoRouting?: string;
65
+ maxRoutingProviders?: number;
66
+ topic?: string;
64
67
  /** Opt this request into the server's rerank stage. It is the only path where
65
68
  * `context`, the reasoning on the last assistant entry of the lineage history
66
69
  * (that entry only), and `goal` change result ORDER (in that precedence) —
@@ -70,6 +73,9 @@ type SearchOptions = {
70
73
  goal?: string;
71
74
  context?: string;
72
75
  session?: string;
76
+ /** Abort this call: the request stops, no retry follows, and the call rejects
77
+ * with the signal's reason. Merged with the per-attempt timeout. */
78
+ signal?: AbortSignal;
73
79
  /** Freeform metadata for the wire. Reserved trajectory-lineage keys
74
80
  * (`RESERVED_METADATA`) are STRIPPED from here — lineage may only be attached
75
81
  * through `withTrajectory`, fed by `buildMetadata`, never smuggled in as data. */
@@ -77,13 +83,16 @@ type SearchOptions = {
77
83
  };
78
84
  /** The subset of search options that may also be set as a client default.
79
85
  * `numResults`/`includeRaw`/`rerank`/`providerOverrides` are per-call intent only. */
80
- type SearchDefaults = Pick<SearchOptions, "tier" | "fields" | "providersInclude" | "providersExclude" | "includeFullContent">;
86
+ type SearchDefaults = Pick<SearchOptions, "tier" | "fields" | "providersInclude" | "providersExclude" | "includeFullContent" | "autoRouting" | "maxRoutingProviders" | "topic">;
81
87
  type FetchOptions = {
82
88
  providers?: string[];
83
89
  inlineContent?: boolean;
84
90
  inlineMaxChars?: number;
85
91
  contentFormat?: string;
86
92
  session?: string;
93
+ /** Abort this call: the request stops, no retry follows, and the call rejects
94
+ * with the signal's reason. Merged with the per-attempt timeout. */
95
+ signal?: AbortSignal;
87
96
  metadata?: Json;
88
97
  };
89
98
  type TelemOptions = SearchDefaults & {
@@ -181,7 +190,7 @@ type ProviderInfo = {
181
190
  normalized: boolean;
182
191
  tiers: string[];
183
192
  };
184
- /** One session as `sessions.list()` reports it. */
193
+ /** One session as `sessions.list` reports it. */
185
194
  type SessionSummary = {
186
195
  id: string;
187
196
  createdAt: string | null;
@@ -258,6 +267,12 @@ declare function snapshotNodeKey(harness: string, conversationId: string, messag
258
267
  /** Stable anchor id for content a host did not assign an id to.
259
268
  * Length-prefixed like every other key component, so concatenation stays injective. */
260
269
  declare function contentAnchor(...parts: string[]): Promise<string>;
270
+ /** `tool_marker` from the Python SDK's _trajectory_v5.py: one tool call as the
271
+ * compact acting-trace marker every harness renders into history. The exporter
272
+ * cuts goals on `[tool telem_search:`, so the text is a contract. Compact JSON,
273
+ * key order kept, matches Python's `separators=(",", ":")`; an unserializable
274
+ * input drops the suffix rather than the marker. */
275
+ declare function toolMarker(name: string, status: string, args: unknown): string;
261
276
  /**
262
277
  * Identify this context-window generation by its first message.
263
278
  *
@@ -318,7 +333,7 @@ declare function withTrajectory(options: FetchOptions, envelope: TrajectoryEnvel
318
333
  *
319
334
  * @example
320
335
  * ```ts
321
- * const telem = new Telem() // env / credentials-file resolution
336
+ * const telem = new Telem // env / credentials-file resolution
322
337
  * const telem = new Telem("tlm_...") // bare API-key shorthand
323
338
  * const telem = new Telem({ apiKey, baseUrl, tier: "max" })
324
339
  * ```
@@ -333,7 +348,7 @@ declare class Telem {
333
348
  readonly timeoutMs: number;
334
349
  /** Resolved retry budget (attempts after the first). */
335
350
  readonly maxRetries: number;
336
- /** The session read surface: `list()` and `results(id)`. */
351
+ /** The session read surface: `list` and `results(id)`. */
337
352
  readonly sessions: SessionsResource;
338
353
  /**
339
354
  * @param options - An API key string, or an options object. Anything left unset
@@ -366,4 +381,4 @@ declare class Telem {
366
381
  providers(): Promise<ProviderInfo[]>;
367
382
  }
368
383
 
369
- export { type FetchOptions, type FetchResponse, type FetchResult, type Json, type ProviderInfo, type ProviderRun, RESERVED_METADATA, type SearchDefaults, type SearchOptions, type SearchResponse, type SearchResult, type SessionResults, type SessionSummary, type SessionsResource, type SnapshotArgs, Telem, type TelemClientErrorCode, TelemError, type TelemErrorOptions, type TelemOptions, TimeoutError, type TrajectoryArgs, type TrajectoryEnvelope, type TrajectorySnapshot, buildMetadata, buildSnapshot, contentAnchor, Telem as default, eventNodeKey, fingerprint, sessionKey, snapshotNodeKey, windowAnchor, withTrajectory };
384
+ export { type FetchOptions, type FetchResponse, type FetchResult, type Json, type ProviderInfo, type ProviderRun, RESERVED_METADATA, type SearchDefaults, type SearchOptions, type SearchResponse, type SearchResult, type SessionResults, type SessionSummary, type SessionsResource, type SnapshotArgs, Telem, type TelemClientErrorCode, TelemError, type TelemErrorOptions, type TelemOptions, TimeoutError, type TrajectoryArgs, type TrajectoryEnvelope, type TrajectorySnapshot, buildMetadata, buildSnapshot, contentAnchor, Telem as default, eventNodeKey, fingerprint, sessionKey, snapshotNodeKey, toolMarker, windowAnchor, withTrajectory };