@prefactor/core 0.1.1 → 0.2.0

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 (62) hide show
  1. package/README.md +35 -2
  2. package/dist/agent/instance-manager.d.ts +19 -0
  3. package/dist/agent/instance-manager.d.ts.map +1 -0
  4. package/dist/agent/instance-manager.js +71 -0
  5. package/dist/agent/instance-manager.js.map +1 -0
  6. package/dist/agent/schema-registry.d.ts +9 -0
  7. package/dist/agent/schema-registry.d.ts.map +1 -0
  8. package/dist/agent/schema-registry.js +16 -0
  9. package/dist/agent/schema-registry.js.map +1 -0
  10. package/dist/config.d.ts +49 -25
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +13 -7
  13. package/dist/config.js.map +1 -1
  14. package/dist/create-core.d.ts +12 -0
  15. package/dist/create-core.d.ts.map +1 -0
  16. package/dist/create-core.js +49 -0
  17. package/dist/create-core.js.map +1 -0
  18. package/dist/index.cjs +390 -134
  19. package/dist/index.cjs.map +14 -9
  20. package/dist/index.d.ts +7 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +391 -135
  23. package/dist/index.js.map +14 -9
  24. package/dist/queue/actions.d.ts +35 -0
  25. package/dist/queue/actions.d.ts.map +1 -0
  26. package/dist/queue/actions.js +2 -0
  27. package/dist/queue/actions.js.map +1 -0
  28. package/dist/queue/base.d.ts +7 -0
  29. package/dist/queue/base.d.ts.map +1 -0
  30. package/dist/queue/base.js +2 -0
  31. package/dist/queue/base.js.map +1 -0
  32. package/dist/queue/in-memory.d.ts +9 -0
  33. package/dist/queue/in-memory.d.ts.map +1 -0
  34. package/dist/queue/in-memory.js +18 -0
  35. package/dist/queue/in-memory.js.map +1 -0
  36. package/dist/tracing/context.d.ts +12 -0
  37. package/dist/tracing/context.d.ts.map +1 -1
  38. package/dist/tracing/context.js +41 -5
  39. package/dist/tracing/context.js.map +1 -1
  40. package/dist/tracing/span.d.ts +5 -0
  41. package/dist/tracing/span.d.ts.map +1 -1
  42. package/dist/tracing/span.js +29 -0
  43. package/dist/tracing/span.js.map +1 -1
  44. package/dist/tracing/tracer.d.ts +7 -17
  45. package/dist/tracing/tracer.d.ts.map +1 -1
  46. package/dist/tracing/tracer.js +20 -39
  47. package/dist/tracing/tracer.js.map +1 -1
  48. package/dist/transport/base.d.ts +2 -22
  49. package/dist/transport/base.d.ts.map +1 -1
  50. package/dist/transport/http.d.ts +5 -28
  51. package/dist/transport/http.d.ts.map +1 -1
  52. package/dist/transport/http.js +76 -110
  53. package/dist/transport/http.js.map +1 -1
  54. package/dist/transport/stdio.d.ts +4 -16
  55. package/dist/transport/stdio.d.ts.map +1 -1
  56. package/dist/transport/stdio.js +14 -29
  57. package/dist/transport/stdio.js.map +1 -1
  58. package/dist/transport/worker.d.ts +22 -0
  59. package/dist/transport/worker.d.ts.map +1 -0
  60. package/dist/transport/worker.js +85 -0
  61. package/dist/transport/worker.js.map +1 -0
  62. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -32,6 +32,7 @@ __export(exports_src, {
32
32
  truncateString: () => truncateString,
33
33
  serializeValue: () => serializeValue,
34
34
  getLogger: () => getLogger,
35
+ createCore: () => createCore,
35
36
  createConfig: () => createConfig,
36
37
  configureLogging: () => configureLogging,
37
38
  Tracer: () => Tracer,
@@ -39,24 +40,115 @@ __export(exports_src, {
39
40
  SpanType: () => SpanType,
40
41
  SpanStatus: () => SpanStatus,
41
42
  SpanContext: () => SpanContext,
43
+ SchemaRegistry: () => SchemaRegistry,
42
44
  PartialHttpConfigSchema: () => PartialHttpConfigSchema,
45
+ InMemoryQueue: () => InMemoryQueue,
43
46
  HttpTransportConfigSchema: () => HttpTransportConfigSchema,
44
47
  HttpTransport: () => HttpTransport,
45
- ConfigSchema: () => ConfigSchema
48
+ DEFAULT_AGENT_SCHEMA: () => DEFAULT_AGENT_SCHEMA,
49
+ ConfigSchema: () => ConfigSchema,
50
+ AgentInstanceManager: () => AgentInstanceManager
46
51
  });
47
52
  module.exports = __toCommonJS(exports_src);
48
53
 
54
+ // packages/core/src/agent/instance-manager.ts
55
+ var import_node_util = require("node:util");
56
+
57
+ // packages/core/src/agent/schema-registry.ts
58
+ class SchemaRegistry {
59
+ schemas = new Map;
60
+ register(schema) {
61
+ this.schemas.set(this.getKey(schema.schemaName, schema.schemaIdentifier), schema);
62
+ }
63
+ has(schemaName, schemaIdentifier) {
64
+ return this.schemas.has(this.getKey(schemaName, schemaIdentifier));
65
+ }
66
+ get(schemaName, schemaIdentifier) {
67
+ return this.schemas.get(this.getKey(schemaName, schemaIdentifier));
68
+ }
69
+ getKey(schemaName, schemaIdentifier) {
70
+ return `${schemaName}@${schemaIdentifier}`;
71
+ }
72
+ }
73
+
74
+ // packages/core/src/agent/instance-manager.ts
75
+ var orderInsensitiveArrayKeys = new Set(["required", "enum", "oneOf", "allOf", "anyOf", "type"]);
76
+ var stableStringify = (value) => JSON.stringify(value) ?? "undefined";
77
+ var normalizeSchema = (value, arrayKey) => {
78
+ if (Array.isArray(value)) {
79
+ const normalizedItems = value.map((item) => normalizeSchema(item));
80
+ if (arrayKey && orderInsensitiveArrayKeys.has(arrayKey)) {
81
+ return normalizedItems.map((item) => ({ item, key: stableStringify(item) })).sort((left, right) => left.key.localeCompare(right.key)).map(({ item }) => item);
82
+ }
83
+ return normalizedItems;
84
+ }
85
+ if (value && typeof value === "object") {
86
+ const proto = Object.getPrototypeOf(value);
87
+ if (proto === Object.prototype || proto === null) {
88
+ const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right));
89
+ const normalized = {};
90
+ for (const [key, entryValue] of entries) {
91
+ normalized[key] = normalizeSchema(entryValue, key);
92
+ }
93
+ return normalized;
94
+ }
95
+ }
96
+ return value;
97
+ };
98
+
99
+ class AgentInstanceManager {
100
+ queue;
101
+ options;
102
+ schemaRegistry = new SchemaRegistry;
103
+ constructor(queue, options) {
104
+ this.queue = queue;
105
+ this.options = options;
106
+ }
107
+ registerSchema(schema) {
108
+ if (this.schemaRegistry.has(this.options.schemaName, this.options.schemaIdentifier)) {
109
+ const existing = this.schemaRegistry.get(this.options.schemaName, this.options.schemaIdentifier);
110
+ if (existing && !import_node_util.isDeepStrictEqual(normalizeSchema(existing.schema), normalizeSchema(schema))) {
111
+ console.warn(`Schema ${this.options.schemaName}@${this.options.schemaIdentifier} is already registered with a different payload. Ignoring registration.`);
112
+ }
113
+ return;
114
+ }
115
+ const registration = {
116
+ schemaName: this.options.schemaName,
117
+ schemaIdentifier: this.options.schemaIdentifier,
118
+ schema
119
+ };
120
+ this.schemaRegistry.register(registration);
121
+ this.queue.enqueue({ type: "schema_register", data: registration });
122
+ }
123
+ startInstance(options = {}) {
124
+ if (!this.options.allowUnregisteredSchema && !this.schemaRegistry.has(this.options.schemaName, this.options.schemaIdentifier)) {
125
+ console.warn(`Schema ${this.options.schemaName}@${this.options.schemaIdentifier} must be registered before starting an agent instance.`);
126
+ return;
127
+ }
128
+ const startData = {
129
+ ...options,
130
+ schemaName: this.options.schemaName,
131
+ schemaIdentifier: this.options.schemaIdentifier
132
+ };
133
+ this.queue.enqueue({ type: "agent_start", data: startData });
134
+ }
135
+ finishInstance() {
136
+ this.queue.enqueue({ type: "agent_finish", data: {} });
137
+ }
138
+ }
49
139
  // packages/core/src/config.ts
