@probelabs/probe 0.6.0-rc100

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 (115) hide show
  1. package/README.md +583 -0
  2. package/bin/.gitkeep +0 -0
  3. package/bin/probe +158 -0
  4. package/bin/probe-binary +0 -0
  5. package/build/agent/ProbeAgent.d.ts +199 -0
  6. package/build/agent/ProbeAgent.js +1486 -0
  7. package/build/agent/acp/README.md +347 -0
  8. package/build/agent/acp/connection.js +237 -0
  9. package/build/agent/acp/connection.test.js +311 -0
  10. package/build/agent/acp/examples/simple-client.js +212 -0
  11. package/build/agent/acp/examples/tool-lifecycle.js +230 -0
  12. package/build/agent/acp/final-test.js +173 -0
  13. package/build/agent/acp/index.js +5 -0
  14. package/build/agent/acp/integration.test.js +385 -0
  15. package/build/agent/acp/manual-test.js +410 -0
  16. package/build/agent/acp/protocol-test.js +190 -0
  17. package/build/agent/acp/server.js +448 -0
  18. package/build/agent/acp/server.test.js +371 -0
  19. package/build/agent/acp/test-runner.js +216 -0
  20. package/build/agent/acp/test-utils/README.md +315 -0
  21. package/build/agent/acp/test-utils/acp-tester.js +484 -0
  22. package/build/agent/acp/test-utils/mock-acp-client.js +434 -0
  23. package/build/agent/acp/tools.js +368 -0
  24. package/build/agent/acp/tools.test.js +334 -0
  25. package/build/agent/acp/types.js +218 -0
  26. package/build/agent/acp/types.test.js +327 -0
  27. package/build/agent/appTracer.js +360 -0
  28. package/build/agent/fileSpanExporter.js +169 -0
  29. package/build/agent/index.js +7426 -0
  30. package/build/agent/mcp/client.js +338 -0
  31. package/build/agent/mcp/config.js +313 -0
  32. package/build/agent/mcp/index.js +64 -0
  33. package/build/agent/mcp/xmlBridge.js +371 -0
  34. package/build/agent/mockProvider.js +53 -0
  35. package/build/agent/probeTool.js +257 -0
  36. package/build/agent/schemaUtils.js +1726 -0
  37. package/build/agent/simpleTelemetry.js +267 -0
  38. package/build/agent/telemetry.js +225 -0
  39. package/build/agent/tokenCounter.js +395 -0
  40. package/build/agent/tools.js +163 -0
  41. package/build/cli.js +49 -0
  42. package/build/delegate.js +267 -0
  43. package/build/directory-resolver.js +237 -0
  44. package/build/downloader.js +750 -0
  45. package/build/extract.js +149 -0
  46. package/build/index.js +70 -0
  47. package/build/mcp/index.js +514 -0
  48. package/build/mcp/index.ts +608 -0
  49. package/build/query.js +116 -0
  50. package/build/search.js +247 -0
  51. package/build/tools/common.js +410 -0
  52. package/build/tools/index.js +40 -0
  53. package/build/tools/langchain.js +88 -0
  54. package/build/tools/system-message.js +121 -0
  55. package/build/tools/vercel.js +271 -0
  56. package/build/utils/file-lister.js +193 -0
  57. package/build/utils.js +128 -0
  58. package/cjs/agent/ProbeAgent.cjs +5829 -0
  59. package/cjs/index.cjs +6217 -0
  60. package/cjs/package.json +3 -0
  61. package/index.d.ts +401 -0
  62. package/package.json +114 -0
  63. package/scripts/postinstall.js +172 -0
  64. package/src/agent/ProbeAgent.d.ts +199 -0
  65. package/src/agent/ProbeAgent.js +1486 -0
  66. package/src/agent/acp/README.md +347 -0
  67. package/src/agent/acp/connection.js +237 -0
  68. package/src/agent/acp/connection.test.js +311 -0
  69. package/src/agent/acp/examples/simple-client.js +212 -0
  70. package/src/agent/acp/examples/tool-lifecycle.js +230 -0
  71. package/src/agent/acp/final-test.js +173 -0
  72. package/src/agent/acp/index.js +5 -0
  73. package/src/agent/acp/integration.test.js +385 -0
  74. package/src/agent/acp/manual-test.js +410 -0
  75. package/src/agent/acp/protocol-test.js +190 -0
  76. package/src/agent/acp/server.js +448 -0
  77. package/src/agent/acp/server.test.js +371 -0
  78. package/src/agent/acp/test-runner.js +216 -0
  79. package/src/agent/acp/test-utils/README.md +315 -0
  80. package/src/agent/acp/test-utils/acp-tester.js +484 -0
  81. package/src/agent/acp/test-utils/mock-acp-client.js +434 -0
  82. package/src/agent/acp/tools.js +368 -0
  83. package/src/agent/acp/tools.test.js +334 -0
  84. package/src/agent/acp/types.js +218 -0
  85. package/src/agent/acp/types.test.js +327 -0
  86. package/src/agent/appTracer.js +360 -0
  87. package/src/agent/fileSpanExporter.js +169 -0
  88. package/src/agent/index.js +813 -0
  89. package/src/agent/mcp/client.js +338 -0
  90. package/src/agent/mcp/config.js +313 -0
  91. package/src/agent/mcp/index.js +64 -0
  92. package/src/agent/mcp/xmlBridge.js +371 -0
  93. package/src/agent/mockProvider.js +53 -0
  94. package/src/agent/probeTool.js +257 -0
  95. package/src/agent/schemaUtils.js +1726 -0
  96. package/src/agent/simpleTelemetry.js +267 -0
  97. package/src/agent/telemetry.js +225 -0
  98. package/src/agent/tokenCounter.js +395 -0
  99. package/src/agent/tools.js +163 -0
  100. package/src/cli.js +49 -0
  101. package/src/delegate.js +267 -0
  102. package/src/directory-resolver.js +237 -0
  103. package/src/downloader.js +750 -0
  104. package/src/extract.js +149 -0
  105. package/src/index.js +70 -0
  106. package/src/mcp/index.ts +608 -0
  107. package/src/query.js +116 -0
  108. package/src/search.js +247 -0
  109. package/src/tools/common.js +410 -0
  110. package/src/tools/index.js +40 -0
  111. package/src/tools/langchain.js +88 -0
  112. package/src/tools/system-message.js +121 -0
  113. package/src/tools/vercel.js +271 -0
  114. package/src/utils/file-lister.js +193 -0
  115. package/src/utils.js +128 -0
