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