@prefactor/core 0.2.0 → 0.2.2

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 (94) 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 +17 -28
  12. package/dist/create-core.js.map +1 -1
  13. package/dist/index.cjs +678 -453
  14. package/dist/index.cjs.map +20 -15
  15. package/dist/index.d.ts +4 -7
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +678 -453
  18. package/dist/index.js.map +20 -15
  19. package/dist/lifecycle.d.ts +5 -0
  20. package/dist/lifecycle.d.ts.map +1 -0
  21. package/dist/lifecycle.js +27 -0
  22. package/dist/lifecycle.js.map +1 -0
  23. package/dist/queue/actions.d.ts +15 -26
  24. package/dist/queue/actions.d.ts.map +1 -1
  25. package/dist/queue/base.d.ts +15 -3
  26. package/dist/queue/base.d.ts.map +1 -1
  27. package/dist/queue/in-memory-queue.d.ts +11 -0
  28. package/dist/queue/in-memory-queue.d.ts.map +1 -0
  29. package/dist/queue/in-memory-queue.js +46 -0
  30. package/dist/queue/in-memory-queue.js.map +1 -0
  31. package/dist/queue/task-executor.d.ts +18 -0
  32. package/dist/queue/task-executor.d.ts.map +1 -0
  33. package/dist/queue/task-executor.js +77 -0
  34. package/dist/queue/task-executor.js.map +1 -0
  35. package/dist/tracing/active-tracer.d.ts +5 -0
  36. package/dist/tracing/active-tracer.d.ts.map +1 -0
  37. package/dist/tracing/active-tracer.js +13 -0
  38. package/dist/tracing/active-tracer.js.map +1 -0
  39. package/dist/tracing/span.d.ts +7 -14
  40. package/dist/tracing/span.d.ts.map +1 -1
  41. package/dist/tracing/span.js +5 -36
  42. package/dist/tracing/span.js.map +1 -1
  43. package/dist/tracing/tracer.d.ts +6 -7
  44. package/dist/tracing/tracer.d.ts.map +1 -1
  45. package/dist/tracing/tracer.js +27 -12
  46. package/dist/tracing/tracer.js.map +1 -1
  47. package/dist/tracing/with-span.d.ts +4 -0
  48. package/dist/tracing/with-span.d.ts.map +1 -0
  49. package/dist/tracing/with-span.js +35 -0
  50. package/dist/tracing/with-span.js.map +1 -0
  51. package/dist/transport/http/agent-instance-client.d.ts +23 -0
  52. package/dist/transport/http/agent-instance-client.d.ts.map +1 -0
  53. package/dist/transport/http/agent-instance-client.js +25 -0
  54. package/dist/transport/http/agent-instance-client.js.map +1 -0
  55. package/dist/transport/http/agent-span-client.d.ts +25 -0
  56. package/dist/transport/http/agent-span-client.d.ts.map +1 -0
  57. package/dist/transport/http/agent-span-client.js +37 -0
  58. package/dist/transport/http/agent-span-client.js.map +1 -0
  59. package/dist/transport/http/http-client.d.ts +43 -0
  60. package/dist/transport/http/http-client.d.ts.map +1 -0
  61. package/dist/transport/http/http-client.js +127 -0
  62. package/dist/transport/http/http-client.js.map +1 -0
  63. package/dist/transport/http/retry-policy.d.ts +4 -0
  64. package/dist/transport/http/retry-policy.d.ts.map +1 -0
  65. package/dist/transport/http/retry-policy.js +10 -0
  66. package/dist/transport/http/retry-policy.js.map +1 -0
  67. package/dist/transport/http.d.ts +34 -50
  68. package/dist/transport/http.d.ts.map +1 -1
  69. package/dist/transport/http.js +163 -227
  70. package/dist/transport/http.js.map +1 -1
  71. package/dist/utils/logging.d.ts.map +1 -1
  72. package/dist/utils/logging.js +7 -1
  73. package/dist/utils/logging.js.map +1 -1
  74. package/package.json +1 -1
  75. package/dist/agent/schema-registry.d.ts +0 -9
  76. package/dist/agent/schema-registry.d.ts.map +0 -1
  77. package/dist/agent/schema-registry.js +0 -16
  78. package/dist/agent/schema-registry.js.map +0 -1
  79. package/dist/queue/in-memory.d.ts +0 -9
  80. package/dist/queue/in-memory.d.ts.map +0 -1
  81. package/dist/queue/in-memory.js +0 -18
  82. package/dist/queue/in-memory.js.map +0 -1
  83. package/dist/transport/base.d.ts +0 -18
  84. package/dist/transport/base.d.ts.map +0 -1
  85. package/dist/transport/base.js +0 -2
  86. package/dist/transport/base.js.map +0 -1
  87. package/dist/transport/stdio.d.ts +0 -36
  88. package/dist/transport/stdio.d.ts.map +0 -1
  89. package/dist/transport/stdio.js +0 -56
  90. package/dist/transport/stdio.js.map +0 -1
  91. package/dist/transport/worker.d.ts +0 -22
  92. package/dist/transport/worker.d.ts.map +0 -1
  93. package/dist/transport/worker.js +0 -85
  94. package/dist/transport/worker.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -29,115 +29,84 @@ var __export = (target, all) => {
29
29
  // packages/core/src/index.ts
30
30
  var exports_src = {};
31
31
  __export(exports_src, {
32
+ withSpan: () => withSpan,
32
33
  truncateString: () => truncateString,
34
+ shutdown: () => shutdown,
33
35
  serializeValue: () => serializeValue,
36
+ registerShutdownHandler: () => registerShutdownHandler,
34
37
  getLogger: () => getLogger,
35
38
  createCore: () => createCore,
36
39
  createConfig: () => createConfig,
37
40
  configureLogging: () => configureLogging,
38
41
  Tracer: () => Tracer,
39
- StdioTransport: () => StdioTransport,
40
42
  SpanType: () => SpanType,
41
43
  SpanStatus: () => SpanStatus,
42
44
  SpanContext: () => SpanContext,
43
- SchemaRegistry: () => SchemaRegistry,
44
45
  PartialHttpConfigSchema: () => PartialHttpConfigSchema,
45
- InMemoryQueue: () => InMemoryQueue,
46
46
  HttpTransportConfigSchema: () => HttpTransportConfigSchema,
47
47
  HttpTransport: () => HttpTransport,
48
- DEFAULT_AGENT_SCHEMA: () => DEFAULT_AGENT_SCHEMA,
49
48
  ConfigSchema: () => ConfigSchema,
50
49
  AgentInstanceManager: () => AgentInstanceManager
51
50
  });
52
51
  module.exports = __toCommonJS(exports_src);
53
52
 
54
53
  // 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
54
  class AgentInstanceManager {
100
- queue;
55
+ transport;
101
56
  options;
102
- schemaRegistry = new SchemaRegistry;
103
- constructor(queue, options) {
104
- this.queue = queue;
57
+ registeredSchema = null;
58
+ constructor(transport, options) {
59
+ this.transport = transport;
105
60
  this.options = options;
106
61
  }
107
62
  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
- }
63
+ if (this.registeredSchema === null) {
64
+ this.registeredSchema = schema;
65
+ this.transport.registerSchema(schema);
113
66
  return;
114
67
  }
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 });
68
+ const existingSchema = stableStringify(this.registeredSchema);
69
+ const incomingSchema = stableStringify(schema);
70
+ if (existingSchema !== incomingSchema) {
71
+ console.warn("A different schema was provided after registration; ignoring subsequent schema.");
72
+ }
122
73
  }