@@ -0,0 +1,360 @@
1
+ import { trace, context, SpanStatusCode } from '@opentelemetry/api';
2
+
3
+ /**
4
+ * Application-specific tracing layer for probe-agent
5
+ * Provides higher-level tracing functions for AI operations and tool calls
6
+ */
7
+ export class AppTracer {
8
+ constructor(telemetryConfig, sessionId = null) {
9
+ this.telemetryConfig = telemetryConfig;
10
+ this.tracer = telemetryConfig?.getTracer();
11
+ this.sessionId = sessionId || this.generateSessionId();
12
+ this.traceId = this.generateTraceId();
13
+ }
14
+
15
+ /**
16
+ * Generate a unique session ID
17
+ */
18
+ generateSessionId() {
19
+ return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
20
+ }
21
+
22
+ /**
23
+ * Generate trace ID from session ID for consistent tracing
24
+ */
25
+ generateTraceId() {
26
+ if (!this.sessionId) return null;
27
+
28
+ // Create a deterministic trace ID from session ID
29
+ const hash = this.hashString(this.sessionId);
30
+ return hash.padEnd(32, '0').substring(0, 32);
31
+ }
32
+
33
+ /**
34
+ * Simple hash function for session ID
35
+ */
36
+ hashString(str) {
37
+ let hash = 0;
38
+ for (let i = 0; i < str.length; i++) {
39
+ const char = str.charCodeAt(i);
40
+ hash = ((hash << 5) - hash) + char;
41
+ hash = hash & hash; // Convert to 32bit integer
42
+ }
43
+ return Math.abs(hash).toString(16);
44
+ }
45
+
46
+ /**
47
+ * Check if tracing is enabled
48
+ */
49
+ isEnabled() {
50
+ return this.tracer !== null;
51
+ }
52
+
53
+ /**
54
+ * Create a root span for the agent session
55
+ */
56
+ createSessionSpan(attributes = {}) {
57
+ if (!this.isEnabled()) return null;
58
+
59
+ return this.tracer.startSpan('agent.session', {
60
+ attributes: {
61
+ 'session.id': this.sessionId,
62
+ 'trace.id': this.traceId,
63
+ ...attributes,
64
+ },
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Create a span for AI model requests
70
+ */
71
+ createAISpan(modelName, provider, attributes = {}) {
72
+ if (!this.isEnabled()) return null;
73
+
74
+ return this.tracer.startSpan('ai.request', {
75
+ attributes: {
76
+ 'ai.model': modelName,
77
+ 'ai.provider': provider,
78
+ 'session.id': this.sessionId,
79
+ ...attributes,
80
+ },
81
+ });
82
+ }
83
+
84
+ /**
85
+ * Create a span for tool calls
86
+ */
87
+ createToolSpan(toolName, attributes = {}) {
88
+ if (!this.isEnabled()) return null;
89
+
90
+ return this.tracer.startSpan('tool.call', {
91
+ attributes: {
92
+ 'tool.name': toolName,
93
+ 'session.id': this.sessionId,
94
+ ...attributes,
95
+ },
96
+ });
97
+ }
98
+
99
+ /**
100
+ * Create a span for code search operations
101
+ */
102
+ createSearchSpan(query, attributes = {}) {
103
+ if (!this.isEnabled()) return null;
104
+
105
+ return this.tracer.startSpan('search.query', {
106
+ attributes: {
107
+ 'search.query': query,
108
+ 'session.id': this.sessionId,
109
+ ...attributes,
110
+ },
111
+ });
112
+ }
113
+
114
+ /**
115
+ * Create a span for code extraction operations
116
+ */
117
+ createExtractSpan(files, attributes = {}) {
118
+ if (!this.isEnabled()) return null;
119
+
120
+ return this.tracer.startSpan('extract.files', {
121
+ attributes: {
122
+ 'extract.file_count': Array.isArray(files) ? files.length : 1,
123
+ 'extract.files': Array.isArray(files) ? files.join(',') : files,
124
+ 'session.id': this.sessionId,
125
+ ...attributes,
126
+ },
127
+ });
128
+ }
129
+
130
+ /**
131
+ * Create a span for agent iterations
132
+ */
133
+ createIterationSpan(iteration, attributes = {}) {
134
+ if (!this.isEnabled()) return null;
135
+
136
+ return this.tracer.startSpan('agent.iteration', {
137
+ attributes: {
138
+ 'iteration.number': iteration,
139
+ 'session.id': this.sessionId,
140
+ ...attributes,
141
+ },
142
+ });
143
+ }
144
+
145
+ /**
146
+ * Create a span for delegation operations
147
+ */
148
+ createDelegationSpan(task, attributes = {}) {
149
+ if (!this.isEnabled()) return null;
150
+
151
+ return this.tracer.startSpan('agent.delegation', {
152
+ attributes: {
153
+ 'delegation.task': task.substring(0, 200) + (task.length > 200 ? '...' : ''),
154
+ 'delegation.task_length': task.length,
155
+ 'session.id': this.sessionId,
156
+ ...attributes,
157
+ },
158
+ });
159
+ }
160
+
161
+ /**
162
+ * Create a span for JSON validation operations
163
+ */
164
+ createJsonValidationSpan(responseLength, attributes = {}) {
165
+ if (!this.isEnabled()) return null;
166
+
167
+ return this.tracer.startSpan('validation.json', {
168
+ attributes: {
169
+ 'validation.response_length': responseLength,
170
+ 'session.id': this.sessionId,
171
+ ...attributes,
172
+ },
173
+ });
174
+ }
175
+
176
+ /**
177
+ * Create a span for Mermaid validation operations
178
+ */
179
+ createMermaidValidationSpan(diagramCount, attributes = {}) {
180
+ if (!this.isEnabled()) return null;
181
+
182
+ return this.tracer.startSpan('validation.mermaid', {
183
+ attributes: {
184
+ 'validation.diagram_count': diagramCount,
185
+ 'session.id': this.sessionId,
186
+ ...attributes,
187
+ },
188
+ });
189
+ }
190
+
191
+ /**
192
+ * Create a span for schema processing operations
193
+ */
194
+ createSchemaProcessingSpan(schemaType, attributes = {}) {
195
+ if (!this.isEnabled()) return null;
196
+
197
+ return this.tracer.startSpan('schema.processing', {
198
+ attributes: {
199
+ 'schema.type': schemaType,
200
+ 'session.id': this.sessionId,
201
+ ...attributes,
202
+ },
203
+ });
204
+ }
205
+
206
+ /**
207
+ * Record delegation events
208
+ */
209
+ recordDelegationEvent(eventType, data = {}) {
210
+ if (!this.isEnabled()) return;
211
+
212
+ this.addEvent(`delegation.${eventType}`, {
213
+ 'session.id': this.sessionId,
214
+ ...data
215
+ });
216
+ }
217
+
218
+ /**
219
+ * Record JSON validation events
220
+ */
221
+ recordJsonValidationEvent(eventType, data = {}) {
222
+ if (!this.isEnabled()) return;
223
+
224
+ this.addEvent(`json_validation.${eventType}`, {
225
+ 'session.id': this.sessionId,
226
+ ...data
227
+ });
228
+ }
229
+
230
+ /**
231
+ * Record Mermaid validation events
232
+ */
233
+ recordMermaidValidationEvent(eventType, data = {}) {
234
+ if (!this.isEnabled()) return;
235
+
236
+ this.addEvent(`mermaid_validation.${eventType}`, {
237
+ 'session.id': this.sessionId,
238
+ ...data
239
+ });
240
+ }
241
+
242
+ /**
243
+ * Add an event to the current or most recent span
244
+ */
245
+ addEvent(name, attributes = {}) {
246
+ if (!this.isEnabled()) return;
247
+
248
+ // Try to add to the current span in context
249
+ const activeSpan = trace.getActiveSpan();
250
+ if (activeSpan) {
251
+ activeSpan.addEvent(name, {
252
+ 'session.id': this.sessionId,
253
+ ...attributes,
254
+ });
255
+ } else {
256
+ // Fallback: log as structured data if no active span
257
+ if (this.telemetryConfig?.enableConsole) {
258
+ console.log(`[Event] ${name}:`, attributes);
259
+ }
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Set attributes on the current span
265
+ */
266
+ setAttributes(attributes) {
267
+ if (!this.isEnabled()) return;
268
+
269
+ const activeSpan = trace.getActiveSpan();
270
+ if (activeSpan) {
271
+ activeSpan.setAttributes({
272
+ 'session.id': this.sessionId,
273
+ ...attributes,
274
+ });
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Wrap a function with automatic span creation
280
+ */
281
+ wrapFunction(spanName, fn, attributes = {}) {
282
+ if (!this.isEnabled()) {
283
+ return fn;
284
+ }
285
+
286
+ return async (...args) => {
287
+ const span = this.tracer.startSpan(spanName, {
288
+ attributes: {
289
+ 'session.id': this.sessionId,
290
+ ...attributes,
291
+ },
292
+ });
293
+
294
+ try {
295
+ const result = await context.with(trace.setSpan(context.active(), span), () => fn(...args));
296
+ span.setStatus({ code: SpanStatusCode.OK });
297
+ return result;
298
+ } catch (error) {
299
+ span.setStatus({
300
+ code: SpanStatusCode.ERROR,
301
+ message: error.message,
302
+ });
303
+ span.recordException(error);
304
+ throw error;
305
+ } finally {
306
+ span.end();
307
+ }
308
+ };
309
+ }
310
+
311
+ /**
312
+ * Execute a function within a span context
313
+ */
314
+ async withSpan(spanName, fn, attributes = {}) {
315
+ if (!this.isEnabled()) {
316
+ return fn();
317
+ }
318
+
319
+ const span = this.tracer.startSpan(spanName, {
320
+ attributes: {
321
+ 'session.id': this.sessionId,
322
+ ...attributes,
323
+ },
324
+ });
325
+
326
+ try {
327
+ const result = await context.with(trace.setSpan(context.active(), span), () => fn());
328
+ span.setStatus({ code: SpanStatusCode.OK });
329
+ return result;
330
+ } catch (error) {
331
+ span.setStatus({
332
+ code: SpanStatusCode.ERROR,
333
+ message: error.message,
334
+ });
335
+ span.recordException(error);
336
+ throw error;
337
+ } finally {
338
+ span.end();
339
+ }
340
+ }
341
+
342
+
343
+ /**
344
+ * Force flush all pending spans
345
+ */
346
+ async flush() {
347
+ if (this.telemetryConfig) {
348
+ await this.telemetryConfig.forceFlush();
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Shutdown tracing
354
+ */
355
+ async shutdown() {
356
+ if (this.telemetryConfig) {
357
+ await this.telemetryConfig.shutdown();
358
+ }
359
+ }
360
+ }
@@ -0,0 +1,169 @@
1
+ import { createWriteStream } from 'fs';
2
+ import corePkg from '@opentelemetry/core';
3
+
4
+ const { ExportResultCode } = corePkg;
5
+
6
+ /**
7
+ * File exporter for OpenTelemetry spans
8
+ * Exports spans to a file in JSON Lines format (one JSON object per line)
9
+ * Following the OTLP JSON format specification
10
+ */
11
+ export class FileSpanExporter {
12
+ constructor(filePath = './traces.jsonl') {
13
+ this.filePath = filePath;
14
+ this.stream = createWriteStream(filePath, { flags: 'a' });
15
+ this.stream.on('error', (error) => {
16
+ console.error(`[FileSpanExporter] Stream error: ${error.message}`);
17
+ });
18
+ }
19
+
20
+ /**
21
+ * Export spans to file
22
+ * @param {ReadableSpan[]} spans - Array of spans to export
23
+ * @param {function} resultCallback - Callback to call with the export result
24
+ */
25
+ export(spans, resultCallback) {
26
+ if (!spans || spans.length === 0) {
27
+ resultCallback({ code: ExportResultCode.SUCCESS });
28
+ return;
29
+ }
30
+
31
+ try {
32
+ const timestamp = Date.now();
33
+
34
+ spans.forEach((span, index) => {
35
+ // Extract parent span ID - check various possible properties
36
+ let parentSpanId = undefined;
37
+
38
+ if (span.parentSpanContext) {
39
+ parentSpanId = span.parentSpanContext.spanId;
40
+ } else if (span._parentSpanContext) {
41
+ parentSpanId = span._parentSpanContext.spanId;
42
+ } else if (span.parent) {
43
+ parentSpanId = span.parent.spanId;
44
+ } else if (span._parent) {
45
+ parentSpanId = span._parent.spanId;
46
+ } else if (span._parentId) {
47
+ parentSpanId = span._parentId;
48
+ } else if (span.parentSpanId) {
49
+ parentSpanId = span.parentSpanId;
50
+ }
51
+
52
+ // Convert span to OTLP JSON format
53
+ const spanData = {
54
+ traceId: span.spanContext().traceId,
55
+ spanId: span.spanContext().spanId,
56
+ parentSpanId: parentSpanId,
57
+ name: span.name,
58
+ kind: span.kind,
59
+ startTimeUnixNano: span.startTime[0] * 1_000_000_000 + span.startTime[1],
60
+ endTimeUnixNano: span.endTime[0] * 1_000_000_000 + span.endTime[1],
61
+ attributes: this.convertAttributes(span.attributes),
62
+ status: span.status,
63
+ events: span.events?.map(event => ({
64
+ timeUnixNano: event.time[0] * 1_000_000_000 + event.time[1],
65
+ name: event.name,
66
+ attributes: this.convertAttributes(event.attributes),
67
+ })) || [],
68
+ links: span.links?.map(link => ({
69
+ traceId: link.context.traceId,
70
+ spanId: link.context.spanId,
71
+ attributes: this.convertAttributes(link.attributes),
72
+ })) || [],
73
+ resource: {
74
+ attributes: this.convertAttributes(span.resource?.attributes || {}),
75
+ },
76
+ instrumentationLibrary: {
77
+ name: span.instrumentationLibrary?.name || 'unknown',
78
+ version: span.instrumentationLibrary?.version || 'unknown',
79
+ },
80
+ timestamp,
81
+ };
82
+
83
+ // Write as JSON Lines format (one JSON object per line)
84
+ this.stream.write(JSON.stringify(spanData) + '\n');
85
+ });
86
+
87
+ resultCallback({ code: ExportResultCode.SUCCESS });
88
+ } catch (error) {
89
+ console.error(`[FileSpanExporter] Export error: ${error.message}`);
90
+ resultCallback({
91
+ code: ExportResultCode.FAILED,
92
+ error: error
93
+ });
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Convert OpenTelemetry attributes to plain object
99
+ * @param {Object} attributes - OpenTelemetry attributes
100
+ * @returns {Object} Plain object with string values
101
+ */
102
+ convertAttributes(attributes) {
103
+ if (!attributes) return {};
104
+
105
+ const result = {};
106
+ for (const [key, value] of Object.entries(attributes)) {
107
+ // Convert all values to strings for JSON compatibility
108
+ if (typeof value === 'object' && value !== null) {
109
+ result[key] = JSON.stringify(value);
110
+ } else {
111
+ result[key] = String(value);
112
+ }
113
+ }
114
+ return result;
115
+ }
116
+
117
+ /**
118
+ * Shutdown the exporter
119
+ * @returns {Promise<void>}
120
+ */
121
+ async shutdown() {
122
+ return new Promise((resolve) => {
123
+ if (this.stream) {
124
+ this.stream.end(() => {
125
+ console.log(`[FileSpanExporter] File stream closed: ${this.filePath}`);
126
+ resolve();
127
+ });
128
+ } else {
129
+ resolve();
130
+ }
131
+ });
132
+ }
133
+
134
+ /**
135
+ * Force flush any pending spans
136
+ * @returns {Promise<void>}
137
+ */
138
+ async forceFlush() {
139
+ return new Promise((resolve, reject) => {
140
+ if (this.stream) {
141
+ const flushTimeout = setTimeout(() => {
142
+ console.warn('[FileSpanExporter] Flush timeout after 5 seconds');
143
+ resolve();
144
+ }, 5000);
145
+
146
+ // Uncork the stream to force buffered writes
147
+ if (this.stream.writableCorked) {
148
+ this.stream.uncork();
149
+ }
150
+
151
+ // If there's buffered data, wait for drain event
152
+ if (this.stream.writableNeedDrain) {
153
+ this.stream.once('drain', () => {
154
+ clearTimeout(flushTimeout);
155
+ resolve();
156
+ });
157
+ } else {
158
+ // No buffered data, but still give it a moment to ensure writes complete
159
+ setImmediate(() => {
160
+ clearTimeout(flushTimeout);
161
+ resolve();
162
+ });
163
+ }
164
+ } else {
165
+ resolve();
166
+ }
167
+ });
168
+ }
169
+ }