50
140
  var import_zod = require("zod");
51
141
  var HttpTransportConfigSchema = import_zod.z.object({
52
142
  apiUrl: import_zod.z.string().url(),
53
143
  apiToken: import_zod.z.string().min(1),
54
144
  agentId: import_zod.z.string().optional(),
55
- agentVersion: import_zod.z.string().optional(),
145
+ agentIdentifier: import_zod.z.string().default("v1.0.0"),
56
146
  agentName: import_zod.z.string().optional(),
57
147
  agentDescription: import_zod.z.string().optional(),
148
+ schemaName: import_zod.z.string().optional(),
149
+ schemaIdentifier: import_zod.z.string().optional(),
58
150
  agentSchema: import_zod.z.record(import_zod.z.unknown()).optional(),
59
- agentSchemaVersion: import_zod.z.string().optional(),
151
+ agentSchemaIdentifier: import_zod.z.string().optional(),
60
152
  skipSchema: import_zod.z.boolean().default(false),
61
153
  requestTimeout: import_zod.z.number().positive().default(30000),
62
154
  connectTimeout: import_zod.z.number().positive().default(1e4),
@@ -69,11 +161,13 @@ var PartialHttpConfigSchema = import_zod.z.object({
69
161
  apiUrl: import_zod.z.string().url(),
70
162
  apiToken: import_zod.z.string().min(1),
71
163
  agentId: import_zod.z.string().optional(),
72
- agentVersion: import_zod.z.string().optional(),
164
+ agentIdentifier: import_zod.z.string().optional(),
73
165
  agentName: import_zod.z.string().optional(),
74
166
  agentDescription: import_zod.z.string().optional(),
167
+ schemaName: import_zod.z.string().optional(),
168
+ schemaIdentifier: import_zod.z.string().optional(),
75
169
  agentSchema: import_zod.z.record(import_zod.z.unknown()).optional(),
76
- agentSchemaVersion: import_zod.z.string().optional(),
170
+ agentSchemaIdentifier: import_zod.z.string().optional(),
77
171
  skipSchema: import_zod.z.boolean().optional(),
78
172
  requestTimeout: import_zod.z.number().positive().optional(),
79
173
  connectTimeout: import_zod.z.number().positive().optional(),
@@ -103,24 +197,75 @@ function createConfig(options) {
103
197
  };
104
198
  return ConfigSchema.parse(config);
105
199
  }
200
+ // packages/core/src/create-core.ts
201
+ var import_pfid2 = require("@prefactor/pfid");
202
+
203
+ // packages/core/src/queue/in-memory.ts
204
+ class InMemoryQueue {
205
+ items = [];
206
+ enqueue(item) {
207
+ this.items.push(item);
208
+ }
209
+ dequeueBatch(maxItems) {
210
+ if (this.items.length === 0)
211
+ return [];
212
+ return this.items.splice(0, maxItems);
213
+ }
214
+ size() {
215
+ return this.items.length;
216
+ }
217
+ async flush() {
218
+ return;
219
+ }
220
+ }
221
+
222
+ // packages/core/src/tracing/tracer.ts
223
+ var import_pfid = require("@prefactor/pfid");
224
+
106
225
  // packages/core/src/tracing/context.ts
107
226
  var import_node_async_hooks = require("node:async_hooks");
108
227
  var spanStorage = new import_node_async_hooks.AsyncLocalStorage;
109
228
 
110
229
  class SpanContext {
111
230
  static getCurrent() {
112
- return spanStorage.getStore();
231
+ const stack = spanStorage.getStore() ?? [];
232
+ return stack[stack.length - 1];
233
+ }
234
+ static getStack() {
235
+ return [...spanStorage.getStore() ?? []];
236
+ }
237
+ static enter(span) {
238
+ const stack = [...spanStorage.getStore() ?? [], span];
239
+ spanStorage.enterWith(stack);
240
+ }
241
+ static exit() {
242
+ const stack = [...spanStorage.getStore() ?? []];
243
+ stack.pop();
244
+ spanStorage.enterWith(stack);
113
245
  }
114
246
  static run(span, fn) {
115
- return spanStorage.run(span, fn);
247
+ const stack = spanStorage.getStore() ?? [];
248
+ const resource = new import_node_async_hooks.AsyncResource("SpanContext.run");
249
+ try {
250
+ return resource.runInAsyncScope(() => spanStorage.run([...stack, span], fn));
251
+ } finally {
252
+ resource.emitDestroy();
253
+ }
116
254
  }
117
255
  static async runAsync(span, fn) {
118
- return spanStorage.run(span, fn);
256
+ const stack = spanStorage.getStore() ?? [];
257
+ const resource = new import_node_async_hooks.AsyncResource("SpanContext.runAsync");
258
+ try {
259
+ return await resource.runInAsyncScope(() => spanStorage.run([...stack, span], fn));
260
+ } finally {
261
+ resource.emitDestroy();
262
+ }
119
263
  }
120
264
  static clear() {
121
265
  spanStorage.disable();
122
266
  }
123
267
  }
268
+
124
269
  // packages/core/src/tracing/span.ts
125
270
  var SpanType;
126
271
  ((SpanType2) => {
@@ -130,27 +275,53 @@ var SpanType;
130
275
  SpanType2["CHAIN"] = "chain";
131
276
  SpanType2["RETRIEVER"] = "retriever";
132
277
  })(SpanType ||= {});
278
+ var DEFAULT_AGENT_SCHEMA = {
279
+ external_identifier: "1.0.0",
280
+ span_schemas: {
281
+ agent: {
282
+ type: "object",
283
+ properties: { type: { type: "string", const: "agent" } }
284
+ },
285
+ llm: {
286
+ type: "object",
287
+ properties: { type: { type: "string", const: "llm" } }
288
+ },
289
+ tool: {
290
+ type: "object",
291
+ properties: { type: { type: "string", const: "tool" } }
292
+ },
293
+ chain: {
294
+ type: "object",
295
+ properties: { type: { type: "string", const: "chain" } }
296
+ },
297
+ retriever: {
298
+ type: "object",
299
+ properties: { type: { type: "string", const: "retriever" } }
300
+ }
301
+ }
302
+ };
133
303
  var SpanStatus;
134
304
  ((SpanStatus2) => {
135
305
  SpanStatus2["RUNNING"] = "running";
136
306
  SpanStatus2["SUCCESS"] = "success";
137
307
  SpanStatus2["ERROR"] = "error";
138
308
  })(SpanStatus ||= {});
309
+
139
310
  // packages/core/src/tracing/tracer.ts
140
- var import_pfid = require("@prefactor/pfid");
141
311
  class Tracer {
142
- transport;
312
+ queue;
143
313
  partition;
144
- constructor(transport, partition) {
145
- this.transport = transport;
314
+ constructor(queue, partition) {
315
+ this.queue = queue;
146
316
  this.partition = partition ?? import_pfid.generatePartition();
147
317
  }
148
318
  startSpan(options) {
319
+ const parentSpan = SpanContext.getCurrent();
149
320
  const spanId = import_pfid.generate(this.partition);
150
- const traceId = options.traceId ?? import_pfid.generate(this.partition);
321
+ const traceId = parentSpan?.traceId ?? import_pfid.generate(this.partition);
151
322
  const span = {
152
323
  spanId,
153
- parentSpanId: options.parentSpanId ?? null,
324
+ parentSpanId: parentSpan?.spanId ?? null,
154
325
  traceId,
155
326
  name: options.name,
156
327
  spanType: options.spanType,
@@ -164,17 +335,18 @@ class Tracer {
164
335
  metadata: options.metadata ?? {},
165
336
  tags: options.tags ?? []
166
337
  };
167
- if (options.spanType === "agent") {
338
+ if (options.spanType === "agent" /* AGENT */) {
168
339
  try {
169
- this.transport.emit(span);
340
+ this.queue.enqueue({ type: "span_end", data: span });
170
341
  } catch (error) {
171
- console.error("Failed to emit agent span:", error);
342
+ console.error("Failed to enqueue agent span:", error);
172
343
  }
173
344
  }
174
345
  return span;
175
346
  }
176
347
  endSpan(span, options) {
177
- span.endTime = Date.now();
348
+ const endTime = Date.now();
349
+ span.endTime = endTime;
178
350
  span.outputs = options?.outputs ?? null;
179
351
  span.tokenUsage = options?.tokenUsage ?? null;
180
352
  if (options?.error) {
@@ -188,37 +360,24 @@ class Tracer {
188
360
  span.status = "success" /* SUCCESS */;
189
361
  }
190
362
  try {
191
- if (span.spanType === "agent") {
192
- this.transport.finishSpan(span.spanId, span.endTime);
363
+ if (span.spanType === "agent" /* AGENT */) {
364
+ this.queue.enqueue({ type: "span_finish", data: { spanId: span.spanId, endTime } });
193
365
  } else {
194
- this.transport.emit(span);
366
+ this.queue.enqueue({ type: "span_end", data: span });
195
367
  }
196
368
  } catch (error) {
197
- console.error("Failed to emit/finish span:", error);
198
- }
199
- }
200
- startAgentInstance() {
201
- try {
202
- this.transport.startAgentInstance();
203
- } catch (error) {
204
- console.error("Failed to start agent instance:", error);
205
- }
206
- }
207
- finishAgentInstance() {
208
- try {
209
- this.transport.finishAgentInstance();
210
- } catch (error) {
211
- console.error("Failed to finish agent instance:", error);
369
+ console.error("Failed to enqueue span action:", error);
212
370
  }
213
371
  }
214
372
  async close() {
215
373
  try {
216
- await this.transport.close();
374
+ await this.queue.flush();
217
375
  } catch (error) {
218
- console.error("Failed to close transport:", error);
376
+ console.error("Failed to flush queue:", error);
219
377
  }
220
378
  }
221
379
  }
380
+
222
381
  // packages/core/src/utils/logging.ts
223
382
  class Logger {
224
383
  namespace;
@@ -271,73 +430,76 @@ var logger = getLogger("http-transport");
271
430
 
272
431
  class HttpTransport {
273
432
  config;
274
- queue = [];
275
- processing = false;
276
433
  closed = false;
277
434
  agentInstanceId = null;
278
435
  spanIdMap = new Map;
436
+ pendingFinishes = new Map;
279
437
  constructor(config) {
280
438
  this.config = config;
281
- this.startProcessing();
282
- }
283
- emit(span) {
284
- if (this.closed) {
285
- return;
286
- }
287
- this.queue.push({ type: "span", data: span });
288
- }
289
- finishSpan(spanId, endTime) {
290
- if (this.closed) {
291
- return;
292
- }
293
- const timestamp = new Date(endTime).toISOString();
294
- this.queue.push({ type: "finish_span", data: { spanId, timestamp } });
295
- }
296
- startAgentInstance() {
297
- if (this.closed) {
298
- return;
299
- }
300
- this.queue.push({ type: "start_agent", data: null });
301
439
  }
302
- finishAgentInstance() {
303
- if (this.closed) {
440
+ async processBatch(items) {
441
+ if (this.closed || items.length === 0) {
304
442
  return;
305
443
  }
306
- this.queue.push({ type: "finish_agent", data: null });
307
- }
308
- async startProcessing() {
309
- this.processing = true;
310
- while (!this.closed || this.queue.length > 0) {
311
- if (this.queue.length === 0) {
312
- await new Promise((resolve) => setTimeout(resolve, 100));
313
- continue;
444
+ for (const item of items) {
445
+ if (item.type === "schema_register") {
446
+ this.config.agentSchema = item.data.schema;
447
+ this.config.agentSchemaIdentifier = item.data.schemaIdentifier;
448
+ this.config.schemaName = item.data.schemaName;
449
+ this.config.schemaIdentifier = item.data.schemaIdentifier;
314
450
  }
315
- const item = this.queue.shift();
316
- if (!item)
317
- continue;
451
+ }
452
+ for (const item of items) {
318
453
  try {
319
- if (!this.agentInstanceId && item.type !== "start_agent") {
320
- await this.ensureAgentRegistered();
321
- }
322
454
  switch (item.type) {
323
- case "span":
324
- await this.sendSpan(item.data);
325
- break;
326
- case "finish_span":
327
- await this.finishSpanHttp(item.data);
455
+ case "schema_register":
328
456
  break;
329
- case "start_agent":
457
+ case "agent_start":
458
+ this.config.agentId = item.data.agentId;
459
+ if (item.data.agentIdentifier !== undefined)
460
+ this.config.agentIdentifier = item.data.agentIdentifier;
461
+ this.config.agentName = item.data.agentName;
462
+ this.config.agentDescription = item.data.agentDescription;
330
463
  await this.startAgentInstanceHttp();
331
464
  break;
332
- case "finish_agent":
465
+ case "agent_finish":
333
466
  await this.finishAgentInstanceHttp();
334
467
  break;
468
+ case "span_end":
469
+ if (!this.agentInstanceId) {
470
+ await this.ensureAgentRegistered();
471
+ }
472
+ await this.sendSpan(item.data);
473
+ break;
474
+ case "span_finish": {
475
+ const spanId = item.data.spanId;
476
+ const backendSpanId = this.spanIdMap.get(spanId);
477
+ if (backendSpanId) {
478
+ const timestamp = new Date(item.data.endTime).toISOString();
479
+ await this.finishSpanHttp({ spanId, timestamp });
480
+ } else {
481
+ this.pendingFinishes.set(spanId, item.data.endTime);
482
+ }
483
+ break;
484
+ }
335
485
  }
336
486
  } catch (error) {
337
- logger.error("Error processing queue item:", error);
487
+ logger.error("Error processing batch item:", error);
338
488
  }
339
489
  }
340
- this.processing = false;
490
+ }
491
+ async processPendingFinishes(spanId) {
492
+ const pendingEndTime = this.pendingFinishes.get(spanId);
493
+ if (!pendingEndTime) {
494
+ return;
495
+ }
496
+ try {
497
+ const timestamp = new Date(pendingEndTime).toISOString();
498
+ await this.finishSpanHttp({ spanId, timestamp });
499
+ this.pendingFinishes.delete(spanId);
500
+ } catch (error) {
501
+ logger.error("Error processing pending span finish:", error);
502
+ }
341
503
  }
342
504
  async sendSpan(span, retry = 0) {
343
505
  const url = `${this.config.apiUrl}/api/v1/agent_spans`;
@@ -357,6 +519,7 @@ class HttpTransport {
357
519
  const backendSpanId = data?.details?.id;
358
520
  if (backendSpanId) {
359
521
  this.spanIdMap.set(span.spanId, backendSpanId);
522
+ await this.processPendingFinishes(span.spanId);
360
523
  }
361
524
  return;
362
525
  }
@@ -418,31 +581,7 @@ class HttpTransport {
418
581
  };
419
582
  }
420
583
  getDefaultSchema() {
421
- return {
422
- external_identifier: "1.0.0",
423
- span_schemas: {
424
- agent: {
425
- type: "object",
426
- properties: { type: { type: "string", const: "agent" } }
427
- },
428
- llm: {
429
- type: "object",
430
- properties: { type: { type: "string", const: "llm" } }
431
- },
432
- tool: {
433
- type: "object",
434
- properties: { type: { type: "string", const: "tool" } }
435
- },
436
- chain: {
437
- type: "object",
438
- properties: { type: { type: "string", const: "chain" } }
439
- },
440
- retriever: {
441
- type: "object",
442
- properties: { type: { type: "string", const: "retriever" } }
443
- }
444
- }
445
- };
584
+ return DEFAULT_AGENT_SCHEMA;
446
585
  }
447
586
  async ensureAgentRegistered() {
448
587
  if (this.agentInstanceId) {
@@ -452,9 +591,9 @@ class HttpTransport {
452
591
  const payload = {};
453
592
  if (this.config.agentId)
454
593
  payload.agent_id = this.config.agentId;
455
- if (this.config.agentVersion) {
594
+ if (this.config.agentIdentifier) {
456
595
  payload.agent_version = {
457
- external_identifier: this.config.agentVersion,
596
+ external_identifier: this.config.agentIdentifier,
458
597
  name: this.config.agentName || "Agent",
459
598
  description: this.config.agentDescription || ""
460
599
  };
@@ -464,10 +603,10 @@ class HttpTransport {
464
603
  } else if (this.config.agentSchema) {
465
604
  logger.debug("Using custom agent schema");
466
605
  payload.agent_schema_version = this.config.agentSchema;
467
- } else if (this.config.agentSchemaVersion) {
468
- logger.debug(`Using schema version: ${this.config.agentSchemaVersion}`);
606
+ } else if (this.config.agentSchemaIdentifier) {
607
+ logger.debug(`Using schema version: ${this.config.agentSchemaIdentifier}`);
469
608
  payload.agent_schema_version = {
470
- external_identifier: this.config.agentSchemaVersion
609
+ external_identifier: this.config.agentSchemaIdentifier
471
610
  };
472
611
  } else {
473
612
  logger.debug("Using default hardcoded schema (v1.0.0)");
@@ -540,6 +679,7 @@ class HttpTransport {
540
679
  } catch (error) {
541
680
  logger.error("Error finishing agent instance:", error);
542
681
  }
682
+ this.agentInstanceId = null;
543
683
  }
544
684
  async finishSpanHttp(data) {
545
685
  const backendSpanId = this.spanIdMap.get(data.spanId);
@@ -567,16 +707,13 @@ class HttpTransport {
567
707
  }
568
708
  async close() {
569
709
  this.closed = true;
570
- const timeout = 1e4;
571
- const start = Date.now();
572
- while (this.processing && Date.now() - start < timeout) {
573
- await new Promise((resolve) => setTimeout(resolve, 100));
574
- }
575
- if (this.processing) {
576
- logger.warn("Transport closed with pending queue items");
710
+ if (this.pendingFinishes.size > 0) {
711
+ logger.warn(`Transport closed with ${this.pendingFinishes.size} pending span finish(es) that could not be processed`);
712
+ this.pendingFinishes.clear();
577
713
  }
578
714
  }
579
715
  }
716
+
580
717
  // packages/core/src/utils/serialization.ts
581
718
  function truncateString(value, maxLength) {
582
719
  if (value.length <= maxLength) {
@@ -615,28 +752,147 @@ function serializeValue(value, maxLength = 1e4) {
615
752
  class StdioTransport {
616
753
  closed = false;
617
754
  writeLock = Promise.resolve();
618
- emit(span) {
619
- if (this.closed) {
755
+ async processBatch(items) {
756
+ if (this.closed || items.length === 0) {
620
757
  return;
621
758
  }
622
759
  this.writeLock = this.writeLock.then(async () => {
623
- try {
624
- const serialized = serializeValue(span);
625
- const json = JSON.stringify(serialized);
626
- await Bun.write(Bun.stdout, `${json}
760
+ for (const item of items) {
761
+ try {
762
+ const serialized = serializeValue(item);
763
+ const json = JSON.stringify(serialized);
764
+ await Bun.write(Bun.stdout, `${json}
627
765
  `);
628
- } catch (error) {
629
- console.error("Failed to emit span to stdout:", error);
766
+ } catch (error) {
767
+ console.error("Failed to emit queue action to stdout:", error);
768
+ }
630
769
  }
631
770
  });
771
+ await this.writeLock;
632
772
  }
633
- finishSpan() {}
634
- startAgentInstance() {}
635
- finishAgentInstance() {}
636
773
  async close() {
637
774
  this.closed = true;
638
775
  await this.writeLock;
639
776
  }
640
777
  }
641
778
 
642
- //# debugId=E95D2EBB45D9CCA264756E2164756E21
779
+ // packages/core/src/transport/worker.ts
780
+ class TransportWorker {
781
+ queue;
782
+ transport;
783
+ config;
784
+ closed = false;
785
+ inFlightPromise = null;
786
+ loopPromise;
787
+ pendingBatch = null;
788
+ constructor(queue, transport, config) {
789
+ this.queue = queue;
790
+ this.transport = transport;
791
+ this.config = config;
792
+ this.loopPromise = this.start();
793
+ }
794
+ async start() {
795
+ while (!this.closed || this.pendingBatch || this.queue.size() > 0 || this.inFlightPromise) {
796
+ const batch = this.pendingBatch ?? this.queue.dequeueBatch(this.config.batchSize);
797
+ if (batch.length === 0) {
798
+ await new Promise((resolve) => setTimeout(resolve, this.config.intervalMs));
799
+ continue;
800
+ }
801
+ try {
802
+ const inFlight = this.transport.processBatch(batch);
803
+ this.inFlightPromise = inFlight;
804
+ await inFlight;
805
+ this.pendingBatch = null;
806
+ } catch (error) {
807
+ this.pendingBatch = batch;
808
+ console.error("TransportWorker.processBatch failed", error);
809
+ await new Promise((resolve) => setTimeout(resolve, this.config.intervalMs));
810
+ } finally {
811
+ this.inFlightPromise = null;
812
+ }
813
+ }
814
+ }
815
+ async flush(timeoutMs) {
816
+ const start = Date.now();
817
+ while ((this.queue.size() > 0 || this.pendingBatch || this.inFlightPromise) && Date.now() - start < timeoutMs) {
818
+ await new Promise((resolve) => setTimeout(resolve, this.config.intervalMs));
819
+ }
820
+ }
821
+ async close(timeoutMs = this.config.intervalMs * 50) {
822
+ this.closed = true;
823
+ const deadline = Date.now() + timeoutMs;
824
+ const awaitWithTimeout = async (promise, label, timeoutMs2, warnOnTimeout = true) => {
825
+ if (timeoutMs2 <= 0) {
826
+ if (warnOnTimeout) {
827
+ console.warn(`TransportWorker.close timed out waiting for ${label}`);
828
+ }
829
+ return false;
830
+ }
831
+ const resolvedPromise = Promise.resolve(promise);
832
+ let timeoutId = null;
833
+ const timeoutPromise = new Promise((resolve) => {
834
+ timeoutId = setTimeout(() => resolve(false), timeoutMs2);
835
+ });
836
+ const completed = await Promise.race([resolvedPromise.then(() => true), timeoutPromise]);
837
+ if (timeoutId) {
838
+ clearTimeout(timeoutId);
839
+ }
840
+ if (!completed && warnOnTimeout) {
841
+ console.warn(`TransportWorker.close timed out waiting for ${label}`);
842
+ }
843
+ return completed;
844
+ };
845
+ const remainingTime = () => Math.max(0, deadline - Date.now());
846
+ const loopCompleted = await awaitWithTimeout(this.loopPromise, "loop to finish", remainingTime(), false);
847
+ if (!loopCompleted) {
848
+ console.warn("TransportWorker.close timed out waiting for loop to finish; closing transport now may cause potential data loss");
849
+ }
850
+ const safeTransportClose = async () => {
851
+ try {
852
+ await this.transport.close();
853
+ } catch (error) {
854
+ console.error("TransportWorker.close failed", error);
855
+ }
856
+ };
857
+ await awaitWithTimeout(safeTransportClose(), "transport to close", remainingTime());
858
+ }
859
+ }
860
+
861
+ // packages/core/src/create-core.ts
862
+ function createCore(config) {
863
+ let transport;
864
+ if (config.transportType === "stdio") {
865
+ transport = new StdioTransport;
866
+ } else {
867
+ if (!config.httpConfig) {
868
+ throw new Error("HTTP transport requires httpConfig to be provided in configuration");
869
+ }
870
+ const httpConfig = HttpTransportConfigSchema.parse(config.httpConfig);
871
+ transport = new HttpTransport(httpConfig);
872
+ }
873
+ let partition;
874
+ if (config.httpConfig?.agentId) {
875
+ try {
876
+ partition = import_pfid2.extractPartition(config.httpConfig.agentId);
877
+ } catch {
878
+ partition = undefined;
879
+ }
880
+ }
881
+ const queue = new InMemoryQueue;
882
+ const worker = new TransportWorker(queue, transport, { batchSize: 25, intervalMs: 50 });
883
+ const tracer = new Tracer(queue, partition);
884
+ const schemaName = config.httpConfig?.schemaName ?? "prefactor:agent";
885
+ const schemaIdentifier = config.httpConfig?.schemaIdentifier ?? "1.0.0";
886
+ const allowUnregisteredSchema = config.transportType === "http" && Boolean(config.httpConfig?.skipSchema || config.httpConfig?.agentSchema || config.httpConfig?.agentSchemaIdentifier);
887
+ const agentManager = new AgentInstanceManager(queue, {
888
+ schemaName,
889
+ schemaIdentifier,
890
+ allowUnregisteredSchema
891
+ });
892
+ const shutdown = async () => {
893
+ await worker.close();
894
+ };
895
+ return { tracer, agentManager, worker, shutdown };
896
+ }
897
+
898
+ //# debugId=CE5CB6A1D11DF5BA64756E2164756E21