123
74
  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.`);
75
+ if (!this.options.allowUnregisteredSchema && this.registeredSchema === null) {
76
+ console.warn("Schema must be registered before starting an agent instance.");
126
77
  return;
127
78
  }
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 });
79
+ this.transport.startAgentInstance(options);
134
80
  }
135
81
  finishInstance() {
136
- this.queue.enqueue({ type: "agent_finish", data: {} });
82
+ this.transport.finishAgentInstance();
137
83
  }
138
84
  }
85
+ function stableStringify(value) {
86
+ return JSON.stringify(normalizeValue(value));
87
+ }
88
+ function normalizeValue(value) {
89
+ if (Array.isArray(value)) {
90
+ return value.map((entry) => normalizeValue(entry));
91
+ }
92
+ if (value && typeof value === "object") {
93
+ const normalized = {};
94
+ const objectValue = value;
95
+ const keys = Object.keys(objectValue).sort((a, b) => a.localeCompare(b));
96
+ for (const key of keys) {
97
+ normalized[key] = normalizeValue(objectValue[key]);
98
+ }
99
+ return normalized;
100
+ }
101
+ return value;
102
+ }
139
103
  // packages/core/src/config.ts
140
104
  var import_zod = require("zod");
105
+ var DEFAULT_RETRY_ON_STATUS_CODES = [
106
+ 429,
107
+ ...Array.from({ length: 100 }, (_, index) => 500 + index)
108
+ ];
109
+ var HttpStatusCodeSchema = import_zod.z.number().int().min(100).max(599);
141
110
  var HttpTransportConfigSchema = import_zod.z.object({
142
111
  apiUrl: import_zod.z.string().url(),
143
112
  apiToken: import_zod.z.string().min(1),
@@ -145,17 +114,13 @@ var HttpTransportConfigSchema = import_zod.z.object({
145
114
  agentIdentifier: import_zod.z.string().default("v1.0.0"),
146
115
  agentName: import_zod.z.string().optional(),
147
116
  agentDescription: import_zod.z.string().optional(),
148
- schemaName: import_zod.z.string().optional(),
149
- schemaIdentifier: import_zod.z.string().optional(),
150
117
  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
118
  requestTimeout: import_zod.z.number().positive().default(30000),
154
- connectTimeout: import_zod.z.number().positive().default(1e4),
155
119
  maxRetries: import_zod.z.number().int().nonnegative().default(3),
156
120
  initialRetryDelay: import_zod.z.number().positive().default(1000),
157
121
  maxRetryDelay: import_zod.z.number().positive().default(60000),
158
- retryMultiplier: import_zod.z.number().positive().default(2)
122
+ retryMultiplier: import_zod.z.number().positive().default(2),
123
+ retryOnStatusCodes: import_zod.z.array(HttpStatusCodeSchema).default([...DEFAULT_RETRY_ON_STATUS_CODES])
159
124
  });
160
125
  var PartialHttpConfigSchema = import_zod.z.object({
161
126
  apiUrl: import_zod.z.string().url(),
@@ -164,20 +129,16 @@ var PartialHttpConfigSchema = import_zod.z.object({
164
129
  agentIdentifier: import_zod.z.string().optional(),
165
130
  agentName: import_zod.z.string().optional(),
166
131
  agentDescription: import_zod.z.string().optional(),
167
- schemaName: import_zod.z.string().optional(),
168
- schemaIdentifier: import_zod.z.string().optional(),
169
132
  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
133
  requestTimeout: import_zod.z.number().positive().optional(),
173
- connectTimeout: import_zod.z.number().positive().optional(),
174
134
  maxRetries: import_zod.z.number().int().nonnegative().optional(),
175
135
  initialRetryDelay: import_zod.z.number().positive().optional(),
176
136
  maxRetryDelay: import_zod.z.number().positive().optional(),
177
- retryMultiplier: import_zod.z.number().positive().optional()
137
+ retryMultiplier: import_zod.z.number().positive().optional(),
138
+ retryOnStatusCodes: import_zod.z.array(HttpStatusCodeSchema).optional()
178
139
  });
179
140
  var ConfigSchema = import_zod.z.object({
180
- transportType: import_zod.z.enum(["stdio", "http"]).default("stdio"),
141
+ transportType: import_zod.z.enum(["http"]).default("http"),
181
142
  sampleRate: import_zod.z.number().min(0).max(1).default(1),
182
143
  captureInputs: import_zod.z.boolean().default(true),
183
144
  captureOutputs: import_zod.z.boolean().default(true),
@@ -186,37 +147,69 @@ var ConfigSchema = import_zod.z.object({
186
147
  httpConfig: PartialHttpConfigSchema.optional()
187
148
  });
188
149
  function createConfig(options) {
150
+ const retryOnStatusCodesFromEnv = parseRetryOnStatusCodesEnv(process.env.PREFACTOR_RETRY_ON_STATUS_CODES);
189
151
  const config = {
190
- transportType: options?.transportType ?? process.env.PREFACTOR_TRANSPORT ?? "stdio",
152
+ transportType: options?.transportType ?? process.env.PREFACTOR_TRANSPORT ?? "http",
191
153
  sampleRate: options?.sampleRate ?? parseFloat(process.env.PREFACTOR_SAMPLE_RATE ?? "1.0"),
192
154
  captureInputs: options?.captureInputs ?? process.env.PREFACTOR_CAPTURE_INPUTS !== "false",
193
155
  captureOutputs: options?.captureOutputs ?? process.env.PREFACTOR_CAPTURE_OUTPUTS !== "false",
194
156
  maxInputLength: options?.maxInputLength ?? parseInt(process.env.PREFACTOR_MAX_INPUT_LENGTH ?? "10000", 10),
195
157
  maxOutputLength: options?.maxOutputLength ?? parseInt(process.env.PREFACTOR_MAX_OUTPUT_LENGTH ?? "10000", 10),
196
- httpConfig: options?.httpConfig
158
+ httpConfig: options?.httpConfig ? {
159
+ ...options.httpConfig,
160
+ retryOnStatusCodes: options.httpConfig.retryOnStatusCodes ?? retryOnStatusCodesFromEnv
161
+ } : undefined
197
162
  };
198
163
  return ConfigSchema.parse(config);
199
164
  }
165
+ function parseRetryOnStatusCodesEnv(value) {
166
+ if (!value) {
167
+ return;
168
+ }
169
+ const parsedCodes = value.split(",").map((status) => status.trim()).filter((status) => /^\d{3}$/.test(status)).map((status) => Number(status)).filter((status) => status >= 100 && status <= 599);
170
+ return parsedCodes.length > 0 ? parsedCodes : undefined;
171
+ }
200
172
  // packages/core/src/create-core.ts
201
173
  var import_pfid2 = require("@prefactor/pfid");
202
174
 
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);
175
+ // packages/core/src/tracing/active-tracer.ts
176
+ var activeTracer;
177
+ function setActiveTracer(tracer) {
178
+ activeTracer = tracer;
179
+ }
180
+ function getActiveTracer() {
181
+ return activeTracer;
182
+ }
183
+ function clearActiveTracer(tracer) {
184
+ if (!tracer || activeTracer === tracer) {
185
+ activeTracer = undefined;
213
186
  }
214
- size() {
215
- return this.items.length;
187
+ }
188
+
189
+ // packages/core/src/lifecycle.ts
190
+ var shutdownHandlers = new Map;
191
+ var activeCoreRuntime = null;
192
+ function setActiveCoreRuntime(runtime) {
193
+ activeCoreRuntime = runtime;
194
+ }
195
+ function registerShutdownHandler(key, handler) {
196
+ shutdownHandlers.set(key, handler);
197
+ return () => shutdownHandlers.delete(key);
198
+ }
199
+ async function shutdown() {
200
+ for (const handler of shutdownHandlers.values()) {
201
+ try {
202
+ await handler();
203
+ } catch (error) {
204
+ console.error("Error during shutdown handler execution:", error);
205
+ }
216
206
  }
217
- async flush() {
218
- return;
207
+ if (activeCoreRuntime) {
208
+ const runtime = activeCoreRuntime;
209
+ activeCoreRuntime = null;
210
+ await runtime.shutdown();
219
211
  }
212
+ clearActiveTracer();
220
213
  }
221
214
 
222
215
  // packages/core/src/tracing/tracer.ts
@@ -267,38 +260,11 @@ class SpanContext {
267
260
  }
268
261
 
269
262
  // 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
- }
263
+ var SpanType = {
264
+ AGENT: "agent",
265
+ LLM: "llm",
266
+ TOOL: "tool",
267
+ CHAIN: "chain"
302
268
  };
303
269
  var SpanStatus;
304
270
  ((SpanStatus2) => {
@@ -309,10 +275,10 @@ var SpanStatus;
309
275
 
310
276
  // packages/core/src/tracing/tracer.ts
311
277
  class Tracer {
312
- queue;
278
+ transport;
313
279
  partition;
314
- constructor(queue, partition) {
315
- this.queue = queue;
280
+ constructor(transport, partition) {
281
+ this.transport = transport;
316
282
  this.partition = partition ?? import_pfid.generatePartition();
317
283
  }
318
284
  startSpan(options) {
@@ -332,14 +298,13 @@ class Tracer {
332
298
  outputs: null,
333
299
  tokenUsage: null,
334
300
  error: null,
335
- metadata: options.metadata ?? {},
336
- tags: options.tags ?? []
301
+ metadata: options.metadata ?? {}
337
302
  };
338
- if (options.spanType === "agent" /* AGENT */) {
303
+ if (options.spanType === SpanType.AGENT) {
339
304
  try {
340
- this.queue.enqueue({ type: "span_end", data: span });
305
+ this.transport.emit(span);
341
306
  } catch (error) {
342
- console.error("Failed to enqueue agent span:", error);
307
+ console.error("Failed to emit agent span:", error);
343
308
  }
344
309
  }
345
310
  return span;
@@ -360,28 +325,180 @@ class Tracer {
360
325
  span.status = "success" /* SUCCESS */;
361
326
  }
362
327
  try {
363
- if (span.spanType === "agent" /* AGENT */) {
364
- this.queue.enqueue({ type: "span_finish", data: { spanId: span.spanId, endTime } });
328
+ if (span.spanType === SpanType.AGENT) {
329
+ this.transport.finishSpan(span.spanId, endTime);
365
330
  } else {
366
- this.queue.enqueue({ type: "span_end", data: span });
331
+ this.transport.emit(span);
367
332
  }
368
333
  } catch (error) {
369
- console.error("Failed to enqueue span action:", error);
334
+ console.error("Failed to emit span action:", error);
370
335
  }
371
336
  }
372
337
  async close() {
373
338
  try {
374
- await this.queue.flush();
339
+ await this.transport.close();
340
+ } catch (error) {
341
+ console.error("Failed to close transport:", error);
342
+ }
343
+ }
344
+ startAgentInstance() {
345
+ try {
346
+ this.transport.startAgentInstance();
347
+ } catch (error) {
348
+ console.error("Failed to start agent instance:", error);
349
+ }
350
+ }
351
+ finishAgentInstance() {
352
+ try {
353
+ this.transport.finishAgentInstance();
375
354
  } catch (error) {
376
- console.error("Failed to flush queue:", error);
355
+ console.error("Failed to finish agent instance:", error);
356
+ }
357
+ }
358
+ }
359
+
360
+ // packages/core/src/queue/in-memory-queue.ts
361
+ class InMemoryQueue {
362
+ items = [];
363
+ waiters = [];
364
+ isClosed = false;
365
+ async put(item) {
366
+ if (this.isClosed) {
367
+ throw new Error("Cannot put item into a closed queue");
368
+ }
369
+ const waiter = this.waiters.shift();
370
+ if (waiter) {
371
+ waiter({ done: false, item });
372
+ return;
373
+ }
374
+ this.items.push({ item });
375
+ }
376
+ async get() {
377
+ if (this.items.length > 0) {
378
+ const entry = this.items.shift();
379
+ if (entry) {
380
+ return { done: false, item: entry.item };
381
+ }
382
+ }
383
+ if (this.isClosed) {
384
+ return { done: true };
385
+ }
386
+ return new Promise((resolve) => {
387
+ this.waiters.push(resolve);
388
+ });
389
+ }
390
+ close() {
391
+ if (this.isClosed) {
392
+ return;
393
+ }
394
+ this.isClosed = true;
395
+ while (this.waiters.length > 0) {
396
+ const waiter = this.waiters.shift();
397
+ if (waiter) {
398
+ waiter({ done: true });
399
+ }
400
+ }
401
+ }
402
+ size() {
403
+ return this.items.length;
404
+ }
405
+ }
406
+
407
+ // packages/core/src/queue/task-executor.ts
408
+ var DEFAULT_WORKER_COUNT = 1;
409
+ var DEFAULT_MAX_RETRIES = 0;
410
+ var DEFAULT_RETRY_DELAY_MS = 0;
411
+
412
+ class TaskExecutor {
413
+ queue;
414
+ handler;
415
+ isRunning = false;
416
+ workerPromises = [];
417
+ workerCount;
418
+ maxRetries;
419
+ retryDelayMs;
420
+ onError;
421
+ constructor(queue, handler, options = {}) {
422
+ this.queue = queue;
423
+ this.handler = handler;
424
+ this.workerCount = Math.max(options.workerCount ?? DEFAULT_WORKER_COUNT, 1);
425
+ this.maxRetries = Math.max(options.maxRetries ?? DEFAULT_MAX_RETRIES, 0);
426
+ this.retryDelayMs = Math.max(options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS, 0);
427
+ this.onError = options.onError;
428
+ }
429
+ start() {
430
+ if (this.isRunning) {
431
+ return;
432
+ }
433
+ this.isRunning = true;
434
+ this.workerPromises = Array.from({ length: this.workerCount }, () => this.runWorker());
435
+ }
436
+ async stop() {
437
+ if (!this.isRunning) {
438
+ return;
439
+ }
440
+ this.isRunning = false;
441
+ this.queue.close();
442
+ await Promise.all(this.workerPromises);
443
+ this.workerPromises = [];
444
+ }
445
+ async runWorker() {
446
+ while (true) {
447
+ const result = await this.queue.get();
448
+ if (result.done) {
449
+ return;
450
+ }
451
+ await this.executeWithRetry(result.item);
452
+ }
453
+ }
454
+ async executeWithRetry(item) {
455
+ let attempt = 0;
456
+ while (attempt <= this.maxRetries) {
457
+ try {
458
+ await this.handler(item);
459
+ return;
460
+ } catch (error) {
461
+ if (attempt >= this.maxRetries) {
462
+ await this.safeOnError(error, item);
463
+ return;
464
+ }
465
+ if (this.retryDelayMs > 0) {
466
+ await new Promise((resolve) => setTimeout(resolve, this.retryDelayMs));
467
+ }
468
+ }
469
+ attempt += 1;
470
+ }
471
+ }
472
+ async safeOnError(error, item) {
473
+ if (!this.onError) {
474
+ return;
475
+ }
476
+ try {
477
+ await this.onError(error, item);
478
+ } catch {
479
+ return;
377
480
  }
378
481
  }
379
482
  }
380
483
 
381
484
  // packages/core/src/utils/logging.ts
485
+ var LogLevel;
486
+ ((LogLevel2) => {
487
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
488
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
489
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
490
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
491
+ })(LogLevel ||= {});
492
+
382
493
  class Logger {
383
494
  namespace;
384
- static level = 1 /* INFO */;
495
+ static level = (() => {
496
+ const level = process.env.PREFACTOR_LOG_LEVEL?.toUpperCase();
497
+ if (level && level in LogLevel) {
498
+ return LogLevel[level];
499
+ }
500
+ return 1 /* INFO */;
501
+ })();
385
502
  constructor(namespace) {
386
503
  this.namespace = namespace;
387
504
  }
@@ -425,72 +542,331 @@ function configureLogging() {
425
542
  }
426
543
  }
427
544
 
545
+ // packages/core/src/transport/http/agent-instance-client.ts
546
+ class AgentInstanceClient {
547
+ httpClient;
548
+ constructor(httpClient) {
549
+ this.httpClient = httpClient;
550
+ }
551
+ register(payload) {
552
+ return this.httpClient.request("/api/v1/agent_instance/register", {
553
+ method: "POST",
554
+ body: payload
555
+ });
556
+ }
557
+ async start(agentInstanceId) {
558
+ await this.httpClient.request(`/api/v1/agent_instance/${agentInstanceId}/start`, {
559
+ method: "POST",
560
+ body: {}
561
+ });
562
+ }
563
+ async finish(agentInstanceId) {
564
+ await this.httpClient.request(`/api/v1/agent_instance/${agentInstanceId}/finish`, {
565
+ method: "POST",
566
+ body: {}
567
+ });
568
+ }
569
+ }
570
+
571
+ // packages/core/src/transport/http/retry-policy.ts
572
+ var JITTER_MIN = 0.5;
573
+ function shouldRetryStatusCode(statusCode, retryOnStatusCodes) {
574
+ return retryOnStatusCodes.includes(statusCode);
575
+ }
576
+ function calculateRetryDelay(attempt, config, random = Math.random) {
577
+ const baseDelay = Math.min(config.initialRetryDelay * config.retryMultiplier ** attempt, config.maxRetryDelay);
578
+ const jitterMultiplier = JITTER_MIN + random() * JITTER_MIN;
579
+ return Math.round(baseDelay * jitterMultiplier);
580
+ }
581
+
582
+ // packages/core/src/transport/http/http-client.ts
583
+ class HttpClientError extends Error {
584
+ url;
585
+ method;
586
+ status;
587
+ statusText;
588
+ responseBody;
589
+ retryable;
590
+ constructor(message, options) {
591
+ super(message, { cause: options.cause });
592
+ this.name = "HttpClientError";
593
+ this.url = options.url;
594
+ this.method = options.method;
595
+ this.status = options.status;
596
+ this.statusText = options.statusText;
597
+ this.responseBody = options.responseBody;
598
+ this.retryable = options.retryable;
599
+ }
600
+ }
601
+
602
+ class HttpClient {
603
+ config;
604
+ fetchFn;
605
+ sleep;
606
+ random;
607
+ constructor(config, dependencies = {}) {
608
+ this.config = config;
609
+ this.fetchFn = dependencies.fetchFn ?? fetch;
610
+ this.sleep = dependencies.sleep ?? ((delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs)));
611
+ this.random = dependencies.random ?? Math.random;
612
+ }
613
+ async request(path, options = {}) {
614
+ const url = new URL(path, this.config.apiUrl).toString();
615
+ const method = options.method ?? "GET";
616
+ let attempt = 0;
617
+ while (true) {
618
+ const headers = new Headers(options.headers);
619
+ headers.set("Authorization", `Bearer ${this.config.apiToken}`);
620
+ if (options.body !== undefined && !headers.has("Content-Type")) {
621
+ headers.set("Content-Type", "application/json");
622
+ }
623
+ const requestInit = {
624
+ ...options,
625
+ method,
626
+ headers,
627
+ body: options.body === undefined ? undefined : JSON.stringify(options.body),
628
+ signal: AbortSignal.timeout(options.timeoutMs ?? this.config.requestTimeout)
629
+ };
630
+ try {
631
+ const response = await this.fetchFn(url, requestInit);
632
+ if (response.ok) {
633
+ return await parseResponseBody(response);
634
+ }
635
+ const responseBody = await parseResponseBody(response);
636
+ const canRetry = attempt < this.config.maxRetries && shouldRetryStatusCode(response.status, this.config.retryOnStatusCodes);
637
+ if (canRetry) {
638
+ const delayMs = calculateRetryDelay(attempt, this.config, this.random);
639
+ await this.sleep(delayMs);
640
+ attempt += 1;
641
+ continue;
642
+ }
643
+ throw new HttpClientError(`HTTP request failed with status ${response.status}`, {
644
+ url,
645
+ method,
646
+ status: response.status,
647
+ statusText: response.statusText,
648
+ responseBody,
649
+ retryable: shouldRetryStatusCode(response.status, this.config.retryOnStatusCodes)
650
+ });
651
+ } catch (error) {
652
+ if (error instanceof HttpClientError) {
653
+ throw error;
654
+ }
655
+ const canRetry = attempt < this.config.maxRetries && isRetryableNetworkError(error);
656
+ if (canRetry) {
657
+ const delayMs = calculateRetryDelay(attempt, this.config, this.random);
658
+ await this.sleep(delayMs);
659
+ attempt += 1;
660
+ continue;
661
+ }
662
+ throw new HttpClientError("HTTP request failed due to network error", {
663
+ url,
664
+ method,
665
+ retryable: false,
666
+ cause: error
667
+ });
668
+ }
669
+ }
670
+ }
671
+ }
672
+ function isRetryableNetworkError(error) {
673
+ if (error instanceof TypeError) {
674
+ return true;
675
+ }
676
+ if (error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError")) {
677
+ return true;
678
+ }
679
+ if (error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError")) {
680
+ return true;
681
+ }
682
+ return false;
683
+ }
684
+ async function parseResponseBody(response) {
685
+ const bodyText = await response.text();
686
+ if (!bodyText) {
687
+ return null;
688
+ }
689
+ const contentType = response.headers.get("content-type") ?? "";
690
+ if (contentType.includes("application/json")) {
691
+ try {
692
+ return JSON.parse(bodyText);
693
+ } catch {
694
+ return bodyText;
695
+ }
696
+ }
697
+ try {
698
+ return JSON.parse(bodyText);
699
+ } catch {
700
+ return bodyText;
701
+ }
702
+ }
703
+
704
+ // packages/core/src/transport/http/agent-span-client.ts
705
+ class AgentSpanClient {
706
+ httpClient;
707
+ constructor(httpClient) {
708
+ this.httpClient = httpClient;
709
+ }
710
+ create(payload) {
711
+ return this.httpClient.request("/api/v1/agent_spans", {
712
+ method: "POST",
713
+ body: payload
714
+ });
715
+ }
716
+ async finish(spanId, timestamp) {
717
+ try {
718
+ await this.httpClient.request(`/api/v1/agent_spans/${spanId}/finish`, {
719
+ method: "POST",
720
+ body: { timestamp }
721
+ });
722
+ } catch (error) {
723
+ if (error instanceof HttpClientError && error.status === 409 && isAlreadyFinishedError(error.responseBody)) {
724
+ return;
725
+ }
726
+ throw error;
727
+ }
728
+ }
729
+ }
730
+ function isAlreadyFinishedError(responseBody) {
731
+ if (!responseBody || typeof responseBody !== "object") {
732
+ return false;
733
+ }
734
+ const payload = responseBody;
735
+ return payload.code === "invalid_action";
736
+ }
737
+
428
738
  // packages/core/src/transport/http.ts
429
739
  var logger = getLogger("http-transport");
430
740
 
431
741
  class HttpTransport {
432
742
  config;
433
743
  closed = false;
744
+ actionQueue = new InMemoryQueue;
745
+ taskExecutor;
746
+ agentInstanceClient;
747
+ agentSpanClient;
748
+ previousAgentSchema = null;
749
+ requiresNewAgentIdentifier = false;
750
+ previousAgentIdentifier = null;
434
751
  agentInstanceId = null;
435
752
  spanIdMap = new Map;
436
753
  pendingFinishes = new Map;
754
+ pendingChildren = new Map;
437
755
  constructor(config) {
438
756
  this.config = config;
757
+ const httpClient = new HttpClient(config);
758
+ this.agentInstanceClient = new AgentInstanceClient(httpClient);
759
+ this.agentSpanClient = new AgentSpanClient(httpClient);
760
+ this.taskExecutor = new TaskExecutor(this.actionQueue, this.processAction, {
761
+ workerCount: 1,
762
+ onError: async (error) => {
763
+ logger.error("Error processing HTTP action:", error);
764
+ }
765
+ });
766
+ this.taskExecutor.start();
767
+ }
768
+ registerSchema(schema) {
769
+ this.enqueue({ type: "schema_register", schema });
770
+ }
771
+ startAgentInstance(options) {
772
+ this.enqueue({ type: "agent_start", options });
773
+ }
774
+ finishAgentInstance() {
775
+ this.enqueue({ type: "agent_finish" });
776
+ }
777
+ emit(span) {
778
+ this.enqueue({ type: "span_end", span });
779
+ }
780
+ finishSpan(spanId, endTime) {
781
+ this.enqueue({ type: "span_finish", spanId, endTime });
782
+ }
783
+ async close() {
784
+ this.closed = true;
785
+ await this.taskExecutor.stop();
786
+ if (this.pendingFinishes.size > 0) {
787
+ logger.warn(`Transport closed with ${this.pendingFinishes.size} pending span finish(es) that could not be processed`);
788
+ this.pendingFinishes.clear();
789
+ }
790
+ if (this.pendingChildren.size > 0) {
791
+ logger.warn(`Transport closed with ${this.pendingChildren.size} unresolved parent span reference(s)`);
792
+ this.pendingChildren.clear();
793
+ }
439
794
  }
440
- async processBatch(items) {
441
- if (this.closed || items.length === 0) {
795
+ enqueue(action) {
796
+ if (this.closed) {
442
797
  return;
443
798
  }
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;
799
+ this.actionQueue.put(action).catch((error) => {
800
+ logger.error("Failed to enqueue HTTP action:", error);
801
+ });
802
+ }
803
+ processAction = async (action) => {
804
+ switch (action.type) {
805
+ case "schema_register": {
806
+ const incomingSchema = JSON.stringify(action.schema);
807
+ if (this.previousAgentSchema !== null && this.previousAgentSchema !== incomingSchema) {
808
+ this.requiresNewAgentIdentifier = true;
809
+ this.previousAgentIdentifier = this.config.agentIdentifier;
810
+ this.agentInstanceId = null;
811
+ }
812
+ this.previousAgentSchema = incomingSchema;
813
+ this.config.agentSchema = action.schema;
814
+ return;
450
815
  }
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;
816
+ case "agent_start": {
817
+ if (this.requiresNewAgentIdentifier) {
818
+ const nextAgentIdentifier = action.options?.agentIdentifier;
819
+ if (nextAgentIdentifier === undefined || nextAgentIdentifier === this.previousAgentIdentifier) {
820
+ logger.error("Schema changed; starting an agent requires a new agentIdentifier value.");
821
+ return;
484
822
  }
823
+ this.requiresNewAgentIdentifier = false;
824
+ this.previousAgentIdentifier = null;
485
825
  }
486
- } catch (error) {
487
- logger.error("Error processing batch item:", error);
826
+ if (action.options?.agentId !== undefined)
827
+ this.config.agentId = action.options.agentId;
828
+ if (action.options?.agentIdentifier !== undefined) {
829
+ this.config.agentIdentifier = action.options.agentIdentifier;
830
+ }
831
+ if (action.options?.agentName !== undefined)
832
+ this.config.agentName = action.options.agentName;
833
+ if (action.options?.agentDescription !== undefined) {
834
+ this.config.agentDescription = action.options.agentDescription;
835
+ }
836
+ await this.startAgentInstanceHttp();
837
+ return;
838
+ }
839
+ case "agent_finish":
840
+ await this.finishAgentInstanceHttp();
841
+ return;
842
+ case "span_end":
843
+ if (!this.agentInstanceId) {
844
+ await this.ensureAgentRegistered();
845
+ }
846
+ if (action.span.parentSpanId && !this.spanIdMap.has(action.span.parentSpanId)) {
847
+ this.queuePendingChild(action.span.parentSpanId, action.span);
848
+ return;
849
+ }
850
+ await this.sendSpan(action.span);
851
+ return;
852
+ case "span_finish": {
853
+ const backendSpanId = this.spanIdMap.get(action.spanId);
854
+ if (backendSpanId) {
855
+ const timestamp = new Date(action.endTime).toISOString();
856
+ await this.finishSpanHttp({ spanId: action.spanId, timestamp });
857
+ } else {
858
+ this.pendingFinishes.set(action.spanId, action.endTime);
859
+ }
860
+ return;
488
861
  }
489
862
  }
490
- }
863
+ };
491
864
  async processPendingFinishes(spanId) {
865
+ if (!this.pendingFinishes.has(spanId)) {
866
+ return;
867
+ }
492
868
  const pendingEndTime = this.pendingFinishes.get(spanId);
493
- if (!pendingEndTime) {
869
+ if (pendingEndTime === undefined) {
494
870
  return;
495
871
  }
496
872
  try {
@@ -501,56 +877,48 @@ class HttpTransport {
501
877
  logger.error("Error processing pending span finish:", error);
502
878
  }
503
879
  }
504
- async sendSpan(span, retry = 0) {
505
- const url = `${this.config.apiUrl}/api/v1/agent_spans`;
880
+ async sendSpan(span) {
506
881
  const payload = this.transformSpanToApiFormat(span);
507
882
  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
- }
883
+ const response = await this.agentSpanClient.create(payload);
884
+ const backendSpanId = response.details?.id;
885
+ if (!backendSpanId) {
524
886
  return;
525
887
  }
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}`);
888
+ this.spanIdMap.set(span.spanId, backendSpanId);
889
+ await this.processPendingFinishes(span.spanId);
890
+ await this.processPendingChildren(span.spanId);
533
891
  } catch (error) {
534
892
  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
- }
893
+ }
894
+ }
895
+ queuePendingChild(parentSpanId, childSpan) {
896
+ const existingChildren = this.pendingChildren.get(parentSpanId) ?? [];
897
+ existingChildren.push(childSpan);
898
+ this.pendingChildren.set(parentSpanId, existingChildren);
899
+ }
900
+ async processPendingChildren(parentSpanId) {
901
+ const waitingChildren = this.pendingChildren.get(parentSpanId);
902
+ if (!waitingChildren || waitingChildren.length === 0) {
903
+ return;
904
+ }
905
+ this.pendingChildren.delete(parentSpanId);
906
+ for (const childSpan of waitingChildren) {
907
+ await this.sendSpan(childSpan);
540
908
  }
541
909
  }
