@prefactor/core 0.2.0 → 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.
Files changed (82) hide show
  1. package/dist/agent/instance-manager.d.ts +5 -8
  2. package/dist/agent/instance-manager.d.ts.map +1 -1
  3. package/dist/agent/instance-manager.js +34 -55
  4. package/dist/agent/instance-manager.js.map +1 -1
  5. package/dist/config.d.ts +15 -63
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +29 -20
  8. package/dist/config.js.map +1 -1
  9. package/dist/create-core.d.ts +0 -2
  10. package/dist/create-core.d.ts.map +1 -1
  11. package/dist/create-core.js +10 -28
  12. package/dist/create-core.js.map +1 -1
  13. package/dist/index.cjs +577 -457
  14. package/dist/index.cjs.map +17 -15
  15. package/dist/index.d.ts +2 -7
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +577 -457
  18. package/dist/index.js.map +17 -15
  19. package/dist/queue/actions.d.ts +15 -26
  20. package/dist/queue/actions.d.ts.map +1 -1
  21. package/dist/queue/base.d.ts +15 -3
  22. package/dist/queue/base.d.ts.map +1 -1
  23. package/dist/queue/in-memory-queue.d.ts +11 -0
  24. package/dist/queue/in-memory-queue.d.ts.map +1 -0
  25. package/dist/queue/in-memory-queue.js +46 -0
  26. package/dist/queue/in-memory-queue.js.map +1 -0
  27. package/dist/queue/task-executor.d.ts +18 -0
  28. package/dist/queue/task-executor.d.ts.map +1 -0
  29. package/dist/queue/task-executor.js +77 -0
  30. package/dist/queue/task-executor.js.map +1 -0
  31. package/dist/tracing/span.d.ts +7 -14
  32. package/dist/tracing/span.d.ts.map +1 -1
  33. package/dist/tracing/span.js +5 -36
  34. package/dist/tracing/span.js.map +1 -1
  35. package/dist/tracing/tracer.d.ts +6 -7
  36. package/dist/tracing/tracer.d.ts.map +1 -1
  37. package/dist/tracing/tracer.js +27 -12
  38. package/dist/tracing/tracer.js.map +1 -1
  39. package/dist/transport/http/agent-instance-client.d.ts +23 -0
  40. package/dist/transport/http/agent-instance-client.d.ts.map +1 -0
  41. package/dist/transport/http/agent-instance-client.js +25 -0
  42. package/dist/transport/http/agent-instance-client.js.map +1 -0
  43. package/dist/transport/http/agent-span-client.d.ts +25 -0
  44. package/dist/transport/http/agent-span-client.d.ts.map +1 -0
  45. package/dist/transport/http/agent-span-client.js +37 -0
  46. package/dist/transport/http/agent-span-client.js.map +1 -0
  47. package/dist/transport/http/http-client.d.ts +43 -0
  48. package/dist/transport/http/http-client.d.ts.map +1 -0
  49. package/dist/transport/http/http-client.js +127 -0
  50. package/dist/transport/http/http-client.js.map +1 -0
  51. package/dist/transport/http/retry-policy.d.ts +4 -0
  52. package/dist/transport/http/retry-policy.d.ts.map +1 -0
  53. package/dist/transport/http/retry-policy.js +10 -0
  54. package/dist/transport/http/retry-policy.js.map +1 -0
  55. package/dist/transport/http.d.ts +31 -50
  56. package/dist/transport/http.d.ts.map +1 -1
  57. package/dist/transport/http.js +138 -227
  58. package/dist/transport/http.js.map +1 -1
  59. package/dist/utils/logging.d.ts.map +1 -1
  60. package/dist/utils/logging.js +7 -1
  61. package/dist/utils/logging.js.map +1 -1
  62. package/package.json +1 -1
  63. package/dist/agent/schema-registry.d.ts +0 -9
  64. package/dist/agent/schema-registry.d.ts.map +0 -1
  65. package/dist/agent/schema-registry.js +0 -16
  66. package/dist/agent/schema-registry.js.map +0 -1
  67. package/dist/queue/in-memory.d.ts +0 -9
  68. package/dist/queue/in-memory.d.ts.map +0 -1
  69. package/dist/queue/in-memory.js +0 -18
  70. package/dist/queue/in-memory.js.map +0 -1
  71. package/dist/transport/base.d.ts +0 -18
  72. package/dist/transport/base.d.ts.map +0 -1
  73. package/dist/transport/base.js +0 -2
  74. package/dist/transport/base.js.map +0 -1
  75. package/dist/transport/stdio.d.ts +0 -36
  76. package/dist/transport/stdio.d.ts.map +0 -1
  77. package/dist/transport/stdio.js +0 -56
  78. package/dist/transport/stdio.js.map +0 -1
  79. package/dist/transport/worker.d.ts +0 -22
  80. package/dist/transport/worker.d.ts.map +0 -1
  81. package/dist/transport/worker.js +0 -85
  82. package/dist/transport/worker.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -36,108 +36,74 @@ __export(exports_src, {
36
36
  createConfig: () => createConfig,
37
37
  configureLogging: () => configureLogging,
38
38
  Tracer: () => Tracer,
39
- StdioTransport: () => StdioTransport,
40
39
  SpanType: () => SpanType,
41
40
  SpanStatus: () => SpanStatus,
42
41
  SpanContext: () => SpanContext,
43
- SchemaRegistry: () => SchemaRegistry,
44
42
  PartialHttpConfigSchema: () => PartialHttpConfigSchema,
45
- InMemoryQueue: () => InMemoryQueue,
46
43
  HttpTransportConfigSchema: () => HttpTransportConfigSchema,
47
44
  HttpTransport: () => HttpTransport,
48
- DEFAULT_AGENT_SCHEMA: () => DEFAULT_AGENT_SCHEMA,
49
45
  ConfigSchema: () => ConfigSchema,
50
46
  AgentInstanceManager: () => AgentInstanceManager
51
47
  });
