@agentuity/runtime 0.0.42 → 0.0.44

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 (134) hide show
  1. package/AGENTS.md +11 -9
  2. package/README.md +4 -4
  3. package/dist/_context.d.ts +12 -4
  4. package/dist/_context.d.ts.map +1 -1
  5. package/dist/_server.d.ts +7 -4
  6. package/dist/_server.d.ts.map +1 -1
  7. package/dist/_services.d.ts +13 -2
  8. package/dist/_services.d.ts.map +1 -1
  9. package/dist/_util.d.ts +1 -1
  10. package/dist/_util.d.ts.map +1 -1
  11. package/dist/_waituntil.d.ts +1 -3
  12. package/dist/_waituntil.d.ts.map +1 -1
  13. package/dist/agent.d.ts +41 -14
  14. package/dist/agent.d.ts.map +1 -1
  15. package/dist/app.d.ts +90 -8
  16. package/dist/app.d.ts.map +1 -1
  17. package/dist/eval.d.ts +79 -0
  18. package/dist/eval.d.ts.map +1 -0
  19. package/dist/index.d.ts +6 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/io/email.d.ts +77 -0
  22. package/dist/io/email.d.ts.map +1 -0
  23. package/dist/logger/console.d.ts +21 -1
  24. package/dist/logger/console.d.ts.map +1 -1
  25. package/dist/logger/index.d.ts +0 -1
  26. package/dist/logger/index.d.ts.map +1 -1
  27. package/dist/logger/user.d.ts +2 -2
  28. package/dist/logger/user.d.ts.map +1 -1
  29. package/dist/otel/config.d.ts +3 -1
  30. package/dist/otel/config.d.ts.map +1 -1
  31. package/dist/otel/console.d.ts +2 -1
  32. package/dist/otel/console.d.ts.map +1 -1
  33. package/dist/otel/exporters/index.d.ts +4 -0
  34. package/dist/otel/exporters/index.d.ts.map +1 -0
  35. package/dist/otel/exporters/jsonl-log-exporter.d.ts +36 -0
  36. package/dist/otel/exporters/jsonl-log-exporter.d.ts.map +1 -0
  37. package/dist/otel/exporters/jsonl-metric-exporter.d.ts +40 -0
  38. package/dist/otel/exporters/jsonl-metric-exporter.d.ts.map +1 -0
  39. package/dist/otel/exporters/jsonl-trace-exporter.d.ts +36 -0
  40. package/dist/otel/exporters/jsonl-trace-exporter.d.ts.map +1 -0
  41. package/dist/otel/http.d.ts.map +1 -1
  42. package/dist/otel/logger.d.ts +15 -11
  43. package/dist/otel/logger.d.ts.map +1 -1
  44. package/dist/otel/otel.d.ts +8 -2
  45. package/dist/otel/otel.d.ts.map +1 -1
  46. package/dist/router.d.ts +4 -1
  47. package/dist/router.d.ts.map +1 -1
  48. package/dist/services/evalrun/composite.d.ts +21 -0
  49. package/dist/services/evalrun/composite.d.ts.map +1 -0
  50. package/dist/services/evalrun/http.d.ts +24 -0
  51. package/dist/services/evalrun/http.d.ts.map +1 -0
  52. package/dist/services/evalrun/index.d.ts +5 -0
  53. package/dist/services/evalrun/index.d.ts.map +1 -0
  54. package/dist/services/evalrun/json.d.ts +21 -0
  55. package/dist/services/evalrun/json.d.ts.map +1 -0
  56. package/dist/services/evalrun/local.d.ts +19 -0
  57. package/dist/services/evalrun/local.d.ts.map +1 -0
  58. package/dist/services/local/_db.d.ts +4 -0
  59. package/dist/services/local/_db.d.ts.map +1 -0
  60. package/dist/services/local/_router.d.ts +3 -0
  61. package/dist/services/local/_router.d.ts.map +1 -0
  62. package/dist/services/local/_util.d.ts +18 -0
  63. package/dist/services/local/_util.d.ts.map +1 -0
  64. package/dist/services/local/index.d.ts +8 -0
  65. package/dist/services/local/index.d.ts.map +1 -0
  66. package/dist/services/local/keyvalue.d.ts +10 -0
  67. package/dist/services/local/keyvalue.d.ts.map +1 -0
  68. package/dist/services/local/objectstore.d.ts +11 -0
  69. package/dist/services/local/objectstore.d.ts.map +1 -0
  70. package/dist/services/local/stream.d.ts +10 -0
  71. package/dist/services/local/stream.d.ts.map +1 -0
  72. package/dist/services/local/vector.d.ts +13 -0
  73. package/dist/services/local/vector.d.ts.map +1 -0
  74. package/dist/services/session/composite.d.ts +21 -0
  75. package/dist/services/session/composite.d.ts.map +1 -0
  76. package/dist/services/session/http.d.ts +23 -0
  77. package/dist/services/session/http.d.ts.map +1 -0
  78. package/dist/services/session/index.d.ts +5 -0
  79. package/dist/services/session/index.d.ts.map +1 -0
  80. package/dist/services/session/json.d.ts +22 -0
  81. package/dist/services/session/json.d.ts.map +1 -0
  82. package/dist/services/session/local.d.ts +19 -0
  83. package/dist/services/session/local.d.ts.map +1 -0
  84. package/dist/session.d.ts +70 -0
  85. package/dist/session.d.ts.map +1 -0
  86. package/package.json +10 -6
  87. package/src/_config.ts +1 -1
  88. package/src/_context.ts +19 -16
  89. package/src/_server.ts +284 -42
  90. package/src/_services.ts +147 -34
  91. package/src/_util.ts +2 -3
  92. package/src/_waituntil.ts +5 -153
  93. package/src/agent.ts +667 -65
  94. package/src/app.ts +159 -13
  95. package/src/eval.ts +95 -0
  96. package/src/index.ts +6 -1
  97. package/src/io/email.ts +173 -0
  98. package/src/logger/console.ts +222 -15
  99. package/src/logger/index.ts +0 -1
  100. package/src/logger/user.ts +8 -4
  101. package/src/otel/config.ts +7 -44
  102. package/src/otel/console.ts +9 -4
  103. package/src/otel/exporters/README.md +217 -0
  104. package/src/otel/exporters/index.ts +3 -0
  105. package/src/otel/exporters/jsonl-log-exporter.ts +113 -0
  106. package/src/otel/exporters/jsonl-metric-exporter.ts +120 -0
  107. package/src/otel/exporters/jsonl-trace-exporter.ts +121 -0
  108. package/src/otel/http.ts +3 -1
  109. package/src/otel/logger.ts +106 -41
  110. package/src/otel/otel.ts +43 -22
  111. package/src/router.ts +44 -4
  112. package/src/services/evalrun/composite.ts +34 -0
  113. package/src/services/evalrun/http.ts +112 -0
  114. package/src/services/evalrun/index.ts +4 -0
  115. package/src/services/evalrun/json.ts +46 -0
  116. package/src/services/evalrun/local.ts +28 -0
  117. package/src/services/local/README.md +1576 -0
  118. package/src/services/local/_db.ts +182 -0
  119. package/src/services/local/_router.ts +86 -0
  120. package/src/services/local/_util.ts +49 -0
  121. package/src/services/local/index.ts +7 -0
  122. package/src/services/local/keyvalue.ts +118 -0
  123. package/src/services/local/objectstore.ts +152 -0
  124. package/src/services/local/stream.ts +296 -0
  125. package/src/services/local/vector.ts +264 -0
  126. package/src/services/session/composite.ts +33 -0
  127. package/src/services/session/http.ts +64 -0
  128. package/src/services/session/index.ts +4 -0
  129. package/src/services/session/json.ts +42 -0
  130. package/src/services/session/local.ts +28 -0
  131. package/src/session.ts +284 -0
  132. package/dist/_unauthenticated.d.ts +0 -26
  133. package/dist/_unauthenticated.d.ts.map +0 -1
  134. package/src/_unauthenticated.ts +0 -126