542
910
  transformSpanToApiFormat(span) {
543
911
  const startedAt = new Date(span.startTime).toISOString();
544
912
  const finishedAt = span.endTime ? new Date(span.endTime).toISOString() : null;
913
+ const apiStatus = this.mapStatusForApi(span.status);
545
914
  const payload = {
546
915
  span_id: span.spanId,
547
916
  trace_id: span.traceId,
548
917
  name: span.name,
549
- status: span.status,
918
+ status: apiStatus,
550
919
  inputs: span.inputs,
551
920
  outputs: span.outputs,
552
921
  metadata: span.metadata,
553
- tags: span.tags,
554
922
  token_usage: null,
555
923
  error: null
556
924
  };
@@ -573,6 +941,7 @@ class HttpTransport {
573
941
  details: {
574
942
  agent_instance_id: this.agentInstanceId,
575
943
  schema_name: span.spanType,
944
+ status: apiStatus,
576
945
  payload,
577
946
  parent_span_id: parentSpanId,
578
947
  started_at: startedAt,
@@ -580,14 +949,22 @@ class HttpTransport {
580
949
  }
581
950
  };
582
951
  }
583
- getDefaultSchema() {
584
- return DEFAULT_AGENT_SCHEMA;
952
+ mapStatusForApi(status) {
953
+ switch (status) {
954
+ case "running":
955
+ return "active";
956
+ case "success":
957
+ return "complete";
958
+ case "error":
959
+ return "failed";
960
+ default:
961
+ return "active";
962
+ }
585
963
  }
586
964
  async ensureAgentRegistered() {
587
965
  if (this.agentInstanceId) {
588
- return;
966
+ return true;
589
967
  }
590
- const url = `${this.config.apiUrl}/api/v1/agent_instance/register`;
591
968
  const payload = {};
592
969
  if (this.config.agentId)
593
970
  payload.agent_id = this.config.agentId;
@@ -598,61 +975,25 @@ class HttpTransport {
598
975
  description: this.config.agentDescription || ""
599
976
  };
600
977
  }
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");
978
+ if (this.config.agentSchema) {
605
979
  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
980
  }
615
981
  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
- }
982
+ const data = await this.agentInstanceClient.register(payload);
983
+ this.agentInstanceId = data.details?.id ?? null;
632
984
  } catch (error) {
633
985
  logger.error("Error registering agent:", error);
634
986
  }
