@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.js CHANGED
@@ -1,25 +1,28 @@
1
- // src/errors.ts
2
- var TelemError = class extends Error {
3
- status;
4
- code;
5
- details;
6
- constructor(message, options = {}) {
7
- super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
8
- this.name = "TelemError";
9
- this.status = options.status;
10
- this.code = options.code;
11
- this.details = options.details;
12
- }
13
- };
14
- var TimeoutError = class extends TelemError {
15
- constructor(message, cause) {
16
- super(message, { code: "timeout", cause });
17
- this.name = "TimeoutError";
18
- }
19
- };
1
+ import {
2
+ RESERVED_METADATA,
3
+ TelemError,
4
+ TimeoutError,
5
+ asArray,
6
+ asObject,
7
+ buildMetadata,
8
+ buildSnapshot,
9
+ contentAnchor,
10
+ eventNodeKey,
11
+ fingerprint,
12
+ invalidRequest,
13
+ isObject,
14
+ request,
15
+ sessionKey,
16
+ snapshotNodeKey,
17
+ stringList,
18
+ toolMarker,
19
+ windowAnchor,
20
+ wireMetadata,
21
+ withTrajectory
22
+ } from "./chunk-RYY4S5LP.js";
20
23
 
21
24
  // src/version.ts
22
- var VERSION = "0.1.1";
25
+ var VERSION = "0.2.1";
23
26
 
24
27
  // src/config.ts
25
28
  var DEFAULT_BASE_URL = "https://router.telem.ai";
@@ -34,6 +37,9 @@ function csvEnv(raw) {
34
37
  const items = raw.split(",").map(trimBlank).filter((item) => item !== "");
35
38
  return items.length ? items : void 0;
36
39
  }