52
48
  module.exports = __toCommonJS(exports_src);
53
49
 
54
50
  // 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
51
  class AgentInstanceManager {
100
- queue;
52
+ transport;
101
53
  options;
102
- schemaRegistry = new SchemaRegistry;
103
- constructor(queue, options) {
104
- this.queue = queue;
54
+ registeredSchema = null;
55
+ constructor(transport, options) {
56
+ this.transport = transport;
105
57
  this.options = options;
106
58
  }
107
59
  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
- }
60
+ if (this.registeredSchema === null) {
61
+ this.registeredSchema = schema;
62
+ this.transport.registerSchema(schema);
113
63
  return;
114
64
  }
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 });
65
+ const existingSchema = stableStringify(this.registeredSchema);
66
+ const incomingSchema = stableStringify(schema);
67
+ if (existingSchema !== incomingSchema) {
68
+ console.warn("A different schema was provided after registration; ignoring subsequent schema.");
69
+ }
122
70
  }
123
71
  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.`);
72
+ if (!this.options.allowUnregisteredSchema && this.registeredSchema === null) {
73
+ console.warn("Schema must be registered before starting an agent instance.");
126
74
  return;
127
75
  }
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 });
76
+ this.transport.startAgentInstance(options);
134
77
  }
135
78
  finishInstance() {
136
- this.queue.enqueue({ type: "agent_finish", data: {} });
79
+ this.transport.finishAgentInstance();
137
80
  }
138
81
  }
82
+ function stableStringify(value) {
83
+ return JSON.stringify(normalizeValue(value));
84
+ }
85
+ function normalizeValue(value) {
86
+ if (Array.isArray(value)) {
87
+ return value.map((entry) => normalizeValue(entry));
88
+ }
89
+ if (value && typeof value === "object") {
90
+ const normalized = {};
91
+ const objectValue = value;
92
+ const keys = Object.keys(objectValue).sort((a, b) => a.localeCompare(b));
93
+ for (const key of keys) {
94
+ normalized[key] = normalizeValue(objectValue[key]);
95
+ }
96
+ return normalized;
97
+ }
98
+ return value;
99
+ }
139
100
  // packages/core/src/config.ts
140
101
  var import_zod = require("zod");
102
+ var DEFAULT_RETRY_ON_STATUS_CODES = [
103
+ 429,
104
+ ...Array.from({ length: 100 }, (_, index) => 500 + index)
105
+ ];
106
+ var HttpStatusCodeSchema = import_zod.z.number().int().min(100).max(599);
141
107
  var HttpTransportConfigSchema = import_zod.z.object({
142
108
  apiUrl: import_zod.z.string().url(),
143
109
  apiToken: import_zod.z.string().min(1),
@@ -145,17 +111,13 @@ var HttpTransportConfigSchema = import_zod.z.object({
145
111
  agentIdentifier: import_zod.z.string().default("v1.0.0"),
146
112
  agentName: import_zod.z.string().optional(),
147
113
  agentDescription: import_zod.z.string().optional(),
148
- schemaName: import_zod.z.string().optional(),
149
- schemaIdentifier: import_zod.z.string().optional(),
150
114
  agentSchema: import_zod.z.record(import_zod.z.unknown()).optional(),
151
- agentSchemaIdentifier: import_zod.z.string().optional(),
152
- skipSchema: import_zod.z.boolean().default(false),
153
115
  requestTimeout: import_zod.z.number().positive().default(30000),
154
- connectTimeout: import_zod.z.number().positive().default(1e4),
155
116
  maxRetries: import_zod.z.number().int().nonnegative().default(3),
156
117
  initialRetryDelay: import_zod.z.number().positive().default(1000),
157
118
  maxRetryDelay: import_zod.z.number().positive().default(60000),
158
- retryMultiplier: import_zod.z.number().positive().default(2)
119
+ retryMultiplier: import_zod.z.number().positive().default(2),
120
+ retryOnStatusCodes: import_zod.z.array(HttpStatusCodeSchema).default([...DEFAULT_RETRY_ON_STATUS_CODES])
159
121
  });
160
122
  var PartialHttpConfigSchema = import_zod.z.object({
161
123
  apiUrl: import_zod.z.string().url(),
@@ -164,20 +126,16 @@ var PartialHttpConfigSchema = import_zod.z.object({
164
126
  agentIdentifier: import_zod.z.string().optional(),
165
127
  agentName: import_zod.z.string().optional(),
166
128
  agentDescription: import_zod.z.string().optional(),
167
- schemaName: import_zod.z.string().optional(),
168
- schemaIdentifier: import_zod.z.string().optional(),
169
129
  agentSchema: import_zod.z.record(import_zod.z.unknown()).optional(),
170
- agentSchemaIdentifier: import_zod.z.string().optional(),
171
- skipSchema: import_zod.z.boolean().optional(),
172
130
  requestTimeout: import_zod.z.number().positive().optional(),
173
- connectTimeout: import_zod.z.number().positive().optional(),
174
131
  maxRetries: import_zod.z.number().int().nonnegative().optional(),
175
132
  initialRetryDelay: import_zod.z.number().positive().optional(),
176
133
  maxRetryDelay: import_zod.z.number().positive().optional(),
177
- retryMultiplier: import_zod.z.number().positive().optional()
134
+ retryMultiplier: import_zod.z.number().positive().optional(),
135
+ retryOnStatusCodes: import_zod.z.array(HttpStatusCodeSchema).optional()
178
136
  });
179
137
  var ConfigSchema = import_zod.z.object({
180
- transportType: import_zod.z.enum(["stdio", "http"]).default("stdio"),
138
+ transportType: import_zod.z.enum(["http"]).default("http"),
181
139
  sampleRate: import_zod.z.number().min(0).max(1).default(1),
182
140
  captureInputs: import_zod.z.boolean().default(true),
183
141
  captureOutputs: import_zod.z.boolean().default(true),
@@ -186,38 +144,30 @@ var ConfigSchema = import_zod.z.object({
186
144
  httpConfig: PartialHttpConfigSchema.optional()
187
145
  });
188
146
  function createConfig(options) {
147
+ const retryOnStatusCodesFromEnv = parseRetryOnStatusCodesEnv(process.env.PREFACTOR_RETRY_ON_STATUS_CODES);
189
148
  const config = {
190
- transportType: options?.transportType ?? process.env.PREFACTOR_TRANSPORT ?? "stdio",
149
+ transportType: options?.transportType ?? process.env.PREFACTOR_TRANSPORT ?? "http",
191
150
  sampleRate: options?.sampleRate ?? parseFloat(process.env.PREFACTOR_SAMPLE_RATE ?? "1.0"),
192
151
  captureInputs: options?.captureInputs ?? process.env.PREFACTOR_CAPTURE_INPUTS !== "false",
193
152
  captureOutputs: options?.captureOutputs ?? process.env.PREFACTOR_CAPTURE_OUTPUTS !== "false",
194
153
  maxInputLength: options?.maxInputLength ?? parseInt(process.env.PREFACTOR_MAX_INPUT_LENGTH ?? "10000", 10),
195
154
  maxOutputLength: options?.maxOutputLength ?? parseInt(process.env.PREFACTOR_MAX_OUTPUT_LENGTH ?? "10000", 10),
196
- httpConfig: options?.httpConfig
155
+ httpConfig: options?.httpConfig ? {
156
+ ...options.httpConfig,
157
+ retryOnStatusCodes: options.httpConfig.retryOnStatusCodes ?? retryOnStatusCodesFromEnv
158
+ } : undefined
197
159
  };
198
160
  return ConfigSchema.parse(config);
199
161
  }
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() {
162
+ function parseRetryOnStatusCodesEnv(value) {
163
+ if (!value) {
218
164
  return;
219
165
  }
166
+ const parsedCodes = value.split(",").map((status) => status.trim()).filter((status) => /^\d{3}$/.test(status)).map((status) => Number(status)).filter((status) => status >= 100 && status <= 599);
167
+ return parsedCodes.length > 0 ? parsedCodes : undefined;
220
168
  }
169
+ // packages/core/src/create-core.ts
170
+ var import_pfid2 = require("@prefactor/pfid");
221
171
 
222
172
  // packages/core/src/tracing/tracer.ts
223
173
  var import_pfid = require("@prefactor/pfid");
@@ -267,38 +217,11 @@ class SpanContext {
267
217
  }
268
218
 
269
219
  // packages/core/src/tracing/span.ts
270
- var SpanType;
271
- ((SpanType2) => {
272
- SpanType2["AGENT"] = "agent";
273
- SpanType2["LLM"] = "llm";
274
- SpanType2["TOOL"] = "tool";
275
- SpanType2["CHAIN"] = "chain";
276
- SpanType2["RETRIEVER"] = "retriever";
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
- }
220
+ var SpanType = {
221
+ AGENT: "agent",
222
+ LLM: "llm",
223
+ TOOL: "tool",
224
+ CHAIN: "chain"
302
225
  };
303
226
  var SpanStatus;
304
227
  ((SpanStatus2) => {
@@ -309,10 +232,10 @@ var SpanStatus;
309
232
 
310
233
  // packages/core/src/tracing/tracer.ts
311
234
  class Tracer {
312
- queue;
235
+ transport;
313
236
  partition;
314
- constructor(queue, partition) {
315
- this.queue = queue;
237
+ constructor(transport, partition) {
238
+ this.transport = transport;
316
239
  this.partition = partition ?? import_pfid.generatePartition();
317
240
  }
318
241
  startSpan(options) {
@@ -332,14 +255,13 @@ class Tracer {
332
255
  outputs: null,
333
256
  tokenUsage: null,
334
257
  error: null,
335
- metadata: options.metadata ?? {},
336
- tags: options.tags ?? []
258
+ metadata: options.metadata ?? {}
337
259
  };
338
- if (options.spanType === "agent" /* AGENT */) {
260
+ if (options.spanType === SpanType.AGENT) {
339
261
  try {
340
- this.queue.enqueue({ type: "span_end", data: span });
262
+ this.transport.emit(span);
341
263
  } catch (error) {
342
- console.error("Failed to enqueue agent span:", error);
264
+ console.error("Failed to emit agent span:", error);
343
265
  }
344
266
  }
345
267
  return span;
@@ -360,28 +282,180 @@ class Tracer {
360
282
  span.status = "success" /* SUCCESS */;
361
283
  }
362
284
  try {
363
- if (span.spanType === "agent" /* AGENT */) {
364
- this.queue.enqueue({ type: "span_finish", data: { spanId: span.spanId, endTime } });
285
+ if (span.spanType === SpanType.AGENT) {
286
+ this.transport.finishSpan(span.spanId, endTime);
365
287
  } else {
366
- this.queue.enqueue({ type: "span_end", data: span });
288
+ this.transport.emit(span);
367
289
  }
368
290
  } catch (error) {
369
- console.error("Failed to enqueue span action:", error);
291
+ console.error("Failed to emit span action:", error);
370
292
  }
371
293
  }
372
294
  async close() {
373
295
  try {
374
- await this.queue.flush();
296
+ await this.transport.close();
297
+ } catch (error) {
298
+ console.error("Failed to close transport:", error);
299
+ }
300
+ }
301
+ startAgentInstance() {
302
+ try {
303
+ this.transport.startAgentInstance();
304
+ } catch (error) {
305
+ console.error("Failed to start agent instance:", error);
306
+ }
307
+ }
308
+ finishAgentInstance() {
309
+ try {
310
+ this.transport.finishAgentInstance();
375
311
  } catch (error) {
376
- console.error("Failed to flush queue:", error);
312
+ console.error("Failed to finish agent instance:", error);
313
+ }
314
+ }
315
+ }
316
+
317
+ // packages/core/src/queue/in-memory-queue.ts
318
+ class InMemoryQueue {
319
+ items = [];
320
+ waiters = [];
321
+ isClosed = false;
322
+ async put(item) {
323
+ if (this.isClosed) {
324
+ throw new Error("Cannot put item into a closed queue");
325
+ }
326
+ const waiter = this.waiters.shift();
327
+ if (waiter) {
328
+ waiter({ done: false, item });
329
+ return;
330
+ }
331
+ this.items.push({ item });
332
+ }
333
+ async get() {
334
+ if (this.items.length > 0) {
335
+ const entry = this.items.shift();
336
+ if (entry) {
337
+ return { done: false, item: entry.item };
338
+ }
339
+ }
340
+ if (this.isClosed) {
341
+ return { done: true };
342
+ }
343
+ return new Promise((resolve) => {
344
+ this.waiters.push(resolve);
345
+ });
346
+ }
347
+ close() {
348
+ if (this.isClosed) {
349
+ return;
350
+ }
351
+ this.isClosed = true;
352
+ while (this.waiters.length > 0) {
353
+ const waiter = this.waiters.shift();
354
+ if (waiter) {
355
+ waiter({ done: true });
356
+ }
357
+ }
358
+ }
359
+ size() {
360
+ return this.items.length;
361
+ }
362
+ }
363
+
364
+ // packages/core/src/queue/task-executor.ts
365
+ var DEFAULT_WORKER_COUNT = 1;
366
+ var DEFAULT_MAX_RETRIES = 0;
367
+ var DEFAULT_RETRY_DELAY_MS = 0;
368
+
369
+ class TaskExecutor {
370
+ queue;
371
+ handler;
372
+ isRunning = false;
373
+ workerPromises = [];
374
+ workerCount;
375
+ maxRetries;
376
+ retryDelayMs;
377
+ onError;
378
+ constructor(queue, handler, options = {}) {
379
+ this.queue = queue;
380
+ this.handler = handler;
381
+ this.workerCount = Math.max(options.workerCount ?? DEFAULT_WORKER_COUNT, 1);
382
+ this.maxRetries = Math.max(options.maxRetries ?? DEFAULT_MAX_RETRIES, 0);
383
+ this.retryDelayMs = Math.max(options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS, 0);
384
+ this.onError = options.onError;
385
+ }
386
+ start() {
387
+ if (this.isRunning) {
388
+ return;
389
+ }
390
+ this.isRunning = true;
391
+ this.workerPromises = Array.from({ length: this.workerCount }, () => this.runWorker());
392
+ }
393
+ async stop() {
394
+ if (!this.isRunning) {
395
+ return;
396
+ }
397
+ this.isRunning = false;
398
+ this.queue.close();
399
+ await Promise.all(this.workerPromises);
400
+ this.workerPromises = [];
401
+ }
402
+ async runWorker() {
403
+ while (true) {
404
+ const result = await this.queue.get();
405
+ if (result.done) {
406
+ return;
407
+ }
408
+ await this.executeWithRetry(result.item);
409
+ }
410
+ }
411
+ async executeWithRetry(item) {
412
+ let attempt = 0;
413
+ while (attempt <= this.maxRetries) {
414
+ try {
415
+ await this.handler(item);
416
+ return;
417
+ } catch (error) {
418
+ if (attempt >= this.maxRetries) {
419
+ await this.safeOnError(error, item);
420
+ return;
421
+ }
422
+ if (this.retryDelayMs > 0) {
423
+ await new Promise((resolve) => setTimeout(resolve, this.retryDelayMs));
424
+ }
425
+ }
426
+ attempt += 1;
427
+ }
428
+ }
429
+ async safeOnError(error, item) {
430
+ if (!this.onError) {
431
+ return;
432
+ }
433
+ try {
434
+ await this.onError(error, item);
435
+ } catch {
436
+ return;
377
437
  }
378
438
  }
379
439
  }
380
440
 
381
441
  // packages/core/src/utils/logging.ts
442
+ var LogLevel;
443
+ ((LogLevel2) => {
444
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
445
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
446
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
447
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
448
+ })(LogLevel ||= {});
449
+
382
450
  class Logger {
383
451
  namespace;
384
- static level = 1 /* INFO */;
452
+ static level = (() => {
453
+ const level = process.env.PREFACTOR_LOG_LEVEL?.toUpperCase();
454
+ if (level && level in LogLevel) {
455
+ return LogLevel[level];
456
+ }
457
+ return 1 /* INFO */;
458
+ })();
385
459
  constructor(namespace) {
386
460
  this.namespace = namespace;
387
461
  }
@@ -425,72 +499,322 @@ function configureLogging() {
425
499
  }
426
500
  }
427
501
 
502
+ // packages/core/src/transport/http/agent-instance-client.ts
503
+ class AgentInstanceClient {
504
+ httpClient;
505
+ constructor(httpClient) {
506
+ this.httpClient = httpClient;
507
+ }
508
+ register(payload) {
509
+ return this.httpClient.request("/api/v1/agent_instance/register", {
510
+ method: "POST",
511
+ body: payload
512
+ });
513
+ }
514
+ async start(agentInstanceId) {
515
+ await this.httpClient.request(`/api/v1/agent_instance/${agentInstanceId}/start`, {
516
+ method: "POST",
517
+ body: {}
518
+ });
519
+ }
520
+ async finish(agentInstanceId) {
521
+ await this.httpClient.request(`/api/v1/agent_instance/${agentInstanceId}/finish`, {
522
+ method: "POST",
523
+ body: {}
524
+ });
525
+ }
526
+ }
527
+
528
+ // packages/core/src/transport/http/retry-policy.ts
529
+ var JITTER_MIN = 0.5;
530
+ function shouldRetryStatusCode(statusCode, retryOnStatusCodes) {
531
+ return retryOnStatusCodes.includes(statusCode);
532
+ }
533
+ function calculateRetryDelay(attempt, config, random = Math.random) {
534
+ const baseDelay = Math.min(config.initialRetryDelay * config.retryMultiplier ** attempt, config.maxRetryDelay);
535
+ const jitterMultiplier = JITTER_MIN + random() * JITTER_MIN;
536
+ return Math.round(baseDelay * jitterMultiplier);
537
+ }
538
+
539
+ // packages/core/src/transport/http/http-client.ts
540
+ class HttpClientError extends Error {
541
+ url;
542
+ method;
543
+ status;
544
+ statusText;
545
+ responseBody;
546
+ retryable;
547
+ constructor(message, options) {
548
+ super(message, { cause: options.cause });
549
+ this.name = "HttpClientError";
550
+ this.url = options.url;
551
+ this.method = options.method;
552
+ this.status = options.status;
553
+ this.statusText = options.statusText;
554
+ this.responseBody = options.responseBody;
555
+ this.retryable = options.retryable;
556
+ }
557
+ }
558
+
559
+ class HttpClient {
560
+ config;
561
+ fetchFn;
562
+ sleep;
563
+ random;
564
+ constructor(config, dependencies = {}) {
565
+ this.config = config;
566
+ this.fetchFn = dependencies.fetchFn ?? fetch;
567
+ this.sleep = dependencies.sleep ?? ((delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs)));
568
+ this.random = dependencies.random ?? Math.random;
569
+ }
570
+ async request(path, options = {}) {
571
+ const url = new URL(path, this.config.apiUrl).toString();
572
+ const method = options.method ?? "GET";
573
+ let attempt = 0;
574
+ while (true) {
575
+ const headers = new Headers(options.headers);
576
+ headers.set("Authorization", `Bearer ${this.config.apiToken}`);
577
+ if (options.body !== undefined && !headers.has("Content-Type")) {
578
+ headers.set("Content-Type", "application/json");
579
+ }
580
+ const requestInit = {
581
+ ...options,
582
+ method,
583
+ headers,
584
+ body: options.body === undefined ? undefined : JSON.stringify(options.body),
585
+ signal: AbortSignal.timeout(options.timeoutMs ?? this.config.requestTimeout)
586
+ };
587
+ try {
588
+ const response = await this.fetchFn(url, requestInit);
589
+ if (response.ok) {
590
+ return await parseResponseBody(response);
591
+ }
592
+ const responseBody = await parseResponseBody(response);
593
+ const canRetry = attempt < this.config.maxRetries && shouldRetryStatusCode(response.status, this.config.retryOnStatusCodes);
594
+ if (canRetry) {
595
+ const delayMs = calculateRetryDelay(attempt, this.config, this.random);
596
+ await this.sleep(delayMs);
597
+ attempt += 1;
598
+ continue;
599
+ }
600
+ throw new HttpClientError(`HTTP request failed with status ${response.status}`, {
601
+ url,
602
+ method,
603
+ status: response.status,
604
+ statusText: response.statusText,
605
+ responseBody,
606
+ retryable: shouldRetryStatusCode(response.status, this.config.retryOnStatusCodes)
607
+ });
608
+ } catch (error) {
609
+ if (error instanceof HttpClientError) {
610
+ throw error;
611
+ }
612
+ const canRetry = attempt < this.config.maxRetries && isRetryableNetworkError(error);
613
+ if (canRetry) {
614
+ const delayMs = calculateRetryDelay(attempt, this.config, this.random);
615
+ await this.sleep(delayMs);
616
+ attempt += 1;
617
+ continue;
618
+ }
619
+ throw new HttpClientError("HTTP request failed due to network error", {
620
+ url,
621
+ method,
622
+ retryable: false,
623
+ cause: error
624
+ });
625
+ }
626
+ }
627
+ }
628
+ }
629
+ function isRetryableNetworkError(error) {
630
+ if (error instanceof TypeError) {
631
+ return true;
632
+ }
633
+ if (error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError")) {
634
+ return true;
635
+ }
636
+ if (error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError")) {
637
+ return true;
638
+ }
639
+ return false;
640
+ }
641
+ async function parseResponseBody(response) {
642
+ const bodyText = await response.text();
643
+ if (!bodyText) {
644
+ return null;
645
+ }
646
+ const contentType = response.headers.get("content-type") ?? "";
647
+ if (contentType.includes("application/json")) {
648
+ try {
649
+ return JSON.parse(bodyText);
650
+ } catch {
651
+ return bodyText;
652
+ }
653
+ }
654
+ try {
655
+ return JSON.parse(bodyText);
656
+ } catch {
657
+ return bodyText;
658
+ }
659
+ }
660
+
661
+ // packages/core/src/transport/http/agent-span-client.ts
662
+ class AgentSpanClient {
663
+ httpClient;
664
+ constructor(httpClient) {
665
+ this.httpClient = httpClient;
666
+ }
667
+ create(payload) {
668
+ return this.httpClient.request("/api/v1/agent_spans", {
669
+ method: "POST",
670
+ body: payload
671
+ });
672
+ }
673
+ async finish(spanId, timestamp) {
674
+ try {
675
+ await this.httpClient.request(`/api/v1/agent_spans/${spanId}/finish`, {
676
+ method: "POST",
677
+ body: { timestamp }
678
+ });
679
+ } catch (error) {
680
+ if (error instanceof HttpClientError && error.status === 409 && isAlreadyFinishedError(error.responseBody)) {
681
+ return;
682
+ }
683
+ throw error;
684
+ }
685
+ }
686
+ }
687
+ function isAlreadyFinishedError(responseBody) {
688
+ if (!responseBody || typeof responseBody !== "object") {
689
+ return false;
690
+ }
691
+ const payload = responseBody;
692
+ return payload.code === "invalid_action";
693
+ }
694
+
428
695
  // packages/core/src/transport/http.ts
429
696
  var logger = getLogger("http-transport");
430
697
 
431
698
  class HttpTransport {
432
699
  config;
433
700
  closed = false;
701
+ actionQueue = new InMemoryQueue;
702
+ taskExecutor;
703
+ agentInstanceClient;
704
+ agentSpanClient;
705
+ previousAgentSchema = null;
706
+ requiresNewAgentIdentifier = false;
707
+ previousAgentIdentifier = null;
434
708
  agentInstanceId = null;
435
709
  spanIdMap = new Map;
436
710
  pendingFinishes = new Map;
437
711
  constructor(config) {
438
712
  this.config = config;
713
+ const httpClient = new HttpClient(config);
714
+ this.agentInstanceClient = new AgentInstanceClient(httpClient);
715
+ this.agentSpanClient = new AgentSpanClient(httpClient);
716
+ this.taskExecutor = new TaskExecutor(this.actionQueue, this.processAction, {
717
+ workerCount: 1,
718
+ onError: async (error) => {
719
+ logger.error("Error processing HTTP action:", error);
720
+ }
721
+ });
722
+ this.taskExecutor.start();
723
+ }
724
+ registerSchema(schema) {
725
+ this.enqueue({ type: "schema_register", schema });
726
+ }
727
+ startAgentInstance(options) {
728
+ this.enqueue({ type: "agent_start", options });
729
+ }
730
+ finishAgentInstance() {
731
+ this.enqueue({ type: "agent_finish" });
439
732
  }
440
- async processBatch(items) {
441
- if (this.closed || items.length === 0) {
733
+ emit(span) {
734
+ this.enqueue({ type: "span_end", span });
735
+ }
736
+ finishSpan(spanId, endTime) {
737
+ this.enqueue({ type: "span_finish", spanId, endTime });
738
+ }
739
+ async close() {
740
+ this.closed = true;
741
+ await this.taskExecutor.stop();
742
+ if (this.pendingFinishes.size > 0) {
743
+ logger.warn(`Transport closed with ${this.pendingFinishes.size} pending span finish(es) that could not be processed`);
744
+ this.pendingFinishes.clear();
745
+ }
746
+ }
747
+ enqueue(action) {
748
+ if (this.closed) {
442
749
  return;
443
750
  }
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;
751
+ this.actionQueue.put(action).catch((error) => {
752
+ logger.error("Failed to enqueue HTTP action:", error);
753
+ });
754
+ }
755
+ processAction = async (action) => {
756
+ switch (action.type) {
757
+ case "schema_register": {
758
+ const incomingSchema = JSON.stringify(action.schema);
759
+ if (this.previousAgentSchema !== null && this.previousAgentSchema !== incomingSchema) {
760
+ this.requiresNewAgentIdentifier = true;
761
+ this.previousAgentIdentifier = this.config.agentIdentifier;
762
+ this.agentInstanceId = null;
763
+ }
764
+ this.previousAgentSchema = incomingSchema;
765
+ this.config.agentSchema = action.schema;
766
+ return;
450
767
  }
451
- }
452
- for (const item of items) {
453
- try {
454
- switch (item.type) {
455
- case "schema_register":
456
- break;
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;
463
- await this.startAgentInstanceHttp();
464
- break;
465
- case "agent_finish":
466
- await this.finishAgentInstanceHttp();
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;
768
+ case "agent_start": {
769
+ if (this.requiresNewAgentIdentifier) {
770
+ const nextAgentIdentifier = action.options?.agentIdentifier;
771
+ if (nextAgentIdentifier === undefined || nextAgentIdentifier === this.previousAgentIdentifier) {
772
+ logger.error("Schema changed; starting an agent requires a new agentIdentifier value.");
773
+ return;
484
774
  }
775
+ this.requiresNewAgentIdentifier = false;
776
+ this.previousAgentIdentifier = null;
485
777
  }
486
- } catch (error) {
487
- logger.error("Error processing batch item:", error);
778
+ if (action.options?.agentId !== undefined)
779
+ this.config.agentId = action.options.agentId;
780
+ if (action.options?.agentIdentifier !== undefined) {
781
+ this.config.agentIdentifier = action.options.agentIdentifier;
782
+ }
783
+ if (action.options?.agentName !== undefined)
784
+ this.config.agentName = action.options.agentName;
785
+ if (action.options?.agentDescription !== undefined) {
786
+ this.config.agentDescription = action.options.agentDescription;
787
+ }
788
+ await this.startAgentInstanceHttp();
789
+ return;
790
+ }
791
+ case "agent_finish":
792
+ await this.finishAgentInstanceHttp();
793
+ return;
794
+ case "span_end":
795
+ if (!this.agentInstanceId) {
796
+ await this.ensureAgentRegistered();
797
+ }
798
+ await this.sendSpan(action.span);
799
+ return;
800
+ case "span_finish": {
801
+ const backendSpanId = this.spanIdMap.get(action.spanId);
802
+ if (backendSpanId) {
803
+ const timestamp = new Date(action.endTime).toISOString();
804
+ await this.finishSpanHttp({ spanId: action.spanId, timestamp });
805
+ } else {
806
+ this.pendingFinishes.set(action.spanId, action.endTime);
807
+ }
808
+ return;
488
809
  }
489
810
  }
490
- }
811
+ };
491
812
  async processPendingFinishes(spanId) {
813
+ if (!this.pendingFinishes.has(spanId)) {
814
+ return;
815
+ }
492
816
  const pendingEndTime = this.pendingFinishes.get(spanId);
493
- if (!pendingEndTime) {
817
+ if (pendingEndTime === undefined) {
494
818
  return;
495
819
  }
496
820
  try {
@@ -501,56 +825,32 @@ class HttpTransport {
501
825
  logger.error("Error processing pending span finish:", error);
502
826
  }
503
827
  }
504
- async sendSpan(span, retry = 0) {
505
- const url = `${this.config.apiUrl}/api/v1/agent_spans`;
828
+ async sendSpan(span) {
506
829
  const payload = this.transformSpanToApiFormat(span);
507
830
  try {
508
- const response = await fetch(url, {
509
- method: "POST",
510
- headers: {
511
- Authorization: `Bearer ${this.config.apiToken}`,
512
- "Content-Type": "application/json"
513
- },
514
- body: JSON.stringify(payload),
515
- signal: AbortSignal.timeout(this.config.requestTimeout)
516
- });
517
- if (response.ok) {
518
- const data = await response.json();
519
- const backendSpanId = data?.details?.id;
520
- if (backendSpanId) {
521
- this.spanIdMap.set(span.spanId, backendSpanId);
522
- await this.processPendingFinishes(span.spanId);
523
- }
831
+ const response = await this.agentSpanClient.create(payload);
832
+ const backendSpanId = response.details?.id;
833
+ if (!backendSpanId) {
524
834
  return;
525
835
  }
526
- if ((response.status >= 500 || response.status === 429) && retry < this.config.maxRetries) {
527
- const delay = Math.min(this.config.initialRetryDelay * this.config.retryMultiplier ** retry, this.config.maxRetryDelay);
528
- logger.debug(`Retrying span send after ${delay}ms (attempt ${retry + 1})`);
529
- await new Promise((resolve) => setTimeout(resolve, delay));
530
- return this.sendSpan(span, retry + 1);
531
- }
532
- logger.error(`Failed to send span: ${response.status} ${response.statusText}`);
836
+ this.spanIdMap.set(span.spanId, backendSpanId);
837
+ await this.processPendingFinishes(span.spanId);
533
838
  } catch (error) {
534
839
  logger.error("Error sending span:", error);
535
- if (retry < this.config.maxRetries) {
536
- const delay = Math.min(this.config.initialRetryDelay * this.config.retryMultiplier ** retry, this.config.maxRetryDelay);
537
- await new Promise((resolve) => setTimeout(resolve, delay));
538
- return this.sendSpan(span, retry + 1);
539
- }
540
840
  }
541
841
  }
542
842
  transformSpanToApiFormat(span) {
543
843
  const startedAt = new Date(span.startTime).toISOString();
544
844
  const finishedAt = span.endTime ? new Date(span.endTime).toISOString() : null;
845
+ const apiStatus = this.mapStatusForApi(span.status);
545
846
  const payload = {
546
847
  span_id: span.spanId,
547
848
  trace_id: span.traceId,
548
849
  name: span.name,
549
- status: span.status,
850
+ status: apiStatus,
550
851
  inputs: span.inputs,
551
852
  outputs: span.outputs,
552
853
  metadata: span.metadata,
553
- tags: span.tags,
554
854
  token_usage: null,
555
855
  error: null
556
856
  };
@@ -573,6 +873,7 @@ class HttpTransport {
573
873
  details: {
574
874
  agent_instance_id: this.agentInstanceId,
575
875
  schema_name: span.spanType,
876
+ status: apiStatus,
576
877
  payload,
577
878
  parent_span_id: parentSpanId,
578
879
  started_at: startedAt,
@@ -580,14 +881,22 @@ class HttpTransport {
580
881
  }
581
882
  };
582
883
  }
583
- getDefaultSchema() {
584
- return DEFAULT_AGENT_SCHEMA;
884
+ mapStatusForApi(status) {
885
+ switch (status) {
886
+ case "running":
887
+ return "active";
888
+ case "success":
889
+ return "complete";
890
+ case "error":
891
+ return "failed";
892
+ default:
893
+ return "active";
894
+ }
585
895
  }
586
896
  async ensureAgentRegistered() {
587
897
  if (this.agentInstanceId) {
588
- return;
898
+ return true;
589
899
  }
590
- const url = `${this.config.apiUrl}/api/v1/agent_instance/register`;
591
900
  const payload = {};
592
901
  if (this.config.agentId)
593
902
  payload.agent_id = this.config.agentId;
@@ -598,61 +907,25 @@ class HttpTransport {
598
907
  description: this.config.agentDescription || ""
599
908
  };
600
909
  }
601
- if (this.config.skipSchema) {
602
- logger.debug("Skipping schema in registration (skipSchema=true)");
603
- } else if (this.config.agentSchema) {
604
- logger.debug("Using custom agent schema");
910
+ if (this.config.agentSchema) {
605
911
  payload.agent_schema_version = this.config.agentSchema;
606
- } else if (this.config.agentSchemaIdentifier) {
607
- logger.debug(`Using schema version: ${this.config.agentSchemaIdentifier}`);
608
- payload.agent_schema_version = {
609
- external_identifier: this.config.agentSchemaIdentifier
610
- };
611
- } else {
612
- logger.debug("Using default hardcoded schema (v1.0.0)");
613
- payload.agent_schema_version = this.getDefaultSchema();
614
912
  }
615
913
  try {
616
- const response = await fetch(url, {
617
- method: "POST",
618
- headers: {
619
- Authorization: `Bearer ${this.config.apiToken}`,
620
- "Content-Type": "application/json"
621
- },
622
- body: JSON.stringify(payload),
623
- signal: AbortSignal.timeout(this.config.requestTimeout)
624
- });
625
- if (response.ok) {
626
- const data = await response.json();
627
- this.agentInstanceId = data?.details?.id ?? null;
628
- logger.debug(`Registered agent instance: ${this.agentInstanceId}`);
629
- } else {
630
- logger.error(`Failed to register agent: ${response.status} ${response.statusText}`);
631
- }
914
+ const data = await this.agentInstanceClient.register(payload);
915
+ this.agentInstanceId = data.details?.id ?? null;
632
916
  } catch (error) {
633
917
  logger.error("Error registering agent:", error);
634
918
  }
919
+ return this.agentInstanceId !== null;
635
920
  }
636
921
  async startAgentInstanceHttp() {
637
- await this.ensureAgentRegistered();
638
- if (!this.agentInstanceId) {
922
+ const isRegistered = await this.ensureAgentRegistered();
923
+ if (!isRegistered || !this.agentInstanceId) {
639
924
  logger.error("Cannot start agent instance: not registered");
640
925
  return;
641
926
  }
642
- const url = `${this.config.apiUrl}/api/v1/agent_instance/${this.agentInstanceId}/start`;
643
927
  try {
644
- const response = await fetch(url, {
645
- method: "POST",
646
- headers: {
647
- Authorization: `Bearer ${this.config.apiToken}`,
648
- "Content-Type": "application/json"
649
- },
650
- body: JSON.stringify({}),
651
- signal: AbortSignal.timeout(this.config.requestTimeout)
652
- });
653
- if (!response.ok) {
654
- logger.error(`Failed to start agent instance: ${response.status} ${response.statusText}`);
655
- }
928
+ await this.agentInstanceClient.start(this.agentInstanceId);
656
929
  } catch (error) {
657
930
  logger.error("Error starting agent instance:", error);
658
931
  }
@@ -662,20 +935,8 @@ class HttpTransport {
662
935
  logger.error("Cannot finish agent instance: not registered");
663
936
  return;
664
937
  }
665
- const url = `${this.config.apiUrl}/api/v1/agent_instance/${this.agentInstanceId}/finish`;
666
938
  try {
667
- const response = await fetch(url, {
668
- method: "POST",
669
- headers: {
670
- Authorization: `Bearer ${this.config.apiToken}`,
671
- "Content-Type": "application/json"
672
- },
673
- body: JSON.stringify({}),
674
- signal: AbortSignal.timeout(this.config.requestTimeout)
675
- });
676
- if (!response.ok) {
677
- logger.error(`Failed to finish agent instance: ${response.status} ${response.statusText}`);
678
- }
939
+ await this.agentInstanceClient.finish(this.agentInstanceId);
679
940
  } catch (error) {
680
941
  logger.error("Error finishing agent instance:", error);
681
942
  }
@@ -687,33 +948,39 @@ class HttpTransport {
687
948
  logger.warn(`Cannot finish span ${data.spanId}: backend ID not found`);
688
949
  return;
689
950
  }
690
- const url = `${this.config.apiUrl}/api/v1/agent_spans/${backendSpanId}/finish`;
691
951
  try {
692
- const response = await fetch(url, {
693
- method: "POST",
694
- headers: {
695
- Authorization: `Bearer ${this.config.apiToken}`,
696
- "Content-Type": "application/json"
697
- },
698
- body: JSON.stringify({ timestamp: data.timestamp }),
699
- signal: AbortSignal.timeout(this.config.requestTimeout)
700
- });
701
- if (!response.ok) {
702
- logger.error(`Failed to finish span: ${response.status} ${response.statusText}`);
703
- }
952
+ await this.agentSpanClient.finish(backendSpanId, data.timestamp);
704
953
  } catch (error) {
705
954
  logger.error("Error finishing span:", error);
706
955
  }
707
956
  }
708
- async close() {
709
- this.closed = true;
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();
957
+ }
958
+
959
+ // packages/core/src/create-core.ts
960
+ function createCore(config) {
961
+ if (!config.httpConfig) {
962
+ throw new Error("HTTP transport requires httpConfig to be provided in configuration");
963
+ }
964
+ const httpConfig = HttpTransportConfigSchema.parse(config.httpConfig);
965
+ const transport = new HttpTransport(httpConfig);
966
+ let partition;
967
+ if (config.httpConfig.agentId) {
968
+ try {
969
+ partition = import_pfid2.extractPartition(config.httpConfig.agentId);
970
+ } catch {
971
+ partition = undefined;
713
972
  }
714
973
  }
974
+ const tracer = new Tracer(transport, partition);
975
+ const allowUnregisteredSchema = Boolean(config.httpConfig.agentSchema);
976
+ const agentManager = new AgentInstanceManager(transport, {
977
+ allowUnregisteredSchema
978
+ });
979
+ const shutdown = async () => {
980
+ await tracer.close();
981
+ };
982
+ return { tracer, agentManager, shutdown };
715
983
  }
716
-
717
984
  // packages/core/src/utils/serialization.ts
718
985
  function truncateString(value, maxLength) {
719
986
  if (value.length <= maxLength) {
@@ -748,151 +1015,4 @@ function serializeValue(value, maxLength = 1e4) {
748
1015
  }
749
1016
  }
750
1017
 
751
- // packages/core/src/transport/stdio.ts
752
- class StdioTransport {
753
- closed = false;
754
- writeLock = Promise.resolve();
755
- async processBatch(items) {
756
- if (this.closed || items.length === 0) {
757
- return;
758
- }
759
- this.writeLock = this.writeLock.then(async () => {
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}
765
- `);
766
- } catch (error) {
767
- console.error("Failed to emit queue action to stdout:", error);
768
- }
769
- }
770
- });
771
- await this.writeLock;
772
- }
773
- async close() {
774
- this.closed = true;
775
- await this.writeLock;
776
- }
777
- }
778
-
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
1018
+ //# debugId=97A3CED64412936764756E2164756E21