@starcite/sdk 0.0.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 ADDED
@@ -0,0 +1,608 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ StarciteApiError: () => StarciteApiError,
24
+ StarciteClient: () => StarciteClient,
25
+ StarciteConnectionError: () => StarciteConnectionError,
26
+ StarciteError: () => StarciteError,
27
+ StarciteSession: () => StarciteSession,
28
+ createStarciteClient: () => createStarciteClient,
29
+ normalizeBaseUrl: () => normalizeBaseUrl,
30
+ starcite: () => starcite
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+
34
+ // src/client.ts
35
+ var import_zod2 = require("zod");
36
+
37
+ // src/errors.ts
38
+ var StarciteError = class extends Error {
39
+ constructor(message) {
40
+ super(message);
41
+ this.name = "StarciteError";
42
+ }
43
+ };
44
+ var StarciteApiError = class extends StarciteError {
45
+ /** HTTP status code returned by the API. */
46
+ status;
47
+ /** Stable API error code (or synthesized `http_<status>` fallback). */
48
+ code;
49
+ /** Parsed API error payload when available. */
50
+ payload;
51
+ constructor(message, status, code, payload) {
52
+ super(message);
53
+ this.name = "StarciteApiError";
54
+ this.status = status;
55
+ this.code = code;
56
+ this.payload = payload;
57
+ }
58
+ };
59
+ var StarciteConnectionError = class extends StarciteError {
60
+ constructor(message) {
61
+ super(message);
62
+ this.name = "StarciteConnectionError";
63
+ }
64
+ };
65
+
66
+ // src/types.ts
67
+ var import_zod = require("zod");
68
+ var ArbitraryObjectSchema = import_zod.z.record(import_zod.z.unknown());
69
+ var CreateSessionInputSchema = import_zod.z.object({
70
+ id: import_zod.z.string().optional(),
71
+ title: import_zod.z.string().optional(),
72
+ metadata: ArbitraryObjectSchema.optional()
73
+ });
74
+ var SessionRecordSchema = import_zod.z.object({
75
+ id: import_zod.z.string(),
76
+ title: import_zod.z.string().nullable().optional(),
77
+ metadata: ArbitraryObjectSchema,
78
+ last_seq: import_zod.z.number().int().nonnegative(),
79
+ created_at: import_zod.z.string(),
80
+ updated_at: import_zod.z.string()
81
+ });
82
+ var SessionListItemSchema = import_zod.z.object({
83
+ id: import_zod.z.string(),
84
+ title: import_zod.z.string().nullable().optional(),
85
+ metadata: ArbitraryObjectSchema,
86
+ created_at: import_zod.z.string()
87
+ });
88
+ var SessionListPageSchema = import_zod.z.object({
89
+ sessions: import_zod.z.array(SessionListItemSchema),
90
+ next_cursor: import_zod.z.string().nullable()
91
+ });
92
+ var AppendEventRequestSchema = import_zod.z.object({
93
+ type: import_zod.z.string().min(1),
94
+ payload: ArbitraryObjectSchema,
95
+ actor: import_zod.z.string().min(1),
96
+ producer_id: import_zod.z.string().min(1),
97
+ producer_seq: import_zod.z.number().int().positive(),
98
+ source: import_zod.z.string().optional(),
99
+ metadata: ArbitraryObjectSchema.optional(),
100
+ refs: ArbitraryObjectSchema.optional(),
101
+ idempotency_key: import_zod.z.string().optional(),
102
+ expected_seq: import_zod.z.number().int().nonnegative().optional()
103
+ });
104
+ var AppendEventResponseSchema = import_zod.z.object({
105
+ seq: import_zod.z.number().int().nonnegative(),
106
+ last_seq: import_zod.z.number().int().nonnegative(),
107
+ deduped: import_zod.z.boolean()
108
+ });
109
+ var TailEventSchema = import_zod.z.object({
110
+ seq: import_zod.z.number().int().nonnegative(),
111
+ type: import_zod.z.string().min(1),
112
+ payload: ArbitraryObjectSchema,
113
+ actor: import_zod.z.string().min(1),
114
+ producer_id: import_zod.z.string().min(1),
115
+ producer_seq: import_zod.z.number().int().positive(),
116
+ source: import_zod.z.string().optional(),
117
+ metadata: ArbitraryObjectSchema.optional(),
118
+ refs: ArbitraryObjectSchema.optional(),
119
+ idempotency_key: import_zod.z.string().nullable().optional(),
120
+ inserted_at: import_zod.z.string().optional()
121
+ });
122
+ var SessionEventInternalSchema = TailEventSchema.extend({
123
+ agent: import_zod.z.string().optional(),
124
+ text: import_zod.z.string().optional()
125
+ });
126
+ var SessionAppendInputSchema = import_zod.z.object({
127
+ agent: import_zod.z.string().trim().min(1),
128
+ producerId: import_zod.z.string().trim().min(1),
129
+ producerSeq: import_zod.z.number().int().positive(),
130
+ text: import_zod.z.string().optional(),
131
+ payload: ArbitraryObjectSchema.optional(),
132
+ type: import_zod.z.string().optional(),
133
+ source: import_zod.z.string().optional(),
134
+ metadata: ArbitraryObjectSchema.optional(),
135
+ refs: ArbitraryObjectSchema.optional(),
136
+ idempotencyKey: import_zod.z.string().optional(),
137
+ expectedSeq: import_zod.z.number().int().nonnegative().optional()
138
+ }).refine((value) => !!(value.text || value.payload), {
139
+ message: "append() requires either 'text' or an object 'payload'"
140
+ });
141
+ var StarciteErrorPayloadSchema = import_zod.z.object({
142
+ error: import_zod.z.string().optional(),
143
+ message: import_zod.z.string().optional()
144
+ }).catchall(import_zod.z.unknown());
145
+
146
+ // src/client.ts
147
+ var DEFAULT_BASE_URL = typeof process !== "undefined" && process.env.STARCITE_BASE_URL ? process.env.STARCITE_BASE_URL : "http://localhost:4000";
148
+ var TRAILING_SLASHES_REGEX = /\/+$/;
149
+ var BEARER_PREFIX_REGEX = /^bearer\s+/i;
150
+ var TailFrameSchema = import_zod2.z.string().transform((frame, context) => {
151
+ try {
152
+ return JSON.parse(frame);
153
+ } catch {
154
+ context.addIssue({
155
+ code: import_zod2.z.ZodIssueCode.custom,
156
+ message: "Tail frame was not valid JSON"
157
+ });
158
+ return import_zod2.z.NEVER;
159
+ }
160
+ }).pipe(TailEventSchema);
161
+ var AsyncQueue = class {
162
+ items = [];
163
+ waiters = [];
164
+ settled = false;
165
+ push(value) {
166
+ if (this.settled) {
167
+ return;
168
+ }
169
+ this.enqueue({ type: "value", value });
170
+ }
171
+ close() {
172
+ if (this.settled) {
173
+ return;
174
+ }
175
+ this.settled = true;
176
+ this.enqueue({ type: "done" });
177
+ }
178
+ fail(error) {
179
+ if (this.settled) {
180
+ return;
181
+ }
182
+ this.settled = true;
183
+ this.enqueue({ type: "error", error: toError(error) });
184
+ }
185
+ async next() {
186
+ const item = this.items.shift() ?? await new Promise((resolve) => {
187
+ this.waiters.push(resolve);
188
+ });
189
+ if (item.type === "value") {
190
+ return { value: item.value, done: false };
191
+ }
192
+ if (item.type === "done") {
193
+ return { value: void 0, done: true };
194
+ }
195
+ throw item.error;
196
+ }
197
+ enqueue(item) {
198
+ const waiter = this.waiters.shift();
199
+ if (waiter) {
200
+ waiter(item);
201
+ return;
202
+ }
203
+ this.items.push(item);
204
+ }
205
+ };
206
+ function normalizeBaseUrl(baseUrl) {
207
+ const trimmed = baseUrl.trim().replace(TRAILING_SLASHES_REGEX, "");
208
+ return trimmed.endsWith("/v1") ? trimmed : `${trimmed}/v1`;
209
+ }
210
+ function toWebSocketBaseUrl(apiBaseUrl) {
211
+ if (apiBaseUrl.startsWith("https://")) {
212
+ return `wss://${apiBaseUrl.slice("https://".length)}`;
213
+ }
214
+ if (apiBaseUrl.startsWith("http://")) {
215
+ return `ws://${apiBaseUrl.slice("http://".length)}`;
216
+ }
217
+ throw new StarciteError(
218
+ `Invalid Starcite base URL '${apiBaseUrl}'. Use http:// or https://.`
219
+ );
220
+ }
221
+ function defaultWebSocketFactory(url, options = {}) {
222
+ if (typeof WebSocket === "undefined") {
223
+ throw new StarciteError(
224
+ "WebSocket is not available in this runtime. Provide websocketFactory in StarciteClientOptions."
225
+ );
226
+ }
227
+ const headers = new Headers(options.headers);
228
+ if (!hasAnyHeaders(headers)) {
229
+ return new WebSocket(url);
230
+ }
231
+ const headerObject = Object.fromEntries(headers.entries());
232
+ try {
233
+ return new WebSocket(url, { headers: headerObject });
234
+ } catch {
235
+ throw new StarciteError(
236
+ "This runtime cannot set WebSocket upgrade headers with the default factory. Provide websocketFactory in StarciteClientOptions."
237
+ );
238
+ }
239
+ }
240
+ function defaultFetch(input, init) {
241
+ if (typeof fetch === "undefined") {
242
+ throw new StarciteError(
243
+ "fetch is not available in this runtime. Provide fetch in StarciteClientOptions."
244
+ );
245
+ }
246
+ return fetch(input, init);
247
+ }
248
+ function toError(error) {
249
+ if (error instanceof Error) {
250
+ return error;
251
+ }
252
+ return new Error(typeof error === "string" ? error : "Unknown error");
253
+ }
254
+ function hasAnyHeaders(headers) {
255
+ for (const _ of headers.keys()) {
256
+ return true;
257
+ }
258
+ return false;
259
+ }
260
+ function formatAuthorizationHeader(apiKey) {
261
+ const normalized = apiKey.trim();
262
+ if (normalized.length === 0) {
263
+ throw new StarciteError("apiKey cannot be empty");
264
+ }
265
+ if (BEARER_PREFIX_REGEX.test(normalized)) {
266
+ return normalized;
267
+ }
268
+ return `Bearer ${normalized}`;
269
+ }
270
+ function parseEventFrame(data) {
271
+ const result = TailFrameSchema.safeParse(data);
272
+ if (!result.success) {
273
+ const reason = result.error.issues[0]?.message ?? "Tail frame did not match schema";
274
+ throw new StarciteConnectionError(reason);
275
+ }
276
+ return result.data;
277
+ }
278
+ function getEventData(event) {
279
+ if (event && typeof event === "object" && "data" in event) {
280
+ return event.data;
281
+ }
282
+ return void 0;
283
+ }
284
+ function agentFromActor(actor) {
285
+ if (actor.startsWith("agent:")) {
286
+ return actor.slice("agent:".length);
287
+ }
288
+ return void 0;
289
+ }
290
+ function toSessionEvent(event) {
291
+ const agent = agentFromActor(event.actor);
292
+ const text = typeof event.payload.text === "string" ? event.payload.text : void 0;
293
+ return {
294
+ ...event,
295
+ agent,
296
+ text
297
+ };
298
+ }
299
+ var StarciteSession = class {
300
+ /** Session identifier. */
301
+ id;
302
+ /** Optional session record captured at creation time. */
303
+ record;
304
+ client;
305
+ constructor(client, id, record) {
306
+ this.client = client;
307
+ this.id = id;
308
+ this.record = record;
309
+ }
310
+ /**
311
+ * Appends a high-level agent event to this session.
312
+ *
313
+ * Automatically prefixes `agent` as `agent:<name>` when needed.
314
+ */
315
+ append(input) {
316
+ const parsed = SessionAppendInputSchema.parse(input);
317
+ const actor = parsed.agent.startsWith("agent:") ? parsed.agent : `agent:${parsed.agent}`;
318
+ return this.client.appendEvent(this.id, {
319
+ type: parsed.type ?? "content",
320
+ payload: parsed.payload ?? { text: parsed.text },
321
+ actor,
322
+ producer_id: parsed.producerId,
323
+ producer_seq: parsed.producerSeq,
324
+ source: parsed.source ?? "agent",
325
+ metadata: parsed.metadata,
326
+ refs: parsed.refs,
327
+ idempotency_key: parsed.idempotencyKey,
328
+ expected_seq: parsed.expectedSeq
329
+ });
330
+ }
331
+ /**
332
+ * Appends a raw event payload as-is.
333
+ */
334
+ appendRaw(input) {
335
+ return this.client.appendEvent(this.id, input);
336
+ }
337
+ /**
338
+ * Streams transformed session events with SDK convenience fields (`agent`, `text`).
339
+ */
340
+ tail(options = {}) {
341
+ return this.client.tailEvents(this.id, options);
342
+ }
343
+ /**
344
+ * Streams raw tail events returned by the API.
345
+ */
346
+ tailRaw(options = {}) {
347
+ return this.client.tailRawEvents(this.id, options);
348
+ }
349
+ };
350
+ var StarciteClient = class {
351
+ /** Normalized API base URL ending with `/v1`. */
352
+ baseUrl;
353
+ websocketBaseUrl;
354
+ fetchFn;
355
+ headers;
356
+ websocketFactory;
357
+ /**
358
+ * Creates a new client instance.
359
+ */
360
+ constructor(options = {}) {
361
+ this.baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_BASE_URL);
362
+ this.websocketBaseUrl = toWebSocketBaseUrl(this.baseUrl);
363
+ this.fetchFn = options.fetch ?? defaultFetch;
364
+ this.headers = new Headers(options.headers);
365
+ if (options.apiKey !== void 0) {
366
+ this.headers.set(
367
+ "authorization",
368
+ formatAuthorizationHeader(options.apiKey)
369
+ );
370
+ }
371
+ this.websocketFactory = options.websocketFactory ?? defaultWebSocketFactory;
372
+ }
373
+ /**
374
+ * Returns a session helper bound to an existing session id.
375
+ */
376
+ session(sessionId, record) {
377
+ return new StarciteSession(this, sessionId, record);
378
+ }
379
+ /**
380
+ * Creates a new session and returns a bound `StarciteSession` helper.
381
+ */
382
+ async create(input = {}) {
383
+ const record = await this.createSession(input);
384
+ return this.session(record.id, record);
385
+ }
386
+ /**
387
+ * Creates a new session and returns the raw session record.
388
+ */
389
+ createSession(input = {}) {
390
+ const payload = CreateSessionInputSchema.parse(input);
391
+ return this.request(
392
+ "/sessions",
393
+ {
394
+ method: "POST",
395
+ body: JSON.stringify(payload)
396
+ },
397
+ SessionRecordSchema
398
+ );
399
+ }
400
+ /**
401
+ * Lists sessions from the archive-backed catalog.
402
+ */
403
+ listSessions(options = {}) {
404
+ const query = new URLSearchParams();
405
+ if (options.limit !== void 0) {
406
+ if (!Number.isInteger(options.limit) || options.limit <= 0) {
407
+ throw new StarciteError(
408
+ "listSessions() limit must be a positive integer"
409
+ );
410
+ }
411
+ query.set("limit", `${options.limit}`);
412
+ }
413
+ if (options.cursor !== void 0) {
414
+ if (options.cursor.trim().length === 0) {
415
+ throw new StarciteError("listSessions() cursor cannot be empty");
416
+ }
417
+ query.set("cursor", options.cursor);
418
+ }
419
+ if (options.metadata !== void 0) {
420
+ for (const [key, value] of Object.entries(options.metadata)) {
421
+ if (key.trim().length === 0 || value.trim().length === 0) {
422
+ throw new StarciteError(
423
+ "listSessions() metadata filters must be non-empty strings"
424
+ );
425
+ }
426
+ query.set(`metadata.${key}`, value);
427
+ }
428
+ }
429
+ const suffix = query.size > 0 ? `?${query.toString()}` : "";
430
+ return this.request(
431
+ `/sessions${suffix}`,
432
+ {
433
+ method: "GET"
434
+ },
435
+ SessionListPageSchema
436
+ );
437
+ }
438
+ /**
439
+ * Appends a raw event payload to a specific session.
440
+ */
441
+ appendEvent(sessionId, input) {
442
+ const payload = AppendEventRequestSchema.parse(input);
443
+ return this.request(
444
+ `/sessions/${encodeURIComponent(sessionId)}/append`,
445
+ {
446
+ method: "POST",
447
+ body: JSON.stringify(payload)
448
+ },
449
+ AppendEventResponseSchema
450
+ );
451
+ }
452
+ /**
453
+ * Opens a WebSocket tail stream and yields raw events.
454
+ */
455
+ async *tailRawEvents(sessionId, options = {}) {
456
+ const queue = new AsyncQueue();
457
+ const cursor = options.cursor ?? 0;
458
+ if (!Number.isInteger(cursor) || cursor < 0) {
459
+ throw new StarciteError("tail() cursor must be a non-negative integer");
460
+ }
461
+ const wsUrl = `${this.websocketBaseUrl}/sessions/${encodeURIComponent(
462
+ sessionId
463
+ )}/tail?cursor=${cursor}`;
464
+ const websocketHeaders = new Headers();
465
+ const authorization = this.headers.get("authorization");
466
+ if (authorization) {
467
+ websocketHeaders.set("authorization", authorization);
468
+ }
469
+ const socket = this.websocketFactory(
470
+ wsUrl,
471
+ hasAnyHeaders(websocketHeaders) ? {
472
+ headers: websocketHeaders
473
+ } : void 0
474
+ );
475
+ const onMessage = (event) => {
476
+ try {
477
+ const parsed = parseEventFrame(getEventData(event));
478
+ if (options.agent && agentFromActor(parsed.actor) !== options.agent) {
479
+ return;
480
+ }
481
+ queue.push(parsed);
482
+ } catch (error) {
483
+ queue.fail(error);
484
+ }
485
+ };
486
+ const onError = () => {
487
+ queue.fail(
488
+ new StarciteConnectionError(
489
+ `Tail connection failed for session '${sessionId}'`
490
+ )
491
+ );
492
+ };
493
+ const onClose = () => {
494
+ queue.close();
495
+ };
496
+ socket.addEventListener("message", onMessage);
497
+ socket.addEventListener("error", onError);
498
+ socket.addEventListener("close", onClose);
499
+ const onAbort = () => {
500
+ queue.close();
501
+ socket.close(1e3, "aborted");
502
+ };
503
+ if (options.signal) {
504
+ if (options.signal.aborted) {
505
+ onAbort();
506
+ } else {
507
+ options.signal.addEventListener("abort", onAbort, { once: true });
508
+ }
509
+ }
510
+ try {
511
+ while (true) {
512
+ const next = await queue.next();
513
+ if (next.done) {
514
+ break;
515
+ }
516
+ yield next.value;
517
+ }
518
+ } finally {
519
+ socket.removeEventListener("message", onMessage);
520
+ socket.removeEventListener("error", onError);
521
+ socket.removeEventListener("close", onClose);
522
+ if (options.signal) {
523
+ options.signal.removeEventListener("abort", onAbort);
524
+ }
525
+ socket.close(1e3, "finished");
526
+ }
527
+ }
528
+ /**
529
+ * Opens a WebSocket tail stream and yields transformed session events.
530
+ */
531
+ async *tailEvents(sessionId, options = {}) {
532
+ for await (const rawEvent of this.tailRawEvents(sessionId, options)) {
533
+ yield toSessionEvent(rawEvent);
534
+ }
535
+ }
536
+ async request(path, init, schema) {
537
+ const headers = new Headers(this.headers);
538
+ if (!headers.has("content-type")) {
539
+ headers.set("content-type", "application/json");
540
+ }
541
+ if (init.headers) {
542
+ const perRequestHeaders = new Headers(init.headers);
543
+ for (const [key, value] of perRequestHeaders.entries()) {
544
+ headers.set(key, value);
545
+ }
546
+ }
547
+ let response;
548
+ try {
549
+ response = await this.fetchFn(`${this.baseUrl}${path}`, {
550
+ ...init,
551
+ headers
552
+ });
553
+ } catch (error) {
554
+ const rootCause = toError(error).message;
555
+ throw new StarciteConnectionError(
556
+ `Failed to connect to Starcite at ${this.baseUrl}: ${rootCause}`
557
+ );
558
+ }
559
+ if (!response.ok) {
560
+ const payload = await tryParseJson(response);
561
+ const code = typeof payload?.error === "string" ? payload.error : `http_${response.status}`;
562
+ const message = typeof payload?.message === "string" ? payload.message : response.statusText;
563
+ throw new StarciteApiError(message, response.status, code, payload);
564
+ }
565
+ if (response.status === 204) {
566
+ return void 0;
567
+ }
568
+ const responseBody = await response.json();
569
+ if (!schema) {
570
+ return responseBody;
571
+ }
572
+ const parsed = schema.safeParse(responseBody);
573
+ if (!parsed.success) {
574
+ const issue = parsed.error.issues[0]?.message ?? "invalid response";
575
+ throw new StarciteConnectionError(
576
+ `Received unexpected response payload from Starcite: ${issue}`
577
+ );
578
+ }
579
+ return parsed.data;
580
+ }
581
+ };
582
+ async function tryParseJson(response) {
583
+ try {
584
+ const parsed = await response.json();
585
+ const result = StarciteErrorPayloadSchema.safeParse(parsed);
586
+ return result.success ? result.data : null;
587
+ } catch {
588
+ return null;
589
+ }
590
+ }
591
+
592
+ // src/index.ts
593
+ function createStarciteClient(options = {}) {
594
+ return new StarciteClient(options);
595
+ }
596
+ var starcite = createStarciteClient();
597
+ // Annotate the CommonJS export names for ESM import in node:
598
+ 0 && (module.exports = {
599
+ StarciteApiError,
600
+ StarciteClient,
601
+ StarciteConnectionError,
602
+ StarciteError,
603
+ StarciteSession,
604
+ createStarciteClient,
605
+ normalizeBaseUrl,
606
+ starcite
607
+ });
608
+ //# sourceMappingURL=index.cjs.map