@@ -1,12 +1,114 @@
1
+ import type { LogLevel } from '@agentuity/core';
1
2
  import { __originalConsole } from '../otel/logger';
2
3
  import type { Logger } from './logger';
3
4
  import { formatMessage } from './util';
4
5
 
5
- const yellow = '\x1b[33m';
6
- const green = '\x1b[32m';
7
- const red = '\x1b[31m';
8
- const black = '\x1b[1;30m';
9
- const reset = '\x1b[0m';
6
+ const BOLD = '\x1b[1m';
7
+ const RESET = '\x1b[0m';
8
+
9
+ // Helper to convert hex color to ANSI 24-bit color code
10
+ function hexToAnsi(hex: string): string {
11
+ const r = parseInt(hex.slice(1, 3), 16);
12
+ const g = parseInt(hex.slice(3, 5), 16);
13
+ const b = parseInt(hex.slice(5, 7), 16);
14
+ return `\x1b[38;2;${r};${g};${b}m`;
15
+ }
16
+
17
+ interface LogColors {
18
+ level: string;
19
+ message: string;
20
+ }
21
+
22
+ function shouldUseColors(): boolean {
23
+ // Check for NO_COLOR environment variable (any non-empty value disables colors)
24
+ if (process.env.NO_COLOR) {
25
+ return false;
26
+ }
27
+
28
+ // Check for TERM=dumb
29
+ if (process.env.TERM === 'dumb') {
30
+ return false;
31
+ }
32
+
33
+ // Check if stdout is a TTY
34
+ if (!process.stdout || typeof process.stdout.isTTY === 'undefined') {
35
+ return false;
36
+ }
37
+
38
+ if (process.stdout && typeof process.stdout.isTTY !== 'undefined' && !process.stdout.isTTY) {
39
+ return false;
40
+ }
41
+
42
+ return true;
43
+ }
44
+
45
+ type ColorScheme = 'light' | 'dark';
46
+
47
+ function getLogColors(scheme: ColorScheme): Record<LogLevel, LogColors> {
48
+ if (scheme === 'light') {
49
+ // Darker, high-contrast colors for light backgrounds
50
+ return {
51
+ trace: {
52
+ level: hexToAnsi('#008B8B') + BOLD, // Dark cyan
53
+ message: hexToAnsi('#4B4B4B'), // Dark gray
54
+ },
55
+ debug: {
56
+ level: hexToAnsi('#0000CD') + BOLD, // Medium blue
57
+ message: hexToAnsi('#006400'), // Dark green
58
+ },
59
+ info: {
60
+ level: hexToAnsi('#FF8C00') + BOLD, // Dark orange
61
+ message: hexToAnsi('#0066CC') + BOLD, // Strong blue
62
+ },
63
+ warn: {
64
+ level: hexToAnsi('#9400D3') + BOLD, // Dark violet
65
+ message: hexToAnsi('#8B008B'), // Dark magenta
66
+ },
67
+ error: {
68
+ level: hexToAnsi('#DC143C') + BOLD, // Crimson
69
+ message: hexToAnsi('#8B0000') + BOLD, // Dark red
70
+ },
71
+ };
72
+ }
73
+
74
+ // Dark mode colors (brighter for dark backgrounds)
75
+ return {
76
+ trace: {
77
+ level: hexToAnsi('#00FFFF') + BOLD, // Cyan
78
+ message: hexToAnsi('#A0A0A0'), // Light gray
79
+ },
80
+ debug: {
81
+ level: hexToAnsi('#5C9CFF') + BOLD, // Blue
82
+ message: hexToAnsi('#90EE90'), // Light green
83
+ },
84
+ info: {
85
+ level: hexToAnsi('#FFD700') + BOLD, // Gold/Yellow
86
+ message: hexToAnsi('#FFFFFF') + BOLD, // White
87
+ },
88
+ warn: {
89
+ level: hexToAnsi('#FF00FF') + BOLD, // Magenta
90
+ message: hexToAnsi('#FF00FF'), // Magenta
91
+ },
92
+ error: {
93
+ level: hexToAnsi('#FF4444') + BOLD, // Red
94
+ message: hexToAnsi('#FF4444'), // Red
95
+ },
96
+ };
97
+ }
98
+
99
+ // Detect color scheme from environment
100
+ function detectColorScheme(): ColorScheme {
101
+ const scheme = process.env.COLOR_SCHEME?.toLowerCase();
102
+ if (scheme === 'light' || scheme === 'dark') {
103
+ return scheme;
104
+ }
105
+ if (process.env.CI) {
106
+ return 'light';
107
+ }
108
+ return 'dark'; // Default to dark mode
109
+ }
110
+
111
+ const NOCOLORS = Object.freeze({ level: '', reset: '', message: '' });
10
112
 