40
+ function intEnv(raw) {
41
+ return raw !== void 0 && /^\s*[+-]?\d+\s*$/.test(raw) ? Number(raw) : void 0;
42
+ }
37
43
  function readCredentials(env) {
38
44
  try {
39
45
  const proc = globalThis.process;
@@ -110,306 +116,13 @@ function resolveConfig(options) {
110
116
  fields: opts.fields ?? csvEnv(env.TELEM_FIELDS),
111
117
  providersInclude: opts.providersInclude ?? csvEnv(env.TELEM_PROVIDERS_INCLUDE),
112
118
  providersExclude: opts.providersExclude ?? csvEnv(env.TELEM_PROVIDERS_EXCLUDE),
113
- includeFullContent: opts.includeFullContent ?? (env.TELEM_FULL_CONTENT === "1" || void 0)
114
- }
115
- };
116
- }
117
-
118
- // src/http.ts
119
- var MAX_DELAY_MS = 3e4;
120
- var MAX_TIMEOUT_MS = 2 ** 31 - 1;
121
- var BASE_DELAY_MS = 500;
122
- function isObject(value) {
123
- return typeof value === "object" && value !== null && !Array.isArray(value);
124
- }
125
- function asObject(value) {
126
- return isObject(value) ? value : {};
127
- }
128
- function asArray(value) {
129
- return Array.isArray(value) ? value : [];
130
- }
131
- function invalidRequest(message) {
132
- return new TelemError(message, { code: "invalid_request" });
133
- }
134
- function stringList(items, empty, what) {
135
- const list = asArray(items);
136
- if (!list.length) throw invalidRequest(empty);
137
- for (const item of list) {
138
- if (typeof item !== "string") {
139
- throw invalidRequest(`${what} must be strings, got ${item === null ? "null" : typeof item}`);
140
- }
141
- if (item === "") throw invalidRequest(`${what} must not be empty`);
142
- }
143
- return list;
144
- }
145
- function errorEnvelope(body) {
146
- if (!isObject(body)) return void 0;
147
- if (isObject(body.error)) return body.error;
148
- if (isObject(body.detail)) return body.detail;
149
- return void 0;
150
- }
151
- function detailFromBody(body) {
152
- if (!isObject(body)) return void 0;
153
- const envelope = errorEnvelope(body);
154
- if (envelope && typeof envelope.message === "string" && envelope.message) {
155
- return envelope.message;
156
- }
157
- const detail = body.detail;
158
- if (typeof detail === "string" && detail) return detail;
159
- if (Array.isArray(detail) && detail.length) {
160
- const messages = detail.map((item) => isObject(item) && typeof item.msg === "string" ? item.msg : void 0).filter((message) => message !== void 0);
161
- return messages.join(", ") || JSON.stringify(detail);
162
- }
163
- return void 0;
164
- }
165
- function codeFromBody(body) {
166
- const envelope = errorEnvelope(body);
167
- return envelope && typeof envelope.code === "string" ? envelope.code : void 0;
168
- }
169
- function isResponseLike(value) {
170
- return isObject(value) && typeof value.text === "function" && typeof value.status === "number" && typeof value.headers?.get === "function";
171
- }
172
- var NotAResponse = class extends Error {
173
- };
174
- function defaultMessage(status) {
175
- if (status === 400) return "Bad request";
176
- if (status === 401 || status === 403) return "Authentication failed";
177
- if (status === 404) return "Not found";
178
- return `Unexpected status code ${status}`;
179
- }
180
- function statusError(status, body) {
181
- return new TelemError(detailFromBody(body) ?? defaultMessage(status), {
182
- status,
183
- code: codeFromBody(body),
184
- details: body
185
- });
186
- }
187
- function connectionError(url, cause) {
188
- const error = cause;
189
- const detail = typeof error?.cause?.message === "string" && error.cause.message || typeof error?.message === "string" && error.message || String(cause);
190
- return new TelemError(`could not reach ${url}: ${detail}`, {
191
- code: "connection_error",
192
- cause
193
- });
194
- }
195
- function isRetryable(status) {
196
- return status >= 500 || status === 429;
197
- }
198
- function backoff(attempt) {
199
- return Math.min(BASE_DELAY_MS * 2 ** attempt, MAX_DELAY_MS);
200
- }
201
- function retryDelay(retryAfter, attempt) {
202
- if (retryAfter !== null && /^\s*\d+\s*$/.test(retryAfter)) {
203
- return Math.min(Number(retryAfter) * 1e3, MAX_DELAY_MS);
204
- }
205
- return backoff(attempt);
206
- }
207
- async function request(config, method, path, body) {
208
- const url = `${config.baseUrl}/${path.replace(/^\/+/, "")}`;
209
- const headers = { "User-Agent": config.userAgent };
210
- if (config.apiKey) headers.Authorization = `Bearer ${config.apiKey}`;
211
- const init = { method, headers };
212
- if (body !== void 0) {
213
- headers["Content-Type"] = "application/json";
214
- try {
215
- init.body = JSON.stringify(body);
216
- } catch (cause) {
217
- throw invalidRequest(`request body is not JSON: ${cause?.message ?? String(cause)}`);
218
- }
219
- }
220
- for (let attempt = 0; ; attempt++) {
221
- const signal = Number.isFinite(config.timeoutMs) ? AbortSignal.timeout(Math.min(config.timeoutMs, MAX_TIMEOUT_MS)) : void 0;
222
- let onAbort = () => {
223
- };
224
- const deadline = signal && new Promise((_, reject) => {
225
- onAbort = () => reject(signal.reason);
226
- signal.addEventListener("abort", onAbort, { once: true });
227
- });
228
- const race = (work) => deadline ? Promise.race([work, deadline]) : work;
229
- let received;
230
- try {
231
- const response = await race(config.fetch(url, { ...init, signal }));
232
- if (!isResponseLike(response)) throw new NotAResponse();
233
- const text = await race(response.text());
234
- let parsed;
235
- try {
236
- parsed = JSON.parse(text);
237
- } catch {
238
- parsed = text;
239
- }
240
- received = {
241
- status: response.status,
242
- retryAfter: response.headers.get("retry-after"),
243
- body: parsed
244
- };
245
- } catch (cause) {
246
- if (cause instanceof NotAResponse) {
247
- throw new TelemError("the injected fetch returned something that is not a Response", {
248
- code: "connection_error"
249
- });
250
- }
251
- if (signal?.aborted) {
252
- throw new TimeoutError(`request to ${url} timed out after ${config.timeoutMs}ms`, cause);
253
- }
254
- if (attempt >= config.maxRetries) throw connectionError(url, cause);
255
- await config.sleep(backoff(attempt));
256
- continue;
257
- } finally {
258
- signal?.removeEventListener("abort", onAbort);
119
+ includeFullContent: opts.includeFullContent ?? (env.TELEM_FULL_CONTENT === "1" || void 0),
120
+ autoRouting: opts.autoRouting ?? (trimBlank(env.TELEM_AUTO_ROUTING ?? "") || void 0),
121
+ maxRoutingProviders: opts.maxRoutingProviders ?? intEnv(env.TELEM_MAX_ROUTING_PROVIDERS),
122
+ topic: opts.topic ?? (trimBlank(env.TELEM_TOPIC ?? "") || void 0)
259
123
  }
260
- if (received.status >= 200 && received.status < 300) return received.body;
261
- if (isRetryable(received.status) && attempt < config.maxRetries) {
262
- await config.sleep(retryDelay(received.retryAfter, attempt));
263
- continue;
264
- }
265
- throw statusError(received.status, received.body);
266
- }
267
- }
268
-
269
- // src/trajectory.ts
270
- var NS_TRAJECTORY = "443866ab-1b45-5ed8-979e-52fdad07b810";
271
- var NONE = "none";
272
- var RESERVED_METADATA = /* @__PURE__ */ new Set([
273
- "message_history",
274
- "session_key",
275
- "fingerprint",
276
- "node_key",
277
- "kind",
278
- "parent_node_key",
279
- "ancestors"
280
- ]);
281
- var ENCODER = new TextEncoder();
282
- var NAMESPACE_BYTES = Uint8Array.from(
283
- NS_TRAJECTORY.replaceAll("-", "").match(/../g) ?? [],
284
- (pair) => parseInt(pair, 16)
285
- );
286
- function hex(bytes) {
287
- let out = "";
288
- for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
289
- return out;
290
- }
291
- async function digest(algorithm, bytes) {
292
- return new Uint8Array(await crypto.subtle.digest(algorithm, bytes));
293
- }
294
- async function sha256(value) {
295
- return hex(await digest("SHA-256", ENCODER.encode(value)));
296
- }
297
- function lp(value) {
298
- return `${ENCODER.encode(value).length}:${value}`;
299
- }
300
- async function uuid5(name) {
301
- const encoded = ENCODER.encode(name);
302
- const input = new Uint8Array(NAMESPACE_BYTES.length + encoded.length);
303
- input.set(NAMESPACE_BYTES);
304
- input.set(encoded, NAMESPACE_BYTES.length);
305
- const bytes = (await digest("SHA-1", input)).slice(0, 16);
306
- bytes[6] = bytes[6] & 15 | 80;
307
- bytes[8] = bytes[8] & 63 | 128;
308
- const chars = hex(bytes);
309
- return [
310
- chars.slice(0, 8),
311
- chars.slice(8, 12),
312
- chars.slice(12, 16),
313
- chars.slice(16, 20),
314
- chars.slice(20)
315
- ].join("-");
316
- }
317
- async function fingerprint(harness, conversationId) {
318
- return await sha256(lp(harness) + lp(conversationId));
319
- }
320
- async function sessionKey(harness, conversationId, windowId) {
321
- const name = lp(harness) + lp(await sha256(conversationId)) + lp(windowId) + lp(NONE);
322
- return await uuid5(name);
323
- }
324
- async function eventNodeKey(harness, session, messageId, toolCallId) {
325
- const name = lp(harness) + lp(session) + lp(messageId) + lp(toolCallId) + lp("event");
326
- return await uuid5(name);
327
- }
328
- async function snapshotNodeKey(harness, conversationId, messageId) {
329
- const name = lp(harness) + lp(await sha256(conversationId)) + lp(messageId) + lp("snap");
330
- return await uuid5(name);
331
- }
332
- async function contentAnchor(...parts) {
333
- return await sha256(parts.map(lp).join(""));
334
- }
335
- var HISTORY_TEXT_CAP = 128e3;
336
- function cap(value) {
337
- if (value.length <= HISTORY_TEXT_CAP) return value;
338
- return Array.from(value).slice(0, HISTORY_TEXT_CAP).join("");
339
- }
340
- function pyStr(value) {
341
- if (typeof value === "string") return value;
342
- if (value === null) return "None";
343
- if (value === true) return "True";
344
- if (value === false) return "False";
345
- return String(value);
346
- }
347
- function messageText(content) {
348
- if (typeof content === "string") return cap(content);
349
- if (Array.isArray(content)) {
350
- const pieces = [];
351
- for (const block of content) {
352
- if (typeof block === "string") pieces.push(block);
353
- else if (isObject(block) && typeof block.text === "string") pieces.push(block.text);
354
- }
355
- return cap(pieces.join("\n"));
356
- }
357
- return content === null || content === void 0 ? "" : cap(pyStr(content));
358
- }
359
- async function windowAnchor(messages) {
360
- const first = messages[0];
361
- if (first === void 0) return NONE;
362
- return await contentAnchor(
363
- first.role === void 0 ? "" : pyStr(first.role),
364
- messageText(first.content)
365
- );
366
- }
367
- async function buildMetadata(args) {
368
- const [session, fp] = await Promise.all([
369
- sessionKey(args.harness, args.conversationId, args.windowId),
370
- fingerprint(args.harness, args.conversationId)
371
- ]);
372
- return {
373
- message_history: args.history,
374
- session_key: session,
375
- fingerprint: fp,
376
- node_key: await eventNodeKey(args.harness, session, args.messageId, args.toolCallId),
377
- parent_node_key: parentNodeKey(args.ancestors),
378
- ancestors: args.ancestors
379
- };
380
- }
381
- async function buildSnapshot(args) {
382
- const [session, fp, node] = await Promise.all([
383
- sessionKey(args.harness, args.conversationId, args.windowId),
384
- fingerprint(args.harness, args.conversationId),
385
- snapshotNodeKey(args.harness, args.conversationId, args.messageId)
386
- ]);
387
- return {
388
- session_key: session,
389
- fingerprint: fp,
390
- node_key: node,
391
- parent_node_key: parentNodeKey(args.ancestors),
392
- context: args.context,
393
- spawned_at: args.spawnedAt ?? (/* @__PURE__ */ new Date()).toISOString()
394
124
  };
395
125
  }
