@pentatonic-ai/ai-agent-sdk 0.3.0-beta.3

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 ADDED
@@ -0,0 +1,699 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/index.js
20
+ var src_exports = {};
21
+ __export(src_exports, {
22
+ Session: () => Session,
23
+ TESClient: () => TESClient,
24
+ buildTrackUrl: () => buildTrackUrl,
25
+ normalizeResponse: () => normalizeResponse,
26
+ rewriteUrls: () => rewriteUrls,
27
+ signPayload: () => signPayload,
28
+ verifyPayload: () => verifyPayload
29
+ });
30
+ module.exports = __toCommonJS(src_exports);
31
+
32
+ // src/normalizer.js
33
+ function normalizeResponse(raw) {
34
+ if (!raw || typeof raw !== "object") {
35
+ return empty();
36
+ }
37
+ if (Array.isArray(raw.choices)) {
38
+ return normalizeOpenAI(raw);
39
+ }
40
+ if (Array.isArray(raw.content) && raw.content[0]?.type) {
41
+ return normalizeAnthropic(raw);
42
+ }
43
+ if (typeof raw.response === "string" || raw.tool_calls && !raw.choices) {
44
+ return normalizeWorkersAI(raw);
45
+ }
46
+ return empty();
47
+ }
48
+ function empty() {
49
+ return {
50
+ content: "",
51
+ model: null,
52
+ usage: { prompt_tokens: 0, completion_tokens: 0 },
53
+ toolCalls: []
54
+ };
55
+ }
56
+ function normalizeOpenAI(raw) {
57
+ const message = raw.choices?.[0]?.message || {};
58
+ const usage = raw.usage || {};
59
+ const rawToolCalls = message.tool_calls?.length ? message.tool_calls : raw.tool_calls || [];
60
+ const toolCalls = rawToolCalls.map((tc) => ({
61
+ tool: tc.function?.name || tc.name,
62
+ args: parseArgs(tc.function?.arguments || tc.arguments)
63
+ }));
64
+ return {
65
+ content: message.content || "",
66
+ model: raw.model || null,
67
+ usage: {
68
+ prompt_tokens: usage.prompt_tokens || 0,
69
+ completion_tokens: usage.completion_tokens || 0
70
+ },
71
+ toolCalls
72
+ };
73
+ }
74
+ function normalizeAnthropic(raw) {
75
+ const usage = raw.usage || {};
76
+ let content = "";
77
+ const toolCalls = [];
78
+ for (const block of raw.content) {
79
+ if (block.type === "text") {
80
+ content += block.text;
81
+ } else if (block.type === "tool_use") {
82
+ toolCalls.push({ tool: block.name, args: block.input || {} });
83
+ }
84
+ }
85
+ return {
86
+ content,
87
+ model: raw.model || null,
88
+ usage: {
89
+ prompt_tokens: usage.input_tokens || 0,
90
+ completion_tokens: usage.output_tokens || 0
91
+ },
92
+ toolCalls
93
+ };
94
+ }
95
+ function normalizeWorkersAI(raw) {
96
+ const usage = raw.usage || {};
97
+ const toolCalls = (raw.tool_calls || []).map((tc) => ({
98
+ tool: tc.function?.name || tc.name,
99
+ args: parseArgs(tc.function?.arguments || tc.arguments || {})
100
+ }));
101
+ return {
102
+ content: raw.response || "",
103
+ model: raw.model || null,
104
+ usage: {
105
+ prompt_tokens: usage.prompt_tokens || 0,
106
+ completion_tokens: usage.completion_tokens || 0
107
+ },
108
+ toolCalls
109
+ };
110
+ }
111
+ function parseArgs(args) {
112
+ if (typeof args === "string") {
113
+ try {
114
+ return JSON.parse(args);
115
+ } catch {
116
+ return {};
117
+ }
118
+ }
119
+ return args || {};
120
+ }
121
+
122
+ // src/transport.js
123
+ var EMIT_EVENT_MUTATION = `
124
+ mutation EmitEvent($input: EventInput!) {
125
+ emitEvent(input: $input) {
126
+ success
127
+ eventId
128
+ message
129
+ }
130
+ }
131
+ `;
132
+ async function sendEvent({ endpoint, apiKey, clientId, userId, headers }, input, fetchFn) {
133
+ const f = fetchFn || globalThis.fetch;
134
+ const authHeaders = apiKey.startsWith("tes_") ? { Authorization: `Bearer ${apiKey}` } : { "x-service-key": apiKey };
135
+ const response = await f(`${endpoint}/api/graphql`, {
136
+ method: "POST",
137
+ headers: {
138
+ "Content-Type": "application/json",
139
+ "x-client-id": clientId,
140
+ ...headers,
141
+ ...authHeaders
142
+ },
143
+ body: JSON.stringify({
144
+ query: EMIT_EVENT_MUTATION,
145
+ variables: {
146
+ input: userId ? { ...input, data: { ...input.data, attributes: { ...input.data?.attributes, userId } } } : input
147
+ }
148
+ })
149
+ });
150
+ if (!response.ok) {
151
+ throw new Error(`TES API error: ${response.status}`);
152
+ }
153
+ const json = await response.json();
154
+ if (json.errors?.length) {
155
+ throw new Error(`TES GraphQL error: ${json.errors[0].message}`);
156
+ }
157
+ return json.data.emitEvent;
158
+ }
159
+
160
+ // src/tracking.js
161
+ var encoder = new TextEncoder();
162
+ function toBase64Url(buffer) {
163
+ const bytes = new Uint8Array(buffer);
164
+ let binary = "";
165
+ for (let i = 0; i < bytes.length; i++)
166
+ binary += String.fromCharCode(bytes[i]);
167
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
168
+ }
169
+ async function signPayload(secret, payload) {
170
+ const key = await crypto.subtle.importKey(
171
+ "raw",
172
+ encoder.encode(secret),
173
+ { name: "HMAC", hash: "SHA-256" },
174
+ false,
175
+ ["sign"]
176
+ );
177
+ const data = encoder.encode(JSON.stringify(payload));
178
+ const sig = await crypto.subtle.sign("HMAC", key, data);
179
+ return toBase64Url(sig);
180
+ }
181
+ async function verifyPayload(secret, payload, signature) {
182
+ const expected = await signPayload(secret, payload);
183
+ return expected === signature;
184
+ }
185
+ async function buildTrackUrl(endpoint, apiKey, payload) {
186
+ const p = { ...payload };
187
+ if (!p.e)
188
+ p.e = "LINK_CLICK";
189
+ const encoded = toBase64Url(encoder.encode(JSON.stringify(p)));
190
+ const sig = await signPayload(apiKey, p);
191
+ return `${endpoint}/r/${encoded}?sig=${sig}`;
192
+ }
193
+ var URL_RE = /https?:\/\/[^\s"'<>)\]]+/g;
194
+ async function rewriteUrls(text, config, sessionId, metadata) {
195
+ if (!text)
196
+ return text;
197
+ const redirectPrefix = `${config.endpoint}/r/`;
198
+ const matches = [...text.matchAll(URL_RE)];
199
+ if (matches.length === 0)
200
+ return text;
201
+ const replacements = /* @__PURE__ */ new Map();
202
+ for (const m of matches) {
203
+ const originalUrl = m[0];
204
+ if (originalUrl.startsWith(redirectPrefix))
205
+ continue;
206
+ if (replacements.has(originalUrl))
207
+ continue;
208
+ const payload = {
209
+ u: originalUrl,
210
+ s: sessionId,
211
+ c: config.clientId,
212
+ t: Math.floor(Date.now() / 1e3)
213
+ };
214
+ if (metadata && Object.keys(metadata).length > 0) {
215
+ payload.a = metadata;
216
+ }
217
+ const trackUrl = await buildTrackUrl(config.endpoint, config.apiKey, payload);
218
+ replacements.set(originalUrl, trackUrl);
219
+ }
220
+ let result = text;
221
+ const sorted = [...replacements.entries()].sort((a, b) => b[0].length - a[0].length);
222
+ for (const [original, tracked] of sorted) {
223
+ result = result.split(original).join(tracked);
224
+ }
225
+ return result;
226
+ }
227
+
228
+ // src/session.js
229
+ function truncate(value, maxLen) {
230
+ if (!value || !maxLen || typeof value !== "string")
231
+ return value;
232
+ if (value.length <= maxLen)
233
+ return value;
234
+ return value.slice(0, maxLen) + "...[truncated]";
235
+ }
236
+ var Session = class {
237
+ constructor(clientConfig, { sessionId, metadata } = {}) {
238
+ Object.defineProperty(this, "_config", {
239
+ value: clientConfig,
240
+ enumerable: false
241
+ });
242
+ this.sessionId = sessionId || crypto.randomUUID();
243
+ this._metadata = metadata || {};
244
+ this._reset();
245
+ }
246
+ _reset() {
247
+ this._promptTokens = 0;
248
+ this._completionTokens = 0;
249
+ this._rounds = 0;
250
+ this._toolCalls = [];
251
+ this._model = null;
252
+ this._systemPrompt = null;
253
+ }
254
+ get totalUsage() {
255
+ return {
256
+ prompt_tokens: this._promptTokens,
257
+ completion_tokens: this._completionTokens,
258
+ total_tokens: this._promptTokens + this._completionTokens,
259
+ ai_rounds: this._rounds
260
+ };
261
+ }
262
+ get toolCalls() {
263
+ return this._toolCalls;
264
+ }
265
+ record(rawResponse) {
266
+ const normalized = normalizeResponse(rawResponse);
267
+ const round = this._rounds;
268
+ this._promptTokens += normalized.usage.prompt_tokens;
269
+ this._completionTokens += normalized.usage.completion_tokens;
270
+ this._rounds += 1;
271
+ if (normalized.model) {
272
+ this._model = normalized.model;
273
+ }
274
+ for (const tc of normalized.toolCalls) {
275
+ this._toolCalls.push({ ...tc, round });
276
+ }
277
+ return normalized;
278
+ }
279
+ /**
280
+ * Attach a result summary to the most recent tool call matching `toolName`.
281
+ * Call this after executing a tool to include results in the emitted event.
282
+ */
283
+ recordToolResult(toolName, result) {
284
+ for (let i = this._toolCalls.length - 1; i >= 0; i--) {
285
+ if (this._toolCalls[i].tool === toolName && !this._toolCalls[i].result) {
286
+ this._toolCalls[i].result = result;
287
+ return;
288
+ }
289
+ }
290
+ }
291
+ async emitChatTurn({ userMessage, assistantResponse, turnNumber, messages }) {
292
+ const capture = this._config.captureContent !== false;
293
+ const maxLen = this._config.maxContentLength;
294
+ const attributes = {
295
+ ...this._metadata,
296
+ source: "pentatonic-ai-sdk",
297
+ model: this._model,
298
+ usage: this.totalUsage,
299
+ tool_calls: this._toolCalls.length ? capture ? this._toolCalls : this._toolCalls.map(({ args, ...rest }) => rest) : void 0
300
+ };
301
+ if (capture) {
302
+ attributes.user_message = truncate(userMessage, maxLen);
303
+ attributes.assistant_response = truncate(assistantResponse, maxLen);
304
+ if (this._systemPrompt) {
305
+ attributes.system_prompt = truncate(this._systemPrompt, maxLen);
306
+ }
307
+ if (messages) {
308
+ attributes.messages = messages.map((m) => {
309
+ if (typeof m.content === "string") {
310
+ return { ...m, content: truncate(m.content, maxLen) };
311
+ }
312
+ return m;
313
+ });
314
+ }
315
+ }
316
+ if (turnNumber !== void 0) {
317
+ attributes.turn_number = turnNumber;
318
+ }
319
+ const result = await sendEvent(this._config, {
320
+ eventType: "CHAT_TURN",
321
+ entityType: "conversation",
322
+ data: {
323
+ entity_id: this.sessionId,
324
+ attributes
325
+ }
326
+ });
327
+ this._reset();
328
+ return result;
329
+ }
330
+ async emitToolUse({ tool, args, resultSummary, durationMs, turnNumber }) {
331
+ const capture = this._config.captureContent !== false;
332
+ const maxLen = this._config.maxContentLength;
333
+ const attributes = {
334
+ ...this._metadata,
335
+ source: "pentatonic-ai-sdk",
336
+ tool,
337
+ duration_ms: durationMs,
338
+ turn_number: turnNumber
339
+ };
340
+ if (capture) {
341
+ attributes.args = args;
342
+ attributes.result_summary = truncate(resultSummary, maxLen);
343
+ }
344
+ return sendEvent(this._config, {
345
+ eventType: "TOOL_USE",
346
+ entityType: "conversation",
347
+ data: {
348
+ entity_id: this.sessionId,
349
+ attributes
350
+ }
351
+ });
352
+ }
353
+ async trackUrl(url, { eventType, attributes } = {}) {
354
+ const payload = {
355
+ u: url,
356
+ s: this.sessionId,
357
+ c: this._config.clientId,
358
+ t: Math.floor(Date.now() / 1e3),
359
+ e: eventType || "LINK_CLICK"
360
+ };
361
+ const meta = { ...this._metadata, ...attributes };
362
+ if (Object.keys(meta).length) {
363
+ payload.a = meta;
364
+ }
365
+ return buildTrackUrl(this._config.endpoint, this._config.apiKey, payload);
366
+ }
367
+ async emitSessionStart() {
368
+ return sendEvent(this._config, {
369
+ eventType: "SESSION_START",
370
+ entityType: "conversation",
371
+ data: {
372
+ entity_id: this.sessionId,
373
+ attributes: {
374
+ source: "pentatonic-ai-sdk",
375
+ metadata: this._metadata
376
+ }
377
+ }
378
+ });
379
+ }
380
+ };
381
+
382
+ // src/wrapper.js
383
+ function detectClientType(client) {
384
+ if (client?.chat?.completions?.create)
385
+ return "openai";
386
+ if (client?.messages?.create)
387
+ return "anthropic";
388
+ if (typeof client?.run === "function")
389
+ return "workers-ai";
390
+ return "unknown";
391
+ }
392
+ function wrapClient(clientConfig, client, sessionOpts = {}) {
393
+ sessionOpts._resolvedSessionId = sessionOpts.sessionId || crypto.randomUUID();
394
+ sessionOpts._session = new Session(clientConfig, {
395
+ sessionId: sessionOpts._resolvedSessionId,
396
+ metadata: sessionOpts.metadata
397
+ });
398
+ const type = detectClientType(client);
399
+ if (type === "openai")
400
+ return wrapOpenAI(clientConfig, client, sessionOpts);
401
+ if (type === "anthropic")
402
+ return wrapAnthropic(clientConfig, client, sessionOpts);
403
+ if (type === "workers-ai")
404
+ return wrapWorkersAI(clientConfig, client, sessionOpts);
405
+ throw new Error(
406
+ "Unsupported client: expected OpenAI (chat.completions.create), Anthropic (messages.create), or Workers AI (run) client"
407
+ );
408
+ }
409
+ function wrapOpenAI(clientConfig, client, sessionOpts) {
410
+ return new Proxy(client, {
411
+ get(target, prop) {
412
+ if (prop === "chat")
413
+ return wrapOpenAIChat(clientConfig, target.chat, target, sessionOpts);
414
+ if (prop === "sessionId")
415
+ return sessionOpts._resolvedSessionId;
416
+ if (prop === "tesSession")
417
+ return sessionOpts._session;
418
+ if (prop === "session")
419
+ return (opts) => new OpenAISession(clientConfig, target, opts);
420
+ return target[prop];
421
+ }
422
+ });
423
+ }
424
+ function wrapOpenAIChat(clientConfig, chat, client, sessionOpts) {
425
+ return new Proxy(chat, {
426
+ get(target, prop) {
427
+ if (prop === "completions")
428
+ return wrapOpenAICompletions(
429
+ clientConfig,
430
+ target.completions,
431
+ client,
432
+ sessionOpts
433
+ );
434
+ return target[prop];
435
+ }
436
+ });
437
+ }
438
+ function wrapOpenAICompletions(clientConfig, completions, client, sessionOpts) {
439
+ return new Proxy(completions, {
440
+ get(target, prop) {
441
+ if (prop === "create") {
442
+ return async (params) => {
443
+ const result = await target.create(params);
444
+ const content = result.choices?.[0]?.message?.content;
445
+ if (content) {
446
+ result.choices[0].message.content = await rewriteUrls(
447
+ content,
448
+ clientConfig,
449
+ sessionOpts._resolvedSessionId,
450
+ sessionOpts.metadata
451
+ );
452
+ }
453
+ fireAndForgetEmit(
454
+ clientConfig,
455
+ sessionOpts,
456
+ params.messages,
457
+ result
458
+ );
459
+ return result;
460
+ };
461
+ }
462
+ return target[prop];
463
+ }
464
+ });
465
+ }
466
+ var OpenAISession = class extends Session {
467
+ constructor(clientConfig, client, opts) {
468
+ super(clientConfig, opts);
469
+ this._client = client;
470
+ }
471
+ async chat(params) {
472
+ const result = await this._client.chat.completions.create(params);
473
+ this.record(result);
474
+ return result;
475
+ }
476
+ };
477
+ function wrapAnthropic(clientConfig, client, sessionOpts) {
478
+ return new Proxy(client, {
479
+ get(target, prop) {
480
+ if (prop === "messages")
481
+ return wrapAnthropicMessages(
482
+ clientConfig,
483
+ target.messages,
484
+ target,
485
+ sessionOpts
486
+ );
487
+ if (prop === "sessionId")
488
+ return sessionOpts._resolvedSessionId;
489
+ if (prop === "tesSession")
490
+ return sessionOpts._session;
491
+ if (prop === "session")
492
+ return (opts) => new AnthropicSession(clientConfig, target, opts);
493
+ return target[prop];
494
+ }
495
+ });
496
+ }
497
+ function wrapAnthropicMessages(clientConfig, messages, client, sessionOpts) {
498
+ return new Proxy(messages, {
499
+ get(target, prop) {
500
+ if (prop === "create") {
501
+ return async (params) => {
502
+ const result = await target.create(params);
503
+ if (Array.isArray(result.content)) {
504
+ for (const block of result.content) {
505
+ if (block.type === "text" && block.text) {
506
+ block.text = await rewriteUrls(
507
+ block.text,
508
+ clientConfig,
509
+ sessionOpts._resolvedSessionId,
510
+ sessionOpts.metadata
511
+ );
512
+ }
513
+ }
514
+ }
515
+ fireAndForgetEmit(
516
+ clientConfig,
517
+ sessionOpts,
518
+ params.messages,
519
+ result
520
+ );
521
+ return result;
522
+ };
523
+ }
524
+ return target[prop];
525
+ }
526
+ });
527
+ }
528
+ var AnthropicSession = class extends Session {
529
+ constructor(clientConfig, client, opts) {
530
+ super(clientConfig, opts);
531
+ this._client = client;
532
+ }
533
+ async chat(params) {
534
+ const result = await this._client.messages.create(params);
535
+ this.record(result);
536
+ return result;
537
+ }
538
+ };
539
+ function wrapWorkersAI(clientConfig, aiBinding, sessionOpts) {
540
+ return new Proxy(aiBinding, {
541
+ get(target, prop) {
542
+ if (prop === "run") {
543
+ return async (model, params, ...rest) => {
544
+ const result = await target.run(model, params, ...rest);
545
+ if (result.response) {
546
+ result.response = await rewriteUrls(
547
+ result.response,
548
+ clientConfig,
549
+ sessionOpts._resolvedSessionId,
550
+ sessionOpts.metadata
551
+ );
552
+ }
553
+ fireAndForgetEmit(
554
+ clientConfig,
555
+ sessionOpts,
556
+ params?.messages,
557
+ result,
558
+ model
559
+ );
560
+ return result;
561
+ };
562
+ }
563
+ if (prop === "sessionId")
564
+ return sessionOpts._resolvedSessionId;
565
+ if (prop === "tesSession")
566
+ return sessionOpts._session;
567
+ if (prop === "session")
568
+ return (opts) => new WorkersAISession(clientConfig, target, opts);
569
+ return target[prop];
570
+ }
571
+ });
572
+ }
573
+ var WorkersAISession = class extends Session {
574
+ constructor(clientConfig, aiBinding, opts) {
575
+ super(clientConfig, opts);
576
+ this._ai = aiBinding;
577
+ }
578
+ async chat(model, params, ...rest) {
579
+ const result = await this._ai.run(model, params, ...rest);
580
+ this.record(result);
581
+ return result;
582
+ }
583
+ };
584
+ function extractToolResults(session, messages) {
585
+ if (!messages?.length || !session._toolCalls.length)
586
+ return;
587
+ const idToName = /* @__PURE__ */ new Map();
588
+ for (const msg of messages) {
589
+ if (msg.role === "assistant" && msg.tool_calls) {
590
+ for (const tc of msg.tool_calls) {
591
+ const id = tc.id || tc.tool_call_id;
592
+ const name = tc.function?.name || tc.name;
593
+ if (id && name)
594
+ idToName.set(id, name);
595
+ }
596
+ }
597
+ }
598
+ for (const msg of messages) {
599
+ if (msg.role !== "tool" || !msg.content)
600
+ continue;
601
+ const callId = msg.tool_call_id;
602
+ const toolName = callId ? idToName.get(callId) : null;
603
+ for (const tc of session._toolCalls) {
604
+ if (tc.result)
605
+ continue;
606
+ if (toolName && tc.tool !== toolName)
607
+ continue;
608
+ try {
609
+ const parsed = JSON.parse(msg.content);
610
+ if (Array.isArray(parsed)) {
611
+ tc.result = { count: parsed.length, sample: parsed.slice(0, 3) };
612
+ } else {
613
+ tc.result = parsed;
614
+ }
615
+ } catch {
616
+ tc.result = msg.content;
617
+ }
618
+ break;
619
+ }
620
+ }
621
+ }
622
+ function fireAndForgetEmit(clientConfig, sessionOpts, messages, result, model) {
623
+ const session = sessionOpts._session;
624
+ const normalized = session.record(result);
625
+ extractToolResults(session, messages);
626
+ if (!session._systemPrompt && messages?.length) {
627
+ const systemMsg = messages.find((m) => m.role === "system");
628
+ if (systemMsg?.content) {
629
+ session._systemPrompt = systemMsg.content;
630
+ }
631
+ }
632
+ if (model && !normalized.model) {
633
+ session._model = model;
634
+ }
635
+ if (sessionOpts.autoEmit === false) {
636
+ return;
637
+ }
638
+ if (!normalized.content && normalized.toolCalls.length > 0) {
639
+ return;
640
+ }
641
+ const userMsg = messages?.filter?.((m) => m.role === "user")?.pop()?.content || "";
642
+ const assistantMsg = normalized.content || "";
643
+ const emitPromise = session.emitChatTurn({ userMessage: userMsg, assistantResponse: assistantMsg }).catch((err) => console.error("[pentatonic-ai] emit failed:", err.message));
644
+ if (typeof sessionOpts.waitUntil === "function") {
645
+ sessionOpts.waitUntil(emitPromise);
646
+ }
647
+ }
648
+
649
+ // src/client.js
650
+ var TESClient = class {
651
+ constructor({ clientId, apiKey, endpoint, headers, userId, captureContent = true, maxContentLength = 4096 }) {
652
+ if (!clientId)
653
+ throw new Error("clientId is required");
654
+ if (!apiKey)
655
+ throw new Error("apiKey is required");
656
+ if (!endpoint)
657
+ throw new Error("endpoint is required");
658
+ const cleanEndpoint = endpoint.replace(/\/$/, "");
659
+ const isLocalDev = /^http:\/\/localhost(:\d+)?(\/|$)/.test(cleanEndpoint) || /^http:\/\/127\.0\.0\.1(:\d+)?(\/|$)/.test(cleanEndpoint);
660
+ if (!cleanEndpoint.startsWith("https://") && !isLocalDev) {
661
+ throw new Error(
662
+ "endpoint must use https:// (http:// is only allowed for localhost)"
663
+ );
664
+ }
665
+ this.clientId = clientId;
666
+ this.endpoint = cleanEndpoint;
667
+ this.userId = userId || null;
668
+ this.captureContent = captureContent;
669
+ this.maxContentLength = maxContentLength;
670
+ Object.defineProperty(this, "_apiKey", {
671
+ value: apiKey,
672
+ enumerable: false,
673
+ writable: false
674
+ });
675
+ Object.defineProperty(this, "_headers", {
676
+ value: headers || {},
677
+ enumerable: false,
678
+ writable: false
679
+ });
680
+ }
681
+ get _config() {
682
+ return {
683
+ clientId: this.clientId,
684
+ apiKey: this._apiKey,
685
+ endpoint: this.endpoint,
686
+ headers: this._headers,
687
+ userId: this.userId,
688
+ captureContent: this.captureContent,
689
+ maxContentLength: this.maxContentLength
690
+ };
691
+ }
692
+ session(opts) {
693
+ return new Session(this._config, opts);
694
+ }
695
+ wrap(client, { sessionId, userId, metadata, autoEmit = true, waitUntil } = {}) {
696
+ const config = userId ? { ...this._config, userId } : this._config;
697
+ return wrapClient(config, client, { sessionId, metadata, autoEmit, waitUntil });
698
+ }
699
+ };