987
+ return this.agentInstanceId !== null;
635
988
  }
636
989
  async startAgentInstanceHttp() {
637
- await this.ensureAgentRegistered();
638
- if (!this.agentInstanceId) {
990
+ const isRegistered = await this.ensureAgentRegistered();
991
+ if (!isRegistered || !this.agentInstanceId) {
639
992
  logger.error("Cannot start agent instance: not registered");
640
993
  return;
641
994
  }
642
- const url = `${this.config.apiUrl}/api/v1/agent_instance/${this.agentInstanceId}/start`;
643
995
  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
- }
996
+ await this.agentInstanceClient.start(this.agentInstanceId);
656
997
  } catch (error) {
657
998
  logger.error("Error starting agent instance:", error);
658
999
  }
@@ -662,20 +1003,8 @@ class HttpTransport {
662
1003
  logger.error("Cannot finish agent instance: not registered");
663
1004
  return;
664
1005
  }
665
- const url = `${this.config.apiUrl}/api/v1/agent_instance/${this.agentInstanceId}/finish`;
666
1006
  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
- }
1007
+ await this.agentInstanceClient.finish(this.agentInstanceId);
679
1008
  } catch (error) {
680
1009
  logger.error("Error finishing agent instance:", error);
681
1010
  }
@@ -687,33 +1016,76 @@ class HttpTransport {
687
1016
  logger.warn(`Cannot finish span ${data.spanId}: backend ID not found`);
688
1017
  return;
689
1018
  }