396
- function parentNodeKey(ancestors) {
397
- return ancestors.at(-1)?.node_key ?? null;
398
- }
399
- var TRAJECTORY = /* @__PURE__ */ Symbol.for("telem.trajectory");
400
- function withTrajectory(options, envelope) {
401
- return { ...options, [TRAJECTORY]: envelope };
402
- }
403
- function wireMetadata(options, kind) {
404
- const metadata = {};
405
- for (const [key, value] of Object.entries(options.metadata ?? {})) {
406
- if (!RESERVED_METADATA.has(key)) metadata[key] = value;
407
- }
408
- const envelope = options[TRAJECTORY];
409
- if (envelope !== void 0) Object.assign(metadata, envelope, { kind });
410
- const sessionId = metadata.session_key === void 0 ? options.session : void 0;
411
- return { metadata, sessionId };
412
- }
413
126
 
414
127
  // src/methods/fetch.ts
415
128
  function fetchBody(url, options) {
@@ -466,7 +179,7 @@ function buildFetchResponse(interaction) {
466
179
  }
467
180
  async function fetchUrls(config, url, options) {
468
181
  const body = fetchBody(url, options);
469
- return buildFetchResponse(await request(config, "POST", "/v1/fetch", body));
182
+ return buildFetchResponse(await request(config, "POST", "/v1/fetch", body, options.signal));
470
183
  }
471
184
 
472
185
  // src/methods/providers.ts
@@ -515,6 +228,12 @@ function searchBlock(defaults, options) {
515
228
  if (options.includeRaw !== void 0) block.include_raw = options.includeRaw;
516
229
  if (options.rerank !== void 0) block.rerank = options.rerank;
517
230
  if (includeFullContent !== void 0) block.include_full_content = includeFullContent;
231
+ const autoRouting = options.autoRouting ?? defaults.autoRouting;
232
+ if (autoRouting !== void 0) block.auto_routing = autoRouting;
233
+ const maxRoutingProviders = options.maxRoutingProviders ?? defaults.maxRoutingProviders;
234
+ if (maxRoutingProviders !== void 0) block.max_routing_providers = maxRoutingProviders;
235
+ const topic = options.topic ?? defaults.topic;
236
+ if (topic !== void 0) block.topic = topic;
518
237
  return block;
519
238
  }
520
239
  function userInput(query) {
@@ -615,7 +334,7 @@ function buildSearchResponse(interaction) {
615
334
  }
616
335
  async function search(config, query, options) {
617
336
  const body = searchBody(config, query, options);
618
- const data = await request(config, "POST", "/v1/interactions", body);
337
+ const data = await request(config, "POST", "/v1/interactions", body, options.signal);
619
338
  assertV2(data);
620
339
  return buildSearchResponse(data);
621
340
  }
@@ -683,7 +402,7 @@ var Telem = class {
683
402
  timeoutMs;
684
403
  /** Resolved retry budget (attempts after the first). */
685
404
  maxRetries;
686
- /** The session read surface: `list()` and `results(id)`. */
405
+ /** The session read surface: `list` and `results(id)`. */
687
406
  sessions;
688
407
  #config;
689
408
  /**
@@ -744,6 +463,7 @@ export {
744
463
  fingerprint,
745
464
  sessionKey,
746
465
  snapshotNodeKey,
466
+ toolMarker,
747
467
  windowAnchor,
748
468
  withTrajectory
749
469
  };