11
113
  /**
12
114
  * Console implementation of the Logger interface
@@ -14,15 +116,68 @@ const reset = '\x1b[0m';
14
116
  export default class ConsoleLogger implements Logger {
15
117
  private context: Record<string, unknown>;
16
118
  private formatContext: boolean;
119
+ private logLevel: LogLevel;
120
+ private colors: Record<LogLevel, LogColors>;
121
+ private detectedTraceLoopLog: boolean | undefined;
122
+ private useColors: boolean;
17
123
 
18
124
  /**
19
125
  * Creates a new console logger
20
126
  *
21
127
  * @param context - Initial context for the logger
22
128
  */
23
- constructor(context: Record<string, unknown> = {}, formatContext = true) {
129
+ constructor(
130
+ context: Record<string, unknown> = {},
131
+ formatContext = true,
132
+ logLevel: LogLevel = 'info'
133
+ ) {
24
134
  this.context = context;
25
135
  this.formatContext = formatContext;
136
+ this.logLevel = logLevel;
137
+ this.useColors = shouldUseColors();
138
+ this.colors = this.useColors
139
+ ? getLogColors(detectColorScheme())
140
+ : ({} as Record<LogLevel, LogColors>);
141
+ }
142
+
143
+ private shouldLog(level: LogLevel): boolean {
144
+ switch (this.logLevel) {
145
+ case 'trace':
146
+ return true;
147
+ case 'debug':
148
+ return level === 'debug' || level === 'info' || level === 'warn' || level === 'error';
149
+ case 'info':
150
+ return level === 'info' || level === 'warn' || level === 'error';
151
+ case 'warn':
152
+ return level === 'warn' || level === 'error';
153
+ case 'error':
154
+ return level === 'error';
155
+ }
156
+ return false;
157
+ }
158
+
159
+ /**
160
+ * Log a trace message (most verbose)
161
+ *
162
+ * @param message - The message to log
163
+ * @param args - Additional arguments to log
164
+ */
165
+ trace(message: unknown, ...args: unknown[]): void {
166
+ if (!this.shouldLog('trace')) {
167
+ return;
168
+ }
169
+ try {
170
+ const colors = this.useColors ? this.colors.trace : NOCOLORS;
171
+ const formattedMessage = formatMessage(this.formatContext, this.context, message, args);
172
+ __originalConsole.debug(
173
+ `${colors.level}[TRACE]${RESET} ${colors.message}${formattedMessage}${RESET}`
174
+ );
175
+ } catch (err) {
176
+ // Fallback to direct logging if formatting fails
177
+ const colors = this.colors.trace;
178
+ __originalConsole.debug(`${colors.level}[TRACE]${RESET} ${message}`, ...args);
179
+ __originalConsole.error('Error formatting log message:', err);
180
+ }
26
181
  }
27
182
 
28
183
  /**
@@ -32,12 +187,19 @@ export default class ConsoleLogger implements Logger {
32
187
  * @param args - Additional arguments to log
33
188
  */
34
189
  debug(message: unknown, ...args: unknown[]): void {
190
+ if (!this.shouldLog('debug')) {
191
+ return;
192
+ }
35
193
  try {
194
+ const colors = this.useColors ? this.colors.debug : NOCOLORS;
36
195
  const formattedMessage = formatMessage(this.formatContext, this.context, message, args);
37
- __originalConsole.debug(`${black}[DEBUG]${reset} ${formattedMessage}`);
196
+ __originalConsole.debug(
197
+ `${colors.level}[DEBUG]${RESET} ${colors.message}${formattedMessage}${RESET}`
198
+ );
38
199
  } catch (err) {
39
200
  // Fallback to direct logging if formatting fails
40
- __originalConsole.debug(`${black}[DEBUG]${reset} ${message}`, ...args);
201
+ const colors = this.colors.debug;
202
+ __originalConsole.debug(`${colors.level}[DEBUG]${RESET} ${message}`, ...args);
41
203
  __originalConsole.error('Error formatting log message:', err);
42
204
  }
43
205
  }
@@ -49,12 +211,31 @@ export default class ConsoleLogger implements Logger {
49
211
  * @param args - Additional arguments to log
50
212
  */
51
213
  info(message: unknown, ...args: unknown[]): void {
214
+ if (!this.shouldLog('info')) {
215
+ return;
216
+ }
217
+ // suppress the default traceloop message at info level
218
+ if (
219
+ !this.detectedTraceLoopLog &&
220
+ typeof message === 'string' &&
221
+ message.includes('Traceloop exporting traces to')
222
+ ) {
223
+ this.detectedTraceLoopLog = true;
224
+ if (this.shouldLog('debug')) {
225
+ this.debug(message, ...args);
226
+ }
227
+ return;
228
+ }
52
229
  try {
230
+ const colors = this.useColors ? this.colors.info : NOCOLORS;
53
231
  const formattedMessage = formatMessage(this.formatContext, this.context, message, args);
54
- __originalConsole.info(`${green}[INFO]${reset} ${formattedMessage}`);
232
+ __originalConsole.info(
233
+ `${colors.level}[INFO]${RESET} ${colors.message}${formattedMessage}${RESET}`
234
+ );
55
235
  } catch (err) {
56
236
  // Fallback to direct logging if formatting fails
57
- __originalConsole.info(`${green}[INFO]${reset} ${message}`, ...args);
237
+ const colors = this.colors.info;
238
+ __originalConsole.info(`${colors.level}[INFO]${RESET} ${message}`, ...args);
58
239
  __originalConsole.error('Error formatting log message:', err);
59
240
  }
60
241
  }
@@ -66,12 +247,19 @@ export default class ConsoleLogger implements Logger {
66
247
  * @param args - Additional arguments to log
67
248
  */
68
249
  warn(message: unknown, ...args: unknown[]): void {
250
+ if (!this.shouldLog('warn')) {
251
+ return;
252
+ }
69
253
  try {
254
+ const colors = this.useColors ? this.colors.warn : NOCOLORS;
70
255
  const formattedMessage = formatMessage(this.formatContext, this.context, message, args);
71
- __originalConsole.warn(`${yellow}[WARN]${reset} ${formattedMessage}`);
256
+ __originalConsole.warn(
257
+ `${colors.level}[WARN]${RESET} ${colors.message}${formattedMessage}${RESET}`
258
+ );
72
259
  } catch (err) {
73
260
  // Fallback to direct logging if formatting fails
74
- __originalConsole.warn(`${yellow}[WARN]${reset} ${message}`, ...args);
261
+ const colors = this.colors.warn;
262
+ __originalConsole.warn(`${colors.level}[WARN]${RESET} ${message}`, ...args);
75
263
  __originalConsole.error('Error formatting log message:', err);
76
264
  }
77
265
  }
@@ -83,16 +271,34 @@ export default class ConsoleLogger implements Logger {
83
271
  * @param args - Additional arguments to log
84
272
  */
85
273
  error(message: unknown, ...args: unknown[]): void {
274
+ if (!this.shouldLog('error')) {
275
+ return;
276
+ }
86
277
  try {
278
+ const colors = this.useColors ? this.colors.error : NOCOLORS;
87
279
  const formattedMessage = formatMessage(this.formatContext, this.context, message, args);
88
- __originalConsole.error(`${red}[ERROR]${reset} ${formattedMessage}`);
280
+ __originalConsole.error(
281
+ `${colors.level}[ERROR]${RESET} ${colors.message}${formattedMessage}${RESET}`
282
+ );
89
283
  } catch (err) {
90
284
  // Fallback to direct logging if formatting fails
91
- __originalConsole.error(`${red}[ERROR]${reset} ${message}`, ...args);
285
+ const colors = this.colors.error;
286
+ __originalConsole.error(`${colors.level}[ERROR]${RESET} ${message}`, ...args);
92
287
  __originalConsole.error('Error formatting log message:', err);
93
288
  }
94
289
  }
95
290
 
291
+ /**
292
+ * Log a fatal error message and exit the process
293
+ *
294
+ * @param message - The message to log
295
+ * @param args - Additional arguments to log
296
+ */
297
+ fatal(message: unknown, ...args: unknown[]): never {
298
+ this.error(message, ...args);
299
+ process.exit(1);
300
+ }
301
+
96
302
  /**
97
303
  * Create a child logger with additional context
98
304
  *
@@ -105,7 +311,8 @@ export default class ConsoleLogger implements Logger {
105
311
  ...this.context,
106
312
  ...opts,
107
313
  },
108
- this.formatContext
314
+ this.formatContext,
315
+ this.logLevel
109
316
  );
110
317
  }
111
318
  }
@@ -1,3 +1,2 @@
1
1
  export { internal } from './internal';
2
- export * from './logger';
3
2
  export { type Logger, logger } from './user';
@@ -1,11 +1,15 @@
1
- import ConsoleLogger from './console';
2
- import type { Logger } from './logger';
1
+ import { type ColorScheme, createLogger } from '@agentuity/server';
2
+ import type { LogLevel, Logger } from '@agentuity/core';
3
3
 
4
4
  /**
5
5
  * User-facing logger instance
6
6
  * This is the logger that SDK consumers should use
7
7
  */
8
- export const logger: Logger = new ConsoleLogger(undefined, false);
8
+ export const logger: Logger = createLogger(
9
+ (process.env.AGENTUITY_LOG_LEVEL || 'info') as LogLevel,
10
+ false,
11
+ (process.env.COLOR_SCHEME ?? 'dark') as ColorScheme
12
+ );
9
13
 
10
14
  // Re-export the Logger type for convenience
11
- export type { Logger } from './logger';
15
+ export type { Logger } from '@agentuity/core';
@@ -1,13 +1,8 @@
1
- import { createResource, createUserLoggerProvider, registerOtel } from './otel';
2
- import { resourceFromAttributes } from '@opentelemetry/resources';
3
- import { OtelLogger } from '../otel/logger';
4
- import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
5
- import type { LoggerProvider } from '@opentelemetry/sdk-logs';
6
- import type { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
7
- import type { LogRecordProcessor } from '@opentelemetry/sdk-logs';
8
- import type { OtelResponse, OtelConfig } from './otel';
1
+ import type { LogLevel } from '@agentuity/core';
9
2
  import type { SpanProcessor } from '@opentelemetry/sdk-trace-base';
10
3
  import * as runtimeConfig from '../_config';
4
+ import type { OtelConfig, OtelResponse } from './otel';
5
+ import { registerOtel } from './otel';
11
6
 
12
7
  /**
13
8
  * Configuration for user provided OpenTelemetry
@@ -23,6 +18,7 @@ export interface CustomizedOtelConfig {
23
18
 
24
19
  interface OtelRegisterConfig {
25
20
  processors?: SpanProcessor[];
21
+ logLevel?: LogLevel;
26
22
  }
27
23
 
28
24
  export function register(registerConfig: OtelRegisterConfig): OtelResponse {
@@ -38,44 +34,11 @@ export function register(registerConfig: OtelRegisterConfig): OtelResponse {
38
34
  projectId: runtimeConfig.getProjectId(),
39
35
  deploymentId: runtimeConfig.getDeploymentId(),
40
36
  environment: runtimeConfig.getEnvironment(),
37
+ logLevel: registerConfig.logLevel,
38
+ jsonlBasePath: process.env.AGENTUITY_CLOUD_EXPORT_DIR,
41
39
  bearerToken,
42
40
  url,
43
41
  };
44
- let userOtelConf: CustomizedOtelConfig | undefined;
45
- if (process.env.AGENTUITY_USER_OTEL_CONF) {
46
- try {
47
- userOtelConf = JSON.parse(process.env.AGENTUITY_USER_OTEL_CONF);
48
- } catch (error) {
49
- console.warn(
50
- `[WARN] Failed to parse AGENTUITY_USER_OTEL_CONF: ${error instanceof Error ? error.message : String(error)}`
51
- );
52
- }
53
- }
54
42
 
55
- const otel = registerOtel(config);
56
- let userLoggerProvider:
57
- | {
58
- provider: LoggerProvider;
59
- exporter: OTLPLogExporter;
60
- processor: LogRecordProcessor;
61
- }
62
- | undefined;
63
- if (userOtelConf) {
64
- const resource = resourceFromAttributes({
65
- ...createResource(config).attributes,
66
- ...userOtelConf.resourceAttributes,
67
- [ATTR_SERVICE_NAME]: userOtelConf.serviceName,
68
- });
69
- userLoggerProvider = createUserLoggerProvider({
70
- url: userOtelConf.endpoint,
71
- headers: userOtelConf.headers,
72
- resource,
73
- });
74
- if (otel.logger instanceof OtelLogger) {
75
- otel.logger.addDelegate(userLoggerProvider.provider.getLogger('default'));
76
- } else {
77
- console.warn('[WARN] user OTEL logger not attached: logger does not support addDelegate');
78
- }
79
- }
80
- return otel;
43
+ return registerOtel(config);
81
44
  }
@@ -1,19 +1,24 @@
1
1
  import { SeverityNumber } from '@opentelemetry/api-logs';
2
2
  import { type ExportResult, ExportResultCode } from '@opentelemetry/core';
3
3
  import type { LogRecordExporter, ReadableLogRecord } from '@opentelemetry/sdk-logs';
4
- import ConsoleLogger from '../logger/console';
4
+ import { type ColorScheme, createLogger } from '@agentuity/server';
5
+ import type { Logger, LogLevel } from '@agentuity/core';
5
6
 
6
7
  /**
7
8
  * Console implementation of the LogRecordExporter interface
8
9
  */
9
10
  export class ConsoleLogRecordExporter implements LogRecordExporter {
10
- private readonly logger: ConsoleLogger;
11
+ private readonly logger: Logger;
11
12
 
12
13
  /**
13
14
  * Creates a new console log record exporter
14
15
  */
15
- constructor() {
16
- this.logger = new ConsoleLogger();
16
+ constructor(logLevel: LogLevel) {
17
+ this.logger = createLogger(
18
+ logLevel,
19
+ false,
20
+ (process.env.COLOR_SCHEME as ColorScheme) ?? 'dark'
21
+ );
17
22
  }
18
23
 
19
24
  /**
@@ -0,0 +1,217 @@
1
+ # JSONL Exporters
2
+
3
+ Custom OpenTelemetry exporters that write telemetry data (logs, traces, metrics) to JSONL (JSON Lines) files instead of sending directly to an OTLP endpoint.
4
+
5
+ ## Overview
6
+
7
+ These exporters write telemetry data to local files in JSONL format. Each line in the file represents a single telemetry item in JSON format. This allows for:
8
+
9
+ 1. **Decoupled Processing**: Telemetry data is buffered locally and processed separately
10
+ 2. **Reliability**: Data persists even if the OTLP endpoint is temporarily unavailable
11
+ 3. **Batch Processing**: A separate cron job can read and send data in batches
12
+ 4. **Easy Debugging**: JSONL files can be inspected directly
13
+
14
+ ## How It Works
15
+
16
+ ### 1. Writing Telemetry Data
17
+
18
+ The exporters write telemetry data to timestamped JSONL files:
19
+
20
+ - **Logs**: `./otel-data/logs-<timestamp>.jsonl`
21
+ - **Traces**: `./otel-data/traces-<timestamp>.jsonl`
22
+ - **Metrics**: `./otel-data/metrics-<timestamp>.jsonl`
23
+
24
+ Files are named with an ISO timestamp (with colons and periods replaced by hyphens) to ensure uniqueness. The exporters will continue writing to the same file as long as it exists.
25
+
26
+ ### 2. Reading and Forwarding Data (External Process)
27
+
28
+ A separate cron job (recommended: every 30 seconds) should:
29
+
30
+ 1. Read the JSONL files
31
+ 2. Parse each line as a JSON object
32
+ 3. Send the telemetry data to your OTLP endpoint
33
+ 4. Delete the file after successful transmission
34
+
35
+ This decouples the application from the OTLP endpoint and provides resilience.
36
+
37
+ ## Configuration
38
+
39
+ ### Enabling JSONL Exporters
40
+
41
+ By default, JSONL exporters are enabled. You can configure them via the `OtelConfig`:
42
+
43
+ ```typescript
44
+ import { registerOtel } from '@agentuity/runtime/otel';
45
+
46
+ registerOtel({
47
+ name: 'my-app',
48
+ version: '1.0.0',
49
+ url: 'https://otel.example.com',
50
+ useJsonlExporter: true, // Enable JSONL exporters (default: true)
51
+ jsonlBasePath: './.agentuity/otel-data', // Directory for JSONL files
52
+ });
53
+ ```
54
+
55
+ ### Disabling JSONL Exporters
56
+
57
+ To use the original OTLP exporters (direct network calls):
58
+
59
+ ```typescript
60
+ registerOtel({
61
+ name: 'my-app',
62
+ version: '1.0.0',
63
+ url: 'https://otel.example.com',
64
+ useJsonlExporter: false, // Disable JSONL, use OTLP directly
65
+ });
66
+ ```
67
+
68
+ ## File Format
69
+
70
+ ### Logs
71
+
72
+ Each log entry contains:
73
+
74
+ ```json
75
+ {
76
+ "timestamp": [seconds, nanoseconds],
77
+ "observedTimestamp": [seconds, nanoseconds],
78
+ "severityNumber": 9,
79
+ "severityText": "INFO",
80
+ "body": "Log message",
81
+ "attributes": { "key": "value" },
82
+ "resource": { "@agentuity/orgId": "...", ... },
83
+ "instrumentationScope": { "name": "...", "version": "..." },
84
+ "spanContext": { "traceId": "...", "spanId": "...", ... }
85
+ }
86
+ ```
87
+
88
+ ### Traces
89
+
90
+ Each span contains:
91
+
92
+ ```json
93
+ {
94
+ "traceId": "...",
95
+ "spanId": "...",
96
+ "traceState": "...",
97
+ "name": "operation-name",
98
+ "kind": 1,
99
+ "startTime": [seconds, nanoseconds],
100
+ "endTime": [seconds, nanoseconds],
101
+ "attributes": { "key": "value" },
102
+ "status": { "code": 0 },
103
+ "events": [],
104
+ "links": [],
105
+ "resource": { "@agentuity/orgId": "...", ... },
106
+ "droppedAttributesCount": 0,
107
+ "droppedEventsCount": 0,
108
+ "droppedLinksCount": 0,
109
+ "duration": [seconds, nanoseconds],
110
+ "ended": true
111
+ }
112
+ ```
113
+
114
+ ### Metrics
115
+
116
+ Each metric batch contains:
117
+
118
+ ```json
119
+ {
120
+ "resource": { "@agentuity/orgId": "...", ... },
121
+ "scopeMetrics": [
122
+ {
123
+ "scope": { "name": "...", "version": "..." },
124
+ "metrics": [
125
+ {
126
+ "descriptor": { "name": "...", "description": "...", ... },
127
+ "dataPointType": 0,
128
+ "dataPoints": [...],
129
+ "aggregationTemporality": 1
130
+ }
131
+ ]
132
+ }
133
+ ]
134
+ }
135
+ ```
136
+
137
+ ## Example Cron Job
138
+
139
+ Here's an example script that reads JSONL files and forwards them to OTLP:
140
+
141
+ ```typescript
142
+ #!/usr/bin/env bun
143
+
144
+ import { readdir, readFile, unlink } from 'node:fs/promises';
145
+ import { join } from 'node:path';
146
+
147
+ const OTEL_ENDPOINT = process.env.OTEL_ENDPOINT || 'https://otel.agentuity.cloud';
148
+ const OTEL_TOKEN = process.env.OTEL_TOKEN;
149
+ const DATA_DIR = process.env.DATA_DIR || './.agentuity/otel-data';
150
+
151
+ async function processFiles() {
152
+ const files = await readdir(DATA_DIR);
153
+
154
+ for (const file of files) {
155
+ if (file.endsWith('.jsonl')) {
156
+ const filePath = join(DATA_DIR, file);
157
+ const content = await readFile(filePath, 'utf-8');
158
+ const lines = content.trim().split('\n');
159
+
160
+ const type = file.startsWith('logs-')
161
+ ? 'logs'
162
+ : file.startsWith('traces-')
163
+ ? 'traces'
164
+ : 'metrics';
165
+
166
+ try {
167
+ // Send to OTLP endpoint
168
+ await fetch(`${OTEL_ENDPOINT}/v1/${type}`, {
169
+ method: 'POST',
170
+ headers: {
171
+ 'Content-Type': 'application/json',
172
+ Authorization: `Bearer ${OTEL_TOKEN}`,
173
+ },
174
+ body: JSON.stringify({ [type]: lines.map((line) => JSON.parse(line)) }),
175
+ });
176
+
177
+ // Delete file after successful transmission
178
+ await unlink(filePath);
179
+ console.log(`Processed and deleted ${file}`);
180
+ } catch (error) {
181
+ console.error(`Failed to process ${file}:`, error);
182
+ // Don't delete the file on error, will retry next time
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ processFiles().catch(console.error);
189
+ ```
190
+
191
+ Add this to your crontab to run every 30 seconds:
192
+
193
+ ```bash
194
+ * * * * * /path/to/process-otel-data.ts
195
+ * * * * * sleep 30 && /path/to/process-otel-data.ts
196
+ ```
197
+
198
+ ## Exporters
199
+
200
+ ### JSONLLogExporter
201
+
202
+ Implements `LogRecordExporter` interface.
203
+
204
+ ### JSONLTraceExporter
205
+
206
+ Implements `SpanExporter` interface.
207
+
208
+ ### JSONLMetricExporter
209
+
210
+ Implements `PushMetricExporter` interface.
211
+
212
+ ## Notes
213
+
214
+ - Files are written synchronously using `appendFileSync` for simplicity and reliability
215
+ - File existence is checked before each write, creating a new file if necessary
216
+ - Timestamps in filenames use ISO format with special characters replaced by hyphens
217
+ - The exporters handle errors gracefully and report them via the callback mechanism
@@ -0,0 +1,3 @@
1
+ export { JSONLLogExporter } from './jsonl-log-exporter';
2
+ export { JSONLTraceExporter } from './jsonl-trace-exporter';
3
+ export { JSONLMetricExporter } from './jsonl-metric-exporter';