690
- const url = `${this.config.apiUrl}/api/v1/agent_spans/${backendSpanId}/finish`;
691
1019
  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
- }
1020
+ await this.agentSpanClient.finish(backendSpanId, data.timestamp);
704
1021
  } catch (error) {
705
1022
  logger.error("Error finishing span:", error);
706
1023
  }
707
1024
  }
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();
1025
+ }
1026
+
1027
+ // packages/core/src/create-core.ts
1028
+ function createCore(config) {
1029
+ if (!config.httpConfig) {
1030
+ throw new Error("HTTP transport requires httpConfig to be provided in configuration");
1031
+ }
1032
+ const httpConfig = HttpTransportConfigSchema.parse(config.httpConfig);
1033
+ const transport = new HttpTransport(httpConfig);
1034
+ let partition;
1035
+ if (config.httpConfig.agentId) {
1036
+ try {
1037
+ partition = import_pfid2.extractPartition(config.httpConfig.agentId);
1038
+ } catch {
1039
+ partition = undefined;
713
1040
  }
714
1041
  }
1042
+ const tracer = new Tracer(transport, partition);
1043
+ setActiveTracer(tracer);
1044
+ const allowUnregisteredSchema = Boolean(config.httpConfig.agentSchema);
1045
+ const agentManager = new AgentInstanceManager(transport, {
1046
+ allowUnregisteredSchema
1047
+ });
1048
+ const shutdown2 = async () => {
1049
+ await tracer.close();
1050
+ clearActiveTracer(tracer);
1051
+ setActiveCoreRuntime(null);
1052
+ };
1053
+ const runtime = { tracer, agentManager, shutdown: shutdown2 };
1054
+ setActiveCoreRuntime(runtime);
1055
+ return runtime;
1056
+ }
1057
+ // packages/core/src/tracing/with-span.ts
1058
+ async function withSpan(tracerOrOptions, optionsOrFn, maybeFn) {
1059
+ const { tracer, options, fn } = resolveArgs(tracerOrOptions, optionsOrFn, maybeFn);
1060
+ const span = tracer.startSpan(options);
1061
+ try {
1062
+ const result = await SpanContext.runAsync(span, async () => await fn());
1063
+ tracer.endSpan(span);
1064
+ return result;
1065
+ } catch (error) {
1066
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
1067
+ tracer.endSpan(span, { error: normalizedError });
1068
+ throw error;
1069
+ }
1070
+ }
1071
+ function resolveArgs(tracerOrOptions, optionsOrFn, maybeFn) {
1072
+ if (maybeFn) {
1073
+ return {
1074
+ tracer: tracerOrOptions,
1075
+ options: optionsOrFn,
1076
+ fn: maybeFn
1077
+ };
1078
+ }
1079
+ const tracer = getActiveTracer();
1080
+ if (!tracer) {
1081
+ throw new Error("No active tracer found. Initialize Prefactor first or pass a tracer explicitly.");
1082
+ }
1083
+ return {
1084
+ tracer,
1085
+ options: tracerOrOptions,
1086
+ fn: optionsOrFn
1087
+ };
715
1088
  }
716
-
717
1089
  // packages/core/src/utils/serialization.ts
718
1090
  function truncateString(value, maxLength) {
719
1091
  if (value.length <= maxLength) {
@@ -748,151 +1120,4 @@ function serializeValue(value, maxLength = 1e4) {
748
1120
  }
749
1121
  }
750
1122
 
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
1123
+ //# debugId=D15C4A9B37308D5E64756E2164756E21