@volcengine/tls-observer-pi 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENCE +13 -0
- package/README.md +58 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2261 -0
- package/dist/tracer.d.ts +35 -0
- package/dist/tracer.d.ts.map +1 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +49 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +35 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2261 @@
|
|
|
1
|
+
import { homedir, hostname, networkInterfaces, userInfo } from "node:os";
|
|
2
|
+
import { createHash as external_node_crypto_createHash } from "node:crypto";
|
|
3
|
+
import { tlsOpenapi } from "@volcengine/openapi";
|
|
4
|
+
import "node:url";
|
|
5
|
+
import * as __rspack_external_node_fs_5ea92f0c from "node:fs";
|
|
6
|
+
import * as __rspack_external_node_path_c5b9b54f from "node:path";
|
|
7
|
+
import * as __rspack_external_node_fs_promises_153e37e0 from "node:fs/promises";
|
|
8
|
+
var package_namespaceObject = {
|
|
9
|
+
rE: "0.0.1"
|
|
10
|
+
};
|
|
11
|
+
function firstEnv(...names) {
|
|
12
|
+
for (const name of names){
|
|
13
|
+
const value = process.env[name];
|
|
14
|
+
if ('string' == typeof value && '' !== value.trim()) return value.trim();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function envDisabled(name) {
|
|
18
|
+
return /^(0|false|no|n|off)$/i.test(process.env[name] ?? '');
|
|
19
|
+
}
|
|
20
|
+
function envPositiveInteger(name, fallback) {
|
|
21
|
+
const value = Number.parseInt(process.env[name] ?? '', 10);
|
|
22
|
+
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
23
|
+
}
|
|
24
|
+
function parseEnvValue(raw) {
|
|
25
|
+
const value = raw.trim();
|
|
26
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1).replace(/\\n/g, '\n').replace(/\\"/g, '"').replace(/\\'/g, "'");
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
function defaultEnvFile() {
|
|
30
|
+
return __rspack_external_node_path_c5b9b54f.join(homedir(), '.pi', 'agent', 'tls-observer-pi.env');
|
|
31
|
+
}
|
|
32
|
+
function defaultDataRoot() {
|
|
33
|
+
return __rspack_external_node_path_c5b9b54f.join(homedir(), '.pi', 'agent', 'tls-observer-pi');
|
|
34
|
+
}
|
|
35
|
+
function loadEnvFile(filePath) {
|
|
36
|
+
const target = filePath || firstEnv('PI_TLS_ENV_FILE') || defaultEnvFile();
|
|
37
|
+
let text;
|
|
38
|
+
try {
|
|
39
|
+
text = __rspack_external_node_fs_5ea92f0c.readFileSync(target, 'utf8');
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
for (const rawLine of text.split(/\r?\n/)){
|
|
44
|
+
const line = rawLine.trim();
|
|
45
|
+
if (!line || line.startsWith('#')) continue;
|
|
46
|
+
const normalized = line.startsWith('export ') ? line.slice(7).trim() : line;
|
|
47
|
+
const match = normalized.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
48
|
+
if (!match) continue;
|
|
49
|
+
const [, key, rawValue] = match;
|
|
50
|
+
process.env[key] ??= parseEnvValue(rawValue);
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
function mirroredTlsEnv() {
|
|
55
|
+
return {
|
|
56
|
+
...process.env,
|
|
57
|
+
TRACE_TLS_ENDPOINT: firstEnv('PI_TLS_ENDPOINT', 'TRACE_TLS_ENDPOINT', 'TLS_ENDPOINT'),
|
|
58
|
+
TRACE_TLS_REGION: firstEnv('PI_TLS_REGION', 'TRACE_TLS_REGION', 'TLS_REGION'),
|
|
59
|
+
TRACE_TLS_TRACE_TOPIC_ID: firstEnv('PI_TLS_TRACE_TOPIC_ID', 'PI_TLS_TOPIC_ID', 'TRACE_TLS_TRACE_TOPIC_ID', 'TLS_TRACE_TOPIC_ID'),
|
|
60
|
+
TRACE_TLS_API_KEY: firstEnv('PI_TLS_API_KEY', 'TRACE_TLS_API_KEY', 'TLS_API_KEY'),
|
|
61
|
+
TRACE_TLS_AK: firstEnv('PI_TLS_AK', 'TRACE_TLS_AK', 'TLS_AK'),
|
|
62
|
+
TRACE_TLS_SK: firstEnv('PI_TLS_SK', 'TRACE_TLS_SK', 'TLS_SK'),
|
|
63
|
+
TRACE_TLS_EXPORT_TIMEOUT_MS: firstEnv('PI_TLS_EXPORT_TIMEOUT_MS', 'TRACE_TLS_EXPORT_TIMEOUT_MS'),
|
|
64
|
+
TLS_ENDPOINT: firstEnv('PI_TLS_ENDPOINT', 'TLS_ENDPOINT'),
|
|
65
|
+
TLS_REGION: firstEnv('PI_TLS_REGION', 'TLS_REGION'),
|
|
66
|
+
TLS_TRACE_TOPIC_ID: firstEnv('PI_TLS_TRACE_TOPIC_ID', 'PI_TLS_TOPIC_ID', 'TLS_TRACE_TOPIC_ID'),
|
|
67
|
+
TLS_API_KEY: firstEnv('PI_TLS_API_KEY', 'TLS_API_KEY'),
|
|
68
|
+
TLS_AK: firstEnv('PI_TLS_AK', 'TLS_AK'),
|
|
69
|
+
TLS_SK: firstEnv('PI_TLS_SK', 'TLS_SK'),
|
|
70
|
+
TLS_AUTH_MODE: firstEnv('PI_TLS_AUTH_MODE', 'TLS_AUTH_MODE'),
|
|
71
|
+
PI_TLS_CAPTURE_CONTENT: firstEnv('PI_TLS_CAPTURE_CONTENT')
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function validateTlsConfig(env) {
|
|
75
|
+
const missing = [];
|
|
76
|
+
if (!env.TRACE_TLS_ENDPOINT) missing.push('PI_TLS_ENDPOINT, TRACE_TLS_ENDPOINT, or TLS_ENDPOINT');
|
|
77
|
+
if (!env.TRACE_TLS_REGION) missing.push('PI_TLS_REGION, TRACE_TLS_REGION, or TLS_REGION');
|
|
78
|
+
if (!env.TRACE_TLS_TRACE_TOPIC_ID) missing.push('PI_TLS_TRACE_TOPIC_ID, TRACE_TLS_TRACE_TOPIC_ID, or TLS_TRACE_TOPIC_ID');
|
|
79
|
+
const apiKey = env.TRACE_TLS_API_KEY;
|
|
80
|
+
const ak = env.TRACE_TLS_AK;
|
|
81
|
+
const sk = env.TRACE_TLS_SK;
|
|
82
|
+
if (!apiKey && (!ak || !sk)) missing.push('PI_TLS_API_KEY or PI_TLS_AK/PI_TLS_SK');
|
|
83
|
+
if (missing.length > 0) throw new Error(`Missing required Pi TLS config: ${missing.join(', ')}`);
|
|
84
|
+
if (apiKey && (ak || sk)) throw new Error('Pi TLS config must use API Key or AK/SK, not both');
|
|
85
|
+
}
|
|
86
|
+
function loadConfig() {
|
|
87
|
+
loadEnvFile();
|
|
88
|
+
const serviceName = firstEnv('PI_OTEL_SERVICE_NAME') || 'pi-plugin';
|
|
89
|
+
const enabled = !envDisabled('PI_TLS_EXPORT');
|
|
90
|
+
const env = mirroredTlsEnv();
|
|
91
|
+
if (enabled) validateTlsConfig(env);
|
|
92
|
+
return {
|
|
93
|
+
enabled,
|
|
94
|
+
dataRoot: firstEnv('PI_TLS_DATA_ROOT') || defaultDataRoot(),
|
|
95
|
+
captureContent: !envDisabled('PI_TLS_CAPTURE_CONTENT'),
|
|
96
|
+
serviceName,
|
|
97
|
+
serviceInstanceId: `${serviceName}@${hostname()}:${process.pid}`,
|
|
98
|
+
pluginVersion: package_namespaceObject.rE,
|
|
99
|
+
exportTimeoutMs: envPositiveInteger('PI_TLS_EXPORT_TIMEOUT_MS', 20000),
|
|
100
|
+
env
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function stableHexId(seed, length) {
|
|
104
|
+
const normalized = String(seed ?? '');
|
|
105
|
+
const digest = external_node_crypto_createHash('sha256').update(normalized).digest('hex').slice(0, length);
|
|
106
|
+
return digest === '0'.repeat(length) ? '1'.padStart(length, '0') : digest;
|
|
107
|
+
}
|
|
108
|
+
function model_traceIdFromSeed(seed) {
|
|
109
|
+
return stableHexId(seed, 32);
|
|
110
|
+
}
|
|
111
|
+
function spanIdFromSeed(...parts) {
|
|
112
|
+
return stableHexId(parts.map((part)=>String(part ?? '')).join(':'), 16);
|
|
113
|
+
}
|
|
114
|
+
function model_toEpochMs(value, fallback = Date.now()) {
|
|
115
|
+
if (value instanceof Date) {
|
|
116
|
+
const time = value.getTime();
|
|
117
|
+
return Number.isFinite(time) ? time : fallback;
|
|
118
|
+
}
|
|
119
|
+
if ('number' == typeof value && Number.isFinite(value)) return value;
|
|
120
|
+
if ('string' == typeof value && '' !== value.trim()) {
|
|
121
|
+
const parsed = Date.parse(value);
|
|
122
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
123
|
+
}
|
|
124
|
+
return fallback;
|
|
125
|
+
}
|
|
126
|
+
function isRecord(value) {
|
|
127
|
+
return null != value && 'object' == typeof value && !Array.isArray(value);
|
|
128
|
+
}
|
|
129
|
+
function validateSpan(span, path) {
|
|
130
|
+
const errors = [];
|
|
131
|
+
if (!isRecord(span)) return [
|
|
132
|
+
`${path} must be an object`
|
|
133
|
+
];
|
|
134
|
+
if ('string' != typeof span.name || '' === span.name.trim()) errors.push(`${path}.name is required`);
|
|
135
|
+
if (void 0 === span.startedAt && void 0 === span.start_time_unix_ms) errors.push(`${path}.startedAt is required`);
|
|
136
|
+
if (void 0 === span.endedAt && void 0 === span.end_time_unix_ms) errors.push(`${path}.endedAt is required`);
|
|
137
|
+
if (void 0 !== span.attributes && !isRecord(span.attributes)) errors.push(`${path}.attributes must be an object`);
|
|
138
|
+
return errors;
|
|
139
|
+
}
|
|
140
|
+
function validateCanonicalTrace(trace) {
|
|
141
|
+
const errors = [];
|
|
142
|
+
const warnings = [];
|
|
143
|
+
if (!isRecord(trace)) return {
|
|
144
|
+
valid: false,
|
|
145
|
+
errors: [
|
|
146
|
+
'trace must be an object'
|
|
147
|
+
],
|
|
148
|
+
warnings
|
|
149
|
+
};
|
|
150
|
+
if (isRecord(trace.source)) {
|
|
151
|
+
const source = trace.source;
|
|
152
|
+
if ('string' != typeof source.tool || '' === source.tool.trim()) errors.push('source.tool is required');
|
|
153
|
+
if ('string' != typeof source.sessionId || '' === source.sessionId.trim()) errors.push('source.sessionId is required');
|
|
154
|
+
} else errors.push('source is required');
|
|
155
|
+
errors.push(...validateSpan(trace.root, 'root'));
|
|
156
|
+
if (Array.isArray(trace.spans)) for (const [index, span] of trace.spans.entries())errors.push(...validateSpan(span, `spans[${index}]`));
|
|
157
|
+
else errors.push('spans must be an array');
|
|
158
|
+
if (void 0 !== trace.messages && !Array.isArray(trace.messages)) errors.push('messages must be an array');
|
|
159
|
+
if (void 0 !== trace.diagnostics) if (isRecord(trace.diagnostics)) {
|
|
160
|
+
if (void 0 !== trace.diagnostics.warnings && !Array.isArray(trace.diagnostics.warnings)) warnings.push('diagnostics.warnings should be an array');
|
|
161
|
+
} else warnings.push('diagnostics should be an object');
|
|
162
|
+
return {
|
|
163
|
+
valid: 0 === errors.length,
|
|
164
|
+
errors,
|
|
165
|
+
warnings
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function assertCanonicalTrace(trace) {
|
|
169
|
+
const result = validateCanonicalTrace(trace);
|
|
170
|
+
if (!result.valid) {
|
|
171
|
+
const error = new Error(`Invalid canonical trace: ${result.errors.join('; ')}`);
|
|
172
|
+
error.name = 'CanonicalTraceValidationError';
|
|
173
|
+
error.validation = result;
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
return trace;
|
|
177
|
+
}
|
|
178
|
+
const DEFAULT_MAX_ATTR_LENGTH = 65536;
|
|
179
|
+
const MAX_ATTR_LENGTH_ENV_KEYS = [
|
|
180
|
+
'TRACE_OTLP_ATTRIBUTE_VALUE_MAX_CHARS',
|
|
181
|
+
'CODEX_OTLP_ATTRIBUTE_VALUE_MAX_CHARS'
|
|
182
|
+
];
|
|
183
|
+
function resolveMaxAttrLength(env = process.env) {
|
|
184
|
+
for (const key of MAX_ATTR_LENGTH_ENV_KEYS){
|
|
185
|
+
const raw = env[key];
|
|
186
|
+
if (null == raw || '' === raw.trim()) continue;
|
|
187
|
+
const parsed = Number(raw);
|
|
188
|
+
if (Number.isFinite(parsed) && parsed >= 0) return Math.trunc(parsed);
|
|
189
|
+
}
|
|
190
|
+
return DEFAULT_MAX_ATTR_LENGTH;
|
|
191
|
+
}
|
|
192
|
+
function truncateTextForAttr(value) {
|
|
193
|
+
return String(value ?? '').slice(0, resolveMaxAttrLength());
|
|
194
|
+
}
|
|
195
|
+
function safeStringify(value) {
|
|
196
|
+
const seen = new WeakSet();
|
|
197
|
+
return JSON.stringify(value, (_key, item)=>{
|
|
198
|
+
if (item && 'object' == typeof item) {
|
|
199
|
+
if (seen.has(item)) return '[Circular]';
|
|
200
|
+
seen.add(item);
|
|
201
|
+
}
|
|
202
|
+
return item;
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
function maskToken(value) {
|
|
206
|
+
if (value.length <= 8) return '***';
|
|
207
|
+
return `${value.slice(0, 3)}***${value.slice(-2)}`;
|
|
208
|
+
}
|
|
209
|
+
function redactSensitiveText(text) {
|
|
210
|
+
return String(text ?? '').replace(/\bAK[0-9A-Za-z]{16,}\b/g, maskToken).replace(/\b(Bearer\s+)([A-Za-z0-9._-]{8,})/gi, (_match, prefix, token)=>`${prefix}${maskToken(token)}`).replace(/\b(token|secret|password|passwd|api[_-]?key|access[_-]?key|sk)\s*[:=]\s*([^,\s"']{8,})/gi, (_match, key, secret)=>`${key}=***${String(secret).slice(-2)}`);
|
|
211
|
+
}
|
|
212
|
+
function attributes_serializeForAttr(value) {
|
|
213
|
+
const serialized = 'string' == typeof value ? value : safeStringify(value);
|
|
214
|
+
return redactSensitiveText(truncateTextForAttr(serialized ?? String(value)));
|
|
215
|
+
}
|
|
216
|
+
function spanKindName(span) {
|
|
217
|
+
const kind = String(span.span_kind ?? span.kind ?? '').toUpperCase();
|
|
218
|
+
if ('SERVER' === kind) return 'server';
|
|
219
|
+
if ('CLIENT' === kind) return 'client';
|
|
220
|
+
return 'internal';
|
|
221
|
+
}
|
|
222
|
+
function unixMsToUs(value) {
|
|
223
|
+
return String(1000 * Math.trunc(model_toEpochMs(value)));
|
|
224
|
+
}
|
|
225
|
+
function span_utils_hasError(span) {
|
|
226
|
+
const statusObjectCode = null != span.status && 'object' == typeof span.status ? span.status.code : void 0;
|
|
227
|
+
const statusCode = statusObjectCode ?? span.status_code ?? span.statusCode ?? span.status;
|
|
228
|
+
if (2 === statusCode || 'ERROR' === String(statusCode).toUpperCase()) return true;
|
|
229
|
+
return Object.entries(span.attributes ?? {}).some(([key, value])=>{
|
|
230
|
+
if (true === value && ('error' === key || key.endsWith('.error'))) return true;
|
|
231
|
+
if ('tool.error' === key && 'string' == typeof value && '' !== value.trim()) return true;
|
|
232
|
+
return false;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
function spanDurationMs(span) {
|
|
236
|
+
if ('number' == typeof span.duration_ms && Number.isFinite(span.duration_ms)) return Math.max(0, Math.trunc(span.duration_ms));
|
|
237
|
+
const start = model_toEpochMs(span.start_time_unix_ms ?? span.startedAt);
|
|
238
|
+
const end = model_toEpochMs(span.end_time_unix_ms ?? span.endedAt, start);
|
|
239
|
+
return Math.max(0, Math.trunc(end - start));
|
|
240
|
+
}
|
|
241
|
+
const SCHEMA_FIELD_ORDER = [
|
|
242
|
+
'span.name',
|
|
243
|
+
'span.kind',
|
|
244
|
+
'tls.app.type',
|
|
245
|
+
'session.id',
|
|
246
|
+
'span.status.code',
|
|
247
|
+
'start_time_unix_us',
|
|
248
|
+
'end_time_unix_us',
|
|
249
|
+
'span.duration_us',
|
|
250
|
+
'service.name',
|
|
251
|
+
'service.instance.id',
|
|
252
|
+
'host.name',
|
|
253
|
+
'client.address',
|
|
254
|
+
'observed_time_unix_ms',
|
|
255
|
+
'telemetry.sdk.name',
|
|
256
|
+
'telemetry.sdk.language',
|
|
257
|
+
'telemetry.sdk.version',
|
|
258
|
+
'otel.scope.name',
|
|
259
|
+
'otel.scope.version',
|
|
260
|
+
'event.name',
|
|
261
|
+
'user.role',
|
|
262
|
+
'gen_ai.operation.name',
|
|
263
|
+
'gen_ai.provider.name',
|
|
264
|
+
'gen_ai.request.model',
|
|
265
|
+
'gen_ai.response.model',
|
|
266
|
+
'gen_ai.request.duration_ms',
|
|
267
|
+
'gen_ai.input.messages',
|
|
268
|
+
'gen_ai.output.messages',
|
|
269
|
+
'gen_ai.usage.input_tokens',
|
|
270
|
+
'gen_ai.usage.output_tokens',
|
|
271
|
+
'gen_ai.usage.reasoning.output_tokens',
|
|
272
|
+
'gen_ai.usage.cache_read.input_tokens',
|
|
273
|
+
'gen_ai.usage.cache_creation.input_tokens',
|
|
274
|
+
'gen_ai.usage.total_tokens',
|
|
275
|
+
'gen_ai.tool.name',
|
|
276
|
+
'gen_ai.tool.call.id',
|
|
277
|
+
'gen_ai.tool.call.arguments',
|
|
278
|
+
'gen_ai.tool.call.result',
|
|
279
|
+
'tool.error',
|
|
280
|
+
'error.type',
|
|
281
|
+
'error.message'
|
|
282
|
+
];
|
|
283
|
+
function normalizeOptions(options) {
|
|
284
|
+
if ('number' == typeof options) return {
|
|
285
|
+
observedTimeUnixMs: options,
|
|
286
|
+
version: '-'
|
|
287
|
+
};
|
|
288
|
+
return {
|
|
289
|
+
observedTimeUnixMs: options.observedTimeUnixMs ?? Date.now(),
|
|
290
|
+
version: options.version?.trim() || '-'
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function hasMessages(value) {
|
|
294
|
+
return null != value && 'object' == typeof value && !Array.isArray(value);
|
|
295
|
+
}
|
|
296
|
+
function stableFieldValue(value) {
|
|
297
|
+
if (null == value) return '';
|
|
298
|
+
if ('string' == typeof value) return attributes_serializeForAttr(value);
|
|
299
|
+
if ('number' == typeof value || 'boolean' == typeof value) return value;
|
|
300
|
+
return attributes_serializeForAttr(value);
|
|
301
|
+
}
|
|
302
|
+
function inferUserRole(span) {
|
|
303
|
+
const inputMessage = hasMessages(span.input) ? span.input.messages?.find((message)=>'string' == typeof message?.role) : void 0;
|
|
304
|
+
const role = inputMessage?.role;
|
|
305
|
+
if ('string' == typeof role && [
|
|
306
|
+
'assistant',
|
|
307
|
+
'system',
|
|
308
|
+
'tool',
|
|
309
|
+
'user'
|
|
310
|
+
].includes(role)) return role;
|
|
311
|
+
return '';
|
|
312
|
+
}
|
|
313
|
+
function inferErrorMessage(span) {
|
|
314
|
+
const output = span.output;
|
|
315
|
+
if (null == output) return '';
|
|
316
|
+
if ('string' == typeof output) return output;
|
|
317
|
+
if ('object' == typeof output && 'error' in output && 'string' == typeof output.error) return output.error;
|
|
318
|
+
if ('object' == typeof output && 'error' in output && null != output.error) return attributes_serializeForAttr(output.error);
|
|
319
|
+
return '';
|
|
320
|
+
}
|
|
321
|
+
function schemaSourceAttribute(attributes, dotted, legacy) {
|
|
322
|
+
return attributes[dotted] ?? attributes[legacy] ?? '';
|
|
323
|
+
}
|
|
324
|
+
function normalizedSpanName(span) {
|
|
325
|
+
if ('model' === span.type) return 'llm.request';
|
|
326
|
+
if ('tool' === span.type) return 'tool.call';
|
|
327
|
+
if ('message' === span.type) return 'agent.turn';
|
|
328
|
+
return span.name ?? '';
|
|
329
|
+
}
|
|
330
|
+
function legacyToolErrorMessage(value) {
|
|
331
|
+
return 'string' == typeof value ? value : '';
|
|
332
|
+
}
|
|
333
|
+
function buildSchemaAlignedRecord(trace, span, options = {}) {
|
|
334
|
+
const { observedTimeUnixMs, version } = normalizeOptions(options);
|
|
335
|
+
const attributes = span.attributes ?? {};
|
|
336
|
+
const resource = trace.resource ?? {};
|
|
337
|
+
const isError = span_utils_hasError(span);
|
|
338
|
+
const isTool = 'tool' === span.type;
|
|
339
|
+
const isModel = 'model' === span.type;
|
|
340
|
+
const isMessage = 'message' === span.type;
|
|
341
|
+
const inputMessages = hasMessages(span.input) ? span.input.messages ?? attributes['gen_ai.input.messages'] : attributes['gen_ai.input.messages'];
|
|
342
|
+
const outputMessages = hasMessages(span.output) ? span.output.messages ?? attributes['gen_ai.output.messages'] : attributes['gen_ai.output.messages'];
|
|
343
|
+
const explicitErrorMessage = attributes['error.message'];
|
|
344
|
+
const spanName = normalizedSpanName(span);
|
|
345
|
+
const toolErrorMessage = isTool ? 'string' == typeof explicitErrorMessage && explicitErrorMessage || legacyToolErrorMessage(attributes['tool.error']) || inferErrorMessage(span) : '';
|
|
346
|
+
const record = {
|
|
347
|
+
'span.name': spanName,
|
|
348
|
+
'span.kind': spanKindName(span),
|
|
349
|
+
'tls.app.type': attributes['tls.app.type'] ?? 'codex',
|
|
350
|
+
'session.id': schemaSourceAttribute(attributes, 'session.id', 'codex.session_id'),
|
|
351
|
+
'span.status.code': isError ? 'ERROR' : 'UNSET',
|
|
352
|
+
start_time_unix_us: unixMsToUs(span.start_time_unix_ms ?? span.startedAt),
|
|
353
|
+
end_time_unix_us: unixMsToUs(span.end_time_unix_ms ?? span.endedAt),
|
|
354
|
+
'span.duration_us': String(1000 * spanDurationMs(span)),
|
|
355
|
+
'service.name': resource['service.name'] ?? "codex-transcript-stop-hook",
|
|
356
|
+
'service.instance.id': resource['service.instance.id'] ?? '',
|
|
357
|
+
'host.name': resource['host.name'] ?? '',
|
|
358
|
+
'client.address': '',
|
|
359
|
+
observed_time_unix_ms: observedTimeUnixMs,
|
|
360
|
+
'telemetry.sdk.name': resource['telemetry.sdk.name'] ?? "codex-transcript-stop-hook",
|
|
361
|
+
'telemetry.sdk.language': resource['telemetry.sdk.language'] ?? 'nodejs',
|
|
362
|
+
'telemetry.sdk.version': resource['telemetry.sdk.version'] ?? version,
|
|
363
|
+
'otel.scope.name': trace.scope?.name ?? "codex-transcript-stop-hook",
|
|
364
|
+
'otel.scope.version': trace.scope?.version ?? version,
|
|
365
|
+
'event.name': spanName,
|
|
366
|
+
'user.role': inferUserRole(span),
|
|
367
|
+
'gen_ai.operation.name': attributes['gen_ai.operation.name'] ?? (isModel ? 'chat' : isMessage ? 'invoke_agent' : ''),
|
|
368
|
+
'gen_ai.provider.name': attributes['gen_ai.provider.name'] ?? '',
|
|
369
|
+
'gen_ai.request.model': attributes['gen_ai.request.model'] ?? '',
|
|
370
|
+
'gen_ai.response.model': attributes['gen_ai.response.model'] ?? attributes['gen_ai.request.model'] ?? '',
|
|
371
|
+
'gen_ai.request.duration_ms': isModel ? spanDurationMs(span) : attributes['request.duration_ms'] ?? '',
|
|
372
|
+
'gen_ai.input.messages': inputMessages ?? '',
|
|
373
|
+
'gen_ai.output.messages': outputMessages ?? '',
|
|
374
|
+
'gen_ai.usage.input_tokens': attributes['gen_ai.usage.input_tokens'] ?? '',
|
|
375
|
+
'gen_ai.usage.output_tokens': attributes['gen_ai.usage.output_tokens'] ?? '',
|
|
376
|
+
'gen_ai.usage.reasoning.output_tokens': attributes['gen_ai.usage.reasoning.output_tokens'] ?? '',
|
|
377
|
+
'gen_ai.usage.cache_read.input_tokens': attributes['gen_ai.usage.cache_read.input_tokens'] ?? '',
|
|
378
|
+
'gen_ai.usage.cache_creation.input_tokens': attributes['gen_ai.usage.cache_creation.input_tokens'] ?? '',
|
|
379
|
+
'gen_ai.usage.total_tokens': attributes['gen_ai.usage.total_tokens'] ?? '',
|
|
380
|
+
'gen_ai.tool.name': isTool ? attributes['gen_ai.tool.name'] ?? attributes['tool.name'] ?? '' : '',
|
|
381
|
+
'gen_ai.tool.call.id': isTool ? attributes['gen_ai.tool.call.id'] ?? attributes['codex.tool_call_id'] ?? '' : '',
|
|
382
|
+
'gen_ai.tool.call.arguments': isTool ? span.input ?? '' : '',
|
|
383
|
+
'gen_ai.tool.call.result': isTool ? span.output ?? '' : '',
|
|
384
|
+
'tool.error': isTool ? isError : '',
|
|
385
|
+
'error.type': isError ? attributes['codex.tool_status'] ?? 'error' : '',
|
|
386
|
+
'error.message': isError ? toolErrorMessage : ''
|
|
387
|
+
};
|
|
388
|
+
const aligned = {};
|
|
389
|
+
for (const key of SCHEMA_FIELD_ORDER)aligned[key] = stableFieldValue(record[key]);
|
|
390
|
+
return aligned;
|
|
391
|
+
}
|
|
392
|
+
function buildSchemaAlignedRecords(trace, options = {}) {
|
|
393
|
+
if (!trace || !Array.isArray(trace.spans)) return [];
|
|
394
|
+
return trace.spans.map((span)=>buildSchemaAlignedRecord(trace, span, options));
|
|
395
|
+
}
|
|
396
|
+
new Set([
|
|
397
|
+
'trace.id',
|
|
398
|
+
'trace.state',
|
|
399
|
+
'span.id',
|
|
400
|
+
'parent.span.id',
|
|
401
|
+
'span.kind',
|
|
402
|
+
'span.name',
|
|
403
|
+
'span.status.code',
|
|
404
|
+
'span.status.message',
|
|
405
|
+
'span.events',
|
|
406
|
+
'span.links',
|
|
407
|
+
'event.type',
|
|
408
|
+
'event.name',
|
|
409
|
+
'service.name',
|
|
410
|
+
'service.instance.id',
|
|
411
|
+
'host.name',
|
|
412
|
+
'client.address',
|
|
413
|
+
'observed_time_unix_ms',
|
|
414
|
+
'telemetry.sdk.name',
|
|
415
|
+
'telemetry.sdk.language',
|
|
416
|
+
'telemetry.sdk.version',
|
|
417
|
+
'otel.scope.name',
|
|
418
|
+
'otel.scope.version',
|
|
419
|
+
'start_time_unix_us',
|
|
420
|
+
'end_time_unix_us',
|
|
421
|
+
'span.duration_us'
|
|
422
|
+
]);
|
|
423
|
+
new Set([
|
|
424
|
+
'session.id',
|
|
425
|
+
'app.thread.id',
|
|
426
|
+
'gen_ai.conversation.id',
|
|
427
|
+
'gen_ai.agent.id',
|
|
428
|
+
'app.run.id',
|
|
429
|
+
'app.turn.id',
|
|
430
|
+
'user.id',
|
|
431
|
+
'codex.input',
|
|
432
|
+
'codex.output',
|
|
433
|
+
'span_type',
|
|
434
|
+
'codex_span_type',
|
|
435
|
+
'codex_session_id',
|
|
436
|
+
'codex_thread_id',
|
|
437
|
+
'codex_conversation_id',
|
|
438
|
+
'codex_parent_conversation_id',
|
|
439
|
+
'codex_agent_id',
|
|
440
|
+
'codex_turn_id',
|
|
441
|
+
'codex_turn_index',
|
|
442
|
+
'codex_originator',
|
|
443
|
+
'codex_cli_version',
|
|
444
|
+
'codex_cwd',
|
|
445
|
+
"codex_transcript_path",
|
|
446
|
+
'codex_permission_mode',
|
|
447
|
+
'codex_agent_scope',
|
|
448
|
+
'codex_runtime_tags',
|
|
449
|
+
'codex_decision_tool_name',
|
|
450
|
+
'codex_decision_tool_call_id',
|
|
451
|
+
'codex_tool_call_id',
|
|
452
|
+
'codex_tool_status',
|
|
453
|
+
'codex_tool_exit_code',
|
|
454
|
+
'codex.system_tag_runtime',
|
|
455
|
+
'gen_ai.usage.input',
|
|
456
|
+
'gen_ai.usage.output',
|
|
457
|
+
'gen_ai.usage.totalTokens',
|
|
458
|
+
'gen_ai.usage.cacheRead',
|
|
459
|
+
'gen_ai.usage.reasoningOutput'
|
|
460
|
+
]);
|
|
461
|
+
function sourceAttributes(source) {
|
|
462
|
+
return {
|
|
463
|
+
'source.tool': source.tool,
|
|
464
|
+
'session.id': source.sessionId,
|
|
465
|
+
'app.turn.id': source.turnId,
|
|
466
|
+
'app.thread.id': source.threadId,
|
|
467
|
+
'gen_ai.conversation.id': source.conversationId,
|
|
468
|
+
'process.command.cwd': source.cwd,
|
|
469
|
+
'gen_ai.request.model': source.model,
|
|
470
|
+
...source.attributes ?? {}
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function resourceAttributes(trace, options) {
|
|
474
|
+
const source = trace.source ?? {};
|
|
475
|
+
return {
|
|
476
|
+
'service.name': options.serviceName ?? `${source.tool ?? 'agent'}-trace-hook`,
|
|
477
|
+
'service.instance.id': source.sessionId ?? '',
|
|
478
|
+
'host.name': hostname(),
|
|
479
|
+
'telemetry.sdk.name': 'agent-trace-reporter',
|
|
480
|
+
'telemetry.sdk.language': 'nodejs',
|
|
481
|
+
'telemetry.sdk.version': options.sdkVersion ?? '0.1.0',
|
|
482
|
+
...options.resourceAttributes ?? {}
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
function canonicalSpanToLegacy(span, traceId, parentId, commonAttributes, index) {
|
|
486
|
+
const spanId = span.id ?? span.span_id ?? spanIdFromSeed(traceId, span.name, index);
|
|
487
|
+
const startedAt = span.start_time_unix_ms ?? span.startedAt;
|
|
488
|
+
const endedAt = span.end_time_unix_ms ?? span.endedAt;
|
|
489
|
+
return {
|
|
490
|
+
trace_id: traceId,
|
|
491
|
+
span_id: spanId,
|
|
492
|
+
parent_span_id: span.parentId ?? span.parent_span_id ?? parentId,
|
|
493
|
+
name: span.name,
|
|
494
|
+
type: span.type ?? 'internal',
|
|
495
|
+
span_kind: String(span.kind ?? 'internal').toUpperCase(),
|
|
496
|
+
start_time_unix_ms: model_toEpochMs(startedAt),
|
|
497
|
+
end_time_unix_ms: model_toEpochMs(endedAt, model_toEpochMs(startedAt)),
|
|
498
|
+
duration_ms: Math.max(0, model_toEpochMs(endedAt, model_toEpochMs(startedAt)) - model_toEpochMs(startedAt)),
|
|
499
|
+
status_code: 'error' === span.status ? 'ERROR' : 'UNSET',
|
|
500
|
+
input: span.input,
|
|
501
|
+
output: span.output,
|
|
502
|
+
attributes: {
|
|
503
|
+
...commonAttributes,
|
|
504
|
+
...span.attributes ?? {}
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function canonicalTraceToOtelTrace(trace, options = {}) {
|
|
509
|
+
const seed = [
|
|
510
|
+
trace.source?.tool,
|
|
511
|
+
trace.source?.sessionId,
|
|
512
|
+
trace.source?.turnId,
|
|
513
|
+
trace.root?.name,
|
|
514
|
+
trace.root?.startedAt
|
|
515
|
+
].join(':');
|
|
516
|
+
const traceId = trace.traceId ?? model_traceIdFromSeed(seed);
|
|
517
|
+
const commonAttributes = sourceAttributes(trace.source ?? {});
|
|
518
|
+
const root = canonicalSpanToLegacy(trace.root ?? {
|
|
519
|
+
name: 'root'
|
|
520
|
+
}, traceId, '', commonAttributes, 'root');
|
|
521
|
+
root.span_id = trace.root?.id ?? trace.spanId ?? spanIdFromSeed(traceId, 'root');
|
|
522
|
+
root.parent_span_id = '';
|
|
523
|
+
return {
|
|
524
|
+
traceId,
|
|
525
|
+
resource: resourceAttributes(trace, options),
|
|
526
|
+
scope: {
|
|
527
|
+
name: options.scopeName ?? 'agent-trace-reporter',
|
|
528
|
+
version: options.scopeVersion ?? '0.1.0'
|
|
529
|
+
},
|
|
530
|
+
spans: [
|
|
531
|
+
root,
|
|
532
|
+
...(trace.spans ?? []).map((span, index)=>canonicalSpanToLegacy(span, traceId, root.span_id ?? '', commonAttributes, index))
|
|
533
|
+
]
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
const TLS_ANONYMOUS_IDENTITY_HEADER = 'x-tls-anonymous-identity';
|
|
537
|
+
const DEFAULT_FILE_NAME = 'agent-trace';
|
|
538
|
+
const NO_RETRY_STATUS_CODES = [
|
|
539
|
+
400,
|
|
540
|
+
401,
|
|
541
|
+
403,
|
|
542
|
+
404
|
|
543
|
+
];
|
|
544
|
+
const producerEntries = new Map();
|
|
545
|
+
function firstExternalIpv4() {
|
|
546
|
+
for (const items of Object.values(networkInterfaces()))for (const item of items ?? [])if ('IPv4' === item.family && !item.internal && item.address) return item.address;
|
|
547
|
+
return '127.0.0.1';
|
|
548
|
+
}
|
|
549
|
+
function normalizeProducerEndpoint(endpoint) {
|
|
550
|
+
const raw = endpoint.trim();
|
|
551
|
+
if (!raw) return raw;
|
|
552
|
+
try {
|
|
553
|
+
const url = new URL(/^https?:\/\//.test(raw) ? raw : `https://${raw}`);
|
|
554
|
+
return url.host || raw;
|
|
555
|
+
} catch {
|
|
556
|
+
return raw.replace(/^https?:\/\//, '').replace(/\/.*$/, '');
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function producerKey(config) {
|
|
560
|
+
return JSON.stringify({
|
|
561
|
+
endpoint: normalizeProducerEndpoint(config.endpoint),
|
|
562
|
+
region: config.region,
|
|
563
|
+
apiKey: config.apiKey || '',
|
|
564
|
+
ak: config.ak || '',
|
|
565
|
+
sk: config.sk || ''
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
function createProducer(config) {
|
|
569
|
+
const producerConfig = tlsOpenapi.Producer.getDefaultProducerConfig();
|
|
570
|
+
producerConfig.Endpoint = normalizeProducerEndpoint(config.endpoint);
|
|
571
|
+
producerConfig.Region = config.region;
|
|
572
|
+
producerConfig.NoRetryStatusCodeList = NO_RETRY_STATUS_CODES;
|
|
573
|
+
if (config.apiKey) {
|
|
574
|
+
producerConfig.AccessKeyID = '';
|
|
575
|
+
producerConfig.AccessKeySecret = '';
|
|
576
|
+
producerConfig.Headers = {
|
|
577
|
+
[TLS_ANONYMOUS_IDENTITY_HEADER]: config.apiKey
|
|
578
|
+
};
|
|
579
|
+
} else {
|
|
580
|
+
producerConfig.AccessKeyID = config.ak || '';
|
|
581
|
+
producerConfig.AccessKeySecret = config.sk || '';
|
|
582
|
+
}
|
|
583
|
+
const producer = new tlsOpenapi.Producer(producerConfig);
|
|
584
|
+
producer.Start();
|
|
585
|
+
return {
|
|
586
|
+
producer,
|
|
587
|
+
source: firstExternalIpv4(),
|
|
588
|
+
timeoutMs: config.timeoutMs
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
function timeoutError(label, timeoutMs) {
|
|
592
|
+
return new Error(`${label} timed out after ${timeoutMs}ms`);
|
|
593
|
+
}
|
|
594
|
+
function withTimeout(promise, timeoutMs, label) {
|
|
595
|
+
const effectiveTimeoutMs = Number.isFinite(timeoutMs) && timeoutMs > 0 ? Math.trunc(timeoutMs) : 20000;
|
|
596
|
+
return new Promise((resolve, reject)=>{
|
|
597
|
+
const timer = setTimeout(()=>reject(timeoutError(label, effectiveTimeoutMs)), effectiveTimeoutMs);
|
|
598
|
+
promise.then((value)=>{
|
|
599
|
+
clearTimeout(timer);
|
|
600
|
+
resolve(value);
|
|
601
|
+
}, (error)=>{
|
|
602
|
+
clearTimeout(timer);
|
|
603
|
+
reject(error);
|
|
604
|
+
});
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
function getProducer(config) {
|
|
608
|
+
const key = producerKey(config);
|
|
609
|
+
const existing = producerEntries.get(key);
|
|
610
|
+
if (existing) return existing;
|
|
611
|
+
const created = createProducer(config);
|
|
612
|
+
producerEntries.set(key, created);
|
|
613
|
+
return created;
|
|
614
|
+
}
|
|
615
|
+
function recordContents(record) {
|
|
616
|
+
return Object.entries(record).map(([Key, Value])=>({
|
|
617
|
+
Key,
|
|
618
|
+
Value
|
|
619
|
+
}));
|
|
620
|
+
}
|
|
621
|
+
async function sendTlsLogRecords(config, records, fileName = DEFAULT_FILE_NAME) {
|
|
622
|
+
if (0 === records.length) return;
|
|
623
|
+
const testMode = process.env.TRACE_TLS_TEST_PRODUCER_MODE;
|
|
624
|
+
const captureFile = process.env.TRACE_TLS_TEST_PRODUCER_CAPTURE_FILE;
|
|
625
|
+
if (testMode || captureFile) {
|
|
626
|
+
if (captureFile) {
|
|
627
|
+
await __rspack_external_node_fs_promises_153e37e0.mkdir(__rspack_external_node_path_c5b9b54f.dirname(captureFile), {
|
|
628
|
+
recursive: true
|
|
629
|
+
});
|
|
630
|
+
await __rspack_external_node_fs_promises_153e37e0.appendFile(captureFile, `${JSON.stringify({
|
|
631
|
+
topic_id: config.traceTopicId,
|
|
632
|
+
endpoint: normalizeProducerEndpoint(config.endpoint),
|
|
633
|
+
region: config.region,
|
|
634
|
+
file_name: fileName,
|
|
635
|
+
records
|
|
636
|
+
})}\n`, 'utf8');
|
|
637
|
+
}
|
|
638
|
+
if ('fail' === testMode) throw new Error('TRACE_TLS_TEST_PRODUCER_MODE=fail');
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
const entry = getProducer(config);
|
|
642
|
+
const logGroup = {
|
|
643
|
+
Logs: records.map((record)=>({
|
|
644
|
+
Time: Date.now(),
|
|
645
|
+
Contents: recordContents(record)
|
|
646
|
+
})),
|
|
647
|
+
Source: entry.source,
|
|
648
|
+
LogTags: [],
|
|
649
|
+
FileName: fileName
|
|
650
|
+
};
|
|
651
|
+
await withTimeout(new Promise((resolve, reject)=>{
|
|
652
|
+
let settled = false;
|
|
653
|
+
const settle = (fn, value)=>{
|
|
654
|
+
if (settled) return;
|
|
655
|
+
settled = true;
|
|
656
|
+
fn(value);
|
|
657
|
+
};
|
|
658
|
+
const resolveUnknown = resolve;
|
|
659
|
+
const sendPromise = entry.producer.SendLogs('', config.traceTopicId, entry.source, fileName, logGroup, {
|
|
660
|
+
Success: ()=>settle(resolveUnknown),
|
|
661
|
+
Fail: (result)=>{
|
|
662
|
+
const attempts = result?.Attempts ?? [];
|
|
663
|
+
const lastAttempt = attempts[attempts.length - 1] ?? {};
|
|
664
|
+
settle(reject, new Error(`TLS SendLogs failed topicId=${config.traceTopicId} requestId=${String(lastAttempt.RequestId ?? '')} errorCode=${String(lastAttempt.ErrorCode ?? '')} errorMessage=${String(lastAttempt.ErrorMessage ?? '')}`));
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
Promise.resolve(sendPromise).catch((error)=>settle(reject, error));
|
|
668
|
+
}), config.timeoutMs, 'TLS SendLogs');
|
|
669
|
+
}
|
|
670
|
+
function config_firstEnv(env, ...names) {
|
|
671
|
+
for (const name of names){
|
|
672
|
+
const value = env[name];
|
|
673
|
+
if ('string' == typeof value && '' !== value.trim()) return value.trim();
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
function tlsEndpointFromLegacyOtelEndpoint(value) {
|
|
677
|
+
if (!value) return;
|
|
678
|
+
try {
|
|
679
|
+
const url = new URL(/^https?:\/\//i.test(value) ? value : `https://${value}`);
|
|
680
|
+
return url.hostname || void 0;
|
|
681
|
+
} catch {
|
|
682
|
+
return value.trim().replace(/^https?:\/\//i, '').replace(/\/.*$/, '').replace(/:\d+$/, '') || void 0;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
function config_resolveTlsConfig(env) {
|
|
686
|
+
const disabled = /^(0|false|no)$/i.test(config_firstEnv(env, 'TRACE_TLS_EXPORT', 'CODEX_TLS_EXPORT') ?? '');
|
|
687
|
+
if (disabled) return {
|
|
688
|
+
enabled: false,
|
|
689
|
+
reason: 'disabled'
|
|
690
|
+
};
|
|
691
|
+
const endpoint = config_firstEnv(env, 'TRACE_TLS_ENDPOINT', 'CODEX_TLS_ENDPOINT') || tlsEndpointFromLegacyOtelEndpoint(config_firstEnv(env, 'TRACE_TLS_OTEL_ENDPOINT', 'CODEX_TLS_OTEL_ENDPOINT', 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT'));
|
|
692
|
+
const region = config_firstEnv(env, 'TRACE_TLS_REGION', 'CODEX_TLS_REGION', 'TLS_REGION');
|
|
693
|
+
const traceTopicId = config_firstEnv(env, 'TRACE_TLS_TRACE_TOPIC_ID', 'TRACE_TLS_TOPIC_ID', 'CODEX_TLS_TRACE_TOPIC_ID', 'CODEX_TLS_TOPIC_ID', 'CODEX_TLS_TRACE_TOPICID', 'TLS_TRACE_TOPIC_ID');
|
|
694
|
+
const apiKey = config_firstEnv(env, 'TRACE_TLS_API_KEY', 'CODEX_TLS_API_KEY', 'TLS_API_KEY');
|
|
695
|
+
const ak = config_firstEnv(env, 'TRACE_TLS_AK', 'CODEX_TLS_AK', 'TLS_AK');
|
|
696
|
+
const sk = config_firstEnv(env, 'TRACE_TLS_SK', 'CODEX_TLS_SK', 'TLS_SK');
|
|
697
|
+
const timeoutMs = Number(config_firstEnv(env, 'TRACE_TLS_EXPORT_TIMEOUT_MS', 'CODEX_TLS_EXPORT_TIMEOUT_MS') ?? 20000);
|
|
698
|
+
const missing = [];
|
|
699
|
+
if (!endpoint) missing.push('TRACE_TLS_ENDPOINT or CODEX_TLS_ENDPOINT');
|
|
700
|
+
if (!region) missing.push('TRACE_TLS_REGION or CODEX_TLS_REGION');
|
|
701
|
+
if (!traceTopicId) missing.push('TRACE_TLS_TRACE_TOPIC_ID or CODEX_TLS_TRACE_TOPIC_ID');
|
|
702
|
+
if (!apiKey && (!ak || !sk)) missing.push('TRACE_TLS_API_KEY or TRACE_TLS_AK/TRACE_TLS_SK');
|
|
703
|
+
if (missing.length > 0 || !endpoint || !region || !traceTopicId) return {
|
|
704
|
+
enabled: false,
|
|
705
|
+
reason: 'missing_config',
|
|
706
|
+
missing
|
|
707
|
+
};
|
|
708
|
+
if (apiKey && (ak || sk)) return {
|
|
709
|
+
enabled: false,
|
|
710
|
+
reason: 'invalid_auth',
|
|
711
|
+
missing: [
|
|
712
|
+
'use apiKey or ak/sk, not both'
|
|
713
|
+
]
|
|
714
|
+
};
|
|
715
|
+
return {
|
|
716
|
+
enabled: true,
|
|
717
|
+
endpoint,
|
|
718
|
+
region,
|
|
719
|
+
traceTopicId,
|
|
720
|
+
apiKey,
|
|
721
|
+
ak,
|
|
722
|
+
sk,
|
|
723
|
+
timeoutMs: Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 20000
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
const TLS_AUTH_RESOURCE_KEYS = {
|
|
727
|
+
traceTopicId: 'tls.otel.tracetopic',
|
|
728
|
+
apiKey: 'tls.otel.apikey',
|
|
729
|
+
ak: 'tls.otel.ak',
|
|
730
|
+
sk: 'tls.otel.sk',
|
|
731
|
+
region: 'tls.otel.region'
|
|
732
|
+
};
|
|
733
|
+
const TLS_AUTH_RESOURCE_CREDENTIAL_KEYS = new Set([
|
|
734
|
+
TLS_AUTH_RESOURCE_KEYS.apiKey,
|
|
735
|
+
TLS_AUTH_RESOURCE_KEYS.ak,
|
|
736
|
+
TLS_AUTH_RESOURCE_KEYS.sk
|
|
737
|
+
]);
|
|
738
|
+
function otlpAnyValueHasContent(value) {
|
|
739
|
+
if (!value || 'object' != typeof value) return false;
|
|
740
|
+
return Object.values(value).some((item)=>null != item && '' !== String(item).trim());
|
|
741
|
+
}
|
|
742
|
+
function otlpPayloadResourceAttributes(payload) {
|
|
743
|
+
return (payload.resourceSpans ?? []).flatMap((resourceSpan)=>{
|
|
744
|
+
const attributes = resourceSpan?.resource?.attributes;
|
|
745
|
+
return Array.isArray(attributes) ? attributes : [];
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
function otlpPayloadContainsTlsCredentials(body) {
|
|
749
|
+
let payload;
|
|
750
|
+
try {
|
|
751
|
+
payload = JSON.parse(body);
|
|
752
|
+
} catch {
|
|
753
|
+
return [
|
|
754
|
+
...TLS_AUTH_RESOURCE_CREDENTIAL_KEYS
|
|
755
|
+
].some((key)=>body.includes(key));
|
|
756
|
+
}
|
|
757
|
+
if ([
|
|
758
|
+
...TLS_AUTH_RESOURCE_CREDENTIAL_KEYS
|
|
759
|
+
].some((key)=>body.includes(key))) return true;
|
|
760
|
+
return otlpPayloadResourceAttributes(payload).some((attribute)=>TLS_AUTH_RESOURCE_CREDENTIAL_KEYS.has(attribute?.key) && otlpAnyValueHasContent(attribute?.value));
|
|
761
|
+
}
|
|
762
|
+
const ATTRIBUTE_PREFIXES = [
|
|
763
|
+
'gen_ai.',
|
|
764
|
+
'error.'
|
|
765
|
+
];
|
|
766
|
+
const ATTRIBUTE_KEYS = new Set([
|
|
767
|
+
'tool.error'
|
|
768
|
+
]);
|
|
769
|
+
const NUMERIC_ATTRIBUTE_KEYS = new Set([
|
|
770
|
+
'gen_ai.request.duration_ms',
|
|
771
|
+
'gen_ai.usage.input_tokens',
|
|
772
|
+
'gen_ai.usage.output_tokens',
|
|
773
|
+
'gen_ai.usage.reasoning.output_tokens',
|
|
774
|
+
'gen_ai.usage.cache_read.input_tokens',
|
|
775
|
+
'gen_ai.usage.cache_creation.input_tokens',
|
|
776
|
+
'gen_ai.usage.total_tokens'
|
|
777
|
+
]);
|
|
778
|
+
const TLS_AUTH_RESOURCE_KEY_SET = new Set(Object.values(TLS_AUTH_RESOURCE_KEYS));
|
|
779
|
+
function stringifyLogValue(value) {
|
|
780
|
+
if (null == value) return '';
|
|
781
|
+
if ('string' == typeof value) return value;
|
|
782
|
+
if ('number' == typeof value || 'boolean' == typeof value) return String(value);
|
|
783
|
+
return attributes_serializeForAttr(value);
|
|
784
|
+
}
|
|
785
|
+
function stringifyRecord(record) {
|
|
786
|
+
return Object.fromEntries(Object.entries(record).map(([key, value])=>[
|
|
787
|
+
key,
|
|
788
|
+
stringifyLogValue(value)
|
|
789
|
+
]));
|
|
790
|
+
}
|
|
791
|
+
function traceIdOf(trace) {
|
|
792
|
+
return trace?.trace_id ?? trace?.traceId ?? '';
|
|
793
|
+
}
|
|
794
|
+
function spanIdOf(span) {
|
|
795
|
+
return span?.span_id ?? span?.id ?? '';
|
|
796
|
+
}
|
|
797
|
+
function parentSpanIdOf(span) {
|
|
798
|
+
return span?.parent_span_id ?? span?.parentId ?? '';
|
|
799
|
+
}
|
|
800
|
+
function records_traceSpansWithRoot(trace) {
|
|
801
|
+
const spans = trace?.spans ?? [];
|
|
802
|
+
const root = trace?.root;
|
|
803
|
+
if (!root) return spans;
|
|
804
|
+
const rootSpanId = spanIdOf(root);
|
|
805
|
+
if (rootSpanId && spans.some((span)=>spanIdOf(span) === rootSpanId)) return spans;
|
|
806
|
+
return [
|
|
807
|
+
root,
|
|
808
|
+
...spans
|
|
809
|
+
];
|
|
810
|
+
}
|
|
811
|
+
function safeJsonStringify(value) {
|
|
812
|
+
try {
|
|
813
|
+
return JSON.stringify(value ?? {});
|
|
814
|
+
} catch {
|
|
815
|
+
return '{}';
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
function normalizeSpanName(span, fallback = '') {
|
|
819
|
+
if (span?.type === 'model') return 'llm.request';
|
|
820
|
+
if (span?.type === 'tool') return 'tool.call';
|
|
821
|
+
if (span?.type === 'message') return 'agent.turn';
|
|
822
|
+
if ('codex_turn' === fallback) return 'agent.turn';
|
|
823
|
+
return fallback || span?.name || '';
|
|
824
|
+
}
|
|
825
|
+
function envelopeKind(span, name) {
|
|
826
|
+
if ('agent.turn' === name) return 'server';
|
|
827
|
+
if ('llm.request' === name || 'tool.call' === name) return 'client';
|
|
828
|
+
return span ? spanKindName(span) : 'internal';
|
|
829
|
+
}
|
|
830
|
+
function isEmptyAttributeValue(value) {
|
|
831
|
+
return null == value || '' === value;
|
|
832
|
+
}
|
|
833
|
+
function normalizeAttributeValue(key, value) {
|
|
834
|
+
if (isEmptyAttributeValue(value)) return;
|
|
835
|
+
if (NUMERIC_ATTRIBUTE_KEYS.has(key)) {
|
|
836
|
+
const numeric = Number(value);
|
|
837
|
+
if (Number.isFinite(numeric)) return numeric;
|
|
838
|
+
}
|
|
839
|
+
if ('string' == typeof value || 'number' == typeof value || 'boolean' == typeof value) return value;
|
|
840
|
+
return attributes_serializeForAttr(value);
|
|
841
|
+
}
|
|
842
|
+
function envelopeAttributes(record, span) {
|
|
843
|
+
const attributes = {};
|
|
844
|
+
const spanAttributes = span?.attributes ?? {};
|
|
845
|
+
const conversationId = record['gen_ai.conversation.id'] ?? spanAttributes['gen_ai.conversation.id'] ?? record['session.id'];
|
|
846
|
+
for (const [key, value] of Object.entries(record))if ('tls.app.type' === key || 'session.id' === key || 'gen_ai.conversation.id' === key || ATTRIBUTE_KEYS.has(key) || ATTRIBUTE_PREFIXES.some((prefix)=>key.startsWith(prefix))) {
|
|
847
|
+
const normalized = normalizeAttributeValue(key, value);
|
|
848
|
+
if (void 0 !== normalized) attributes[key] = normalized;
|
|
849
|
+
}
|
|
850
|
+
attributes['tls.app.type'] ??= 'codex';
|
|
851
|
+
if (!isEmptyAttributeValue(record['session.id'])) attributes['session.id'] ??= record['session.id'];
|
|
852
|
+
if (!isEmptyAttributeValue(conversationId)) attributes['gen_ai.conversation.id'] ??= conversationId;
|
|
853
|
+
if (span?.type === 'message') attributes['gen_ai.operation.name'] = 'invoke_agent';
|
|
854
|
+
if (span?.type === 'model') attributes['gen_ai.operation.name'] = 'chat';
|
|
855
|
+
return attributes;
|
|
856
|
+
}
|
|
857
|
+
function envelopeResource(trace, record) {
|
|
858
|
+
const resource = trace.resource ?? {};
|
|
859
|
+
return Object.fromEntries(Object.entries({
|
|
860
|
+
...resource,
|
|
861
|
+
'service.instance.id': record['service.instance.id'] ?? resource['service.instance.id'],
|
|
862
|
+
'telemetry.sdk.language': record['telemetry.sdk.language'] ?? resource['telemetry.sdk.language'] ?? 'nodejs',
|
|
863
|
+
'telemetry.sdk.name': record['telemetry.sdk.name'] ?? resource['telemetry.sdk.name'] ?? "codex-transcript-stop-hook",
|
|
864
|
+
'telemetry.sdk.version': record['telemetry.sdk.version'] ?? resource['telemetry.sdk.version'] ?? '0.1.0'
|
|
865
|
+
}).filter(([key, value])=>!TLS_AUTH_RESOURCE_KEY_SET.has(key) && !isEmptyAttributeValue(value)));
|
|
866
|
+
}
|
|
867
|
+
function otlpEnvelopeRecord(trace, span, record) {
|
|
868
|
+
const name = normalizeSpanName(span, String(record['span.name'] ?? ''));
|
|
869
|
+
const start = String(record['start_time_unix_us'] ?? unixMsToUs(span?.start_time_unix_ms ?? span?.startedAt));
|
|
870
|
+
const end = String(record['end_time_unix_us'] ?? unixMsToUs(span?.end_time_unix_ms ?? span?.endedAt));
|
|
871
|
+
const duration = String(record['span.duration_us'] ?? 1000 * spanDurationMs(span ?? {}));
|
|
872
|
+
const error = span ? span_utils_hasError(span) : 'ERROR' === record['span.status.code'];
|
|
873
|
+
const resource = envelopeResource(trace, record);
|
|
874
|
+
const serviceName = stringifyLogValue(record['service.name'] ?? resource['service.name'] ?? "codex-transcript-stop-hook");
|
|
875
|
+
const host = stringifyLogValue(record['host.name'] ?? resource['host.name'] ?? '');
|
|
876
|
+
const scopeName = stringifyLogValue(record['otel.scope.name'] ?? trace.scope?.name ?? "codex-transcript-stop-hook");
|
|
877
|
+
const scopeVersion = stringifyLogValue(record['otel.scope.version'] ?? trace.scope?.version ?? '0.1.0');
|
|
878
|
+
return stringifyRecord({
|
|
879
|
+
Attributes: safeJsonStringify(envelopeAttributes(record, span)),
|
|
880
|
+
Duration: duration,
|
|
881
|
+
End: end,
|
|
882
|
+
Events: '[]',
|
|
883
|
+
Host: host,
|
|
884
|
+
Kind: envelopeKind(span, name),
|
|
885
|
+
Links: '[]',
|
|
886
|
+
Name: name,
|
|
887
|
+
OTLPName: scopeName,
|
|
888
|
+
OTLPVersion: scopeVersion,
|
|
889
|
+
ParentSpanID: parentSpanIdOf(span) || record['parent.span.id'] || '',
|
|
890
|
+
Resource: safeJsonStringify(resource),
|
|
891
|
+
ServiceName: serviceName,
|
|
892
|
+
SpanID: spanIdOf(span) || String(record['span.id'] ?? ''),
|
|
893
|
+
Start: start,
|
|
894
|
+
StatusCode: error ? 'ERROR' : 'OK',
|
|
895
|
+
StatusDescription: error ? record['error.message'] ?? '' : '',
|
|
896
|
+
TraceID: span?.trace_id ?? traceIdOf(trace) ?? record['trace.id'] ?? '',
|
|
897
|
+
TraceState: ''
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
function recordsFromTrace(trace) {
|
|
901
|
+
const spans = records_traceSpansWithRoot(trace);
|
|
902
|
+
const schemaRecords = buildSchemaAlignedRecords({
|
|
903
|
+
...trace,
|
|
904
|
+
spans
|
|
905
|
+
});
|
|
906
|
+
return schemaRecords.map((record, index)=>otlpEnvelopeRecord(trace, spans[index], record));
|
|
907
|
+
}
|
|
908
|
+
function exporter_traceIdOf(trace) {
|
|
909
|
+
return trace?.trace_id ?? trace?.traceId ?? '';
|
|
910
|
+
}
|
|
911
|
+
function notifyRequestBody(options, request) {
|
|
912
|
+
if ('function' != typeof options.onRequestBody) return;
|
|
913
|
+
try {
|
|
914
|
+
options.onRequestBody(request);
|
|
915
|
+
} catch {}
|
|
916
|
+
}
|
|
917
|
+
function endpointDiagnostics(config) {
|
|
918
|
+
return {
|
|
919
|
+
endpoint_host: normalizeProducerEndpoint(config.endpoint),
|
|
920
|
+
endpoint_path: 'SendLogs',
|
|
921
|
+
region: config.region
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
async function exportRecordsToTls(records, config, metadata = {}, options = {}, fileName = 'agent-trace') {
|
|
925
|
+
if (0 === records.length) return {
|
|
926
|
+
status: 'skipped',
|
|
927
|
+
reason: 'empty_records'
|
|
928
|
+
};
|
|
929
|
+
const body = JSON.stringify({
|
|
930
|
+
records
|
|
931
|
+
});
|
|
932
|
+
const startedAt = Date.now();
|
|
933
|
+
const traceId = 'string' == typeof metadata.trace_id ? metadata.trace_id : records[0]?.TraceID ?? records[0]?.['trace.id'] ?? '';
|
|
934
|
+
const spanCount = Number.isFinite(Number(metadata.span_count)) ? Number(metadata.span_count) : records.length;
|
|
935
|
+
notifyRequestBody(options, {
|
|
936
|
+
body,
|
|
937
|
+
attempt: 1,
|
|
938
|
+
request_kind: 'tls_log_records',
|
|
939
|
+
payload_bytes: Buffer.byteLength(body, 'utf8'),
|
|
940
|
+
record_count: records.length,
|
|
941
|
+
span_count: spanCount,
|
|
942
|
+
trace_id: traceId,
|
|
943
|
+
...endpointDiagnostics(config)
|
|
944
|
+
});
|
|
945
|
+
try {
|
|
946
|
+
await sendTlsLogRecords(config, records, fileName);
|
|
947
|
+
return {
|
|
948
|
+
status: 'sent',
|
|
949
|
+
trace_id: traceId,
|
|
950
|
+
span_count: spanCount,
|
|
951
|
+
record_count: records.length,
|
|
952
|
+
payload_bytes: Buffer.byteLength(body, 'utf8'),
|
|
953
|
+
attempts: 1,
|
|
954
|
+
total_duration_ms: Date.now() - startedAt,
|
|
955
|
+
...endpointDiagnostics(config)
|
|
956
|
+
};
|
|
957
|
+
} catch (error) {
|
|
958
|
+
return {
|
|
959
|
+
status: 'failed',
|
|
960
|
+
reason: 'producer_error',
|
|
961
|
+
message: error instanceof Error ? error.message : String(error),
|
|
962
|
+
trace_id: traceId,
|
|
963
|
+
span_count: spanCount,
|
|
964
|
+
record_count: records.length,
|
|
965
|
+
payload_bytes: Buffer.byteLength(body, 'utf8'),
|
|
966
|
+
attempts: 1,
|
|
967
|
+
total_duration_ms: Date.now() - startedAt,
|
|
968
|
+
error_name: error instanceof Error ? error.name : void 0,
|
|
969
|
+
...endpointDiagnostics(config)
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
async function exportTraceToTls(trace, env = process.env, options = {}) {
|
|
974
|
+
const spans = records_traceSpansWithRoot(trace);
|
|
975
|
+
if (!trace || 0 === spans.length) return {
|
|
976
|
+
status: 'skipped',
|
|
977
|
+
reason: 'empty_trace'
|
|
978
|
+
};
|
|
979
|
+
const config = config_resolveTlsConfig(env);
|
|
980
|
+
if (!config.enabled) return {
|
|
981
|
+
status: 'skipped',
|
|
982
|
+
reason: config.reason,
|
|
983
|
+
missing: config.missing
|
|
984
|
+
};
|
|
985
|
+
return exportRecordsToTls(recordsFromTrace(trace), config, {
|
|
986
|
+
span_count: spans.length,
|
|
987
|
+
trace_id: exporter_traceIdOf(trace)
|
|
988
|
+
}, options, `${trace.source?.tool ?? 'agent'}-trace`);
|
|
989
|
+
}
|
|
990
|
+
function file_store_hasErrorCode(error, code) {
|
|
991
|
+
return 'object' == typeof error && null !== error && 'code' in error && error.code === code;
|
|
992
|
+
}
|
|
993
|
+
async function ensureParent(filePath) {
|
|
994
|
+
await __rspack_external_node_fs_promises_153e37e0.mkdir(__rspack_external_node_path_c5b9b54f.dirname(filePath), {
|
|
995
|
+
recursive: true
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
async function writeJsonFile(filePath, value, options = {}) {
|
|
999
|
+
await ensureParent(filePath);
|
|
1000
|
+
const tempPath = `${filePath}.${process.pid}.tmp`;
|
|
1001
|
+
const writeOptions = options.mode ? {
|
|
1002
|
+
encoding: 'utf8',
|
|
1003
|
+
mode: options.mode
|
|
1004
|
+
} : 'utf8';
|
|
1005
|
+
await __rspack_external_node_fs_promises_153e37e0.writeFile(tempPath, `${JSON.stringify(value, null, 2)}\n`, writeOptions);
|
|
1006
|
+
await __rspack_external_node_fs_promises_153e37e0.rename(tempPath, filePath);
|
|
1007
|
+
if (options.mode) await __rspack_external_node_fs_promises_153e37e0.chmod(filePath, options.mode);
|
|
1008
|
+
}
|
|
1009
|
+
async function appendJsonl(filePath, value) {
|
|
1010
|
+
await ensureParent(filePath);
|
|
1011
|
+
await __rspack_external_node_fs_promises_153e37e0.appendFile(filePath, `${JSON.stringify(value)}\n`, 'utf8');
|
|
1012
|
+
}
|
|
1013
|
+
async function readJsonl(filePath) {
|
|
1014
|
+
let text;
|
|
1015
|
+
try {
|
|
1016
|
+
text = await __rspack_external_node_fs_promises_153e37e0.readFile(filePath, 'utf8');
|
|
1017
|
+
} catch (error) {
|
|
1018
|
+
if (file_store_hasErrorCode(error, 'ENOENT')) return [];
|
|
1019
|
+
throw error;
|
|
1020
|
+
}
|
|
1021
|
+
return text.split(/\r?\n/).filter(Boolean).map((line)=>JSON.parse(line));
|
|
1022
|
+
}
|
|
1023
|
+
function safeName(value) {
|
|
1024
|
+
return String(value || 'trace').replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 160) || 'trace';
|
|
1025
|
+
}
|
|
1026
|
+
async function writeFailedTraceCache(paths, key, trace, body, result, config = {}) {
|
|
1027
|
+
if (result?.status !== 'failed') return {
|
|
1028
|
+
persisted: false,
|
|
1029
|
+
reason: 'not_failed'
|
|
1030
|
+
};
|
|
1031
|
+
if (!body) return {
|
|
1032
|
+
persisted: false,
|
|
1033
|
+
reason: 'missing_otlp_payload'
|
|
1034
|
+
};
|
|
1035
|
+
if (otlpPayloadContainsTlsCredentials(body)) return {
|
|
1036
|
+
persisted: false,
|
|
1037
|
+
reason: 'payload_contains_tls_credentials'
|
|
1038
|
+
};
|
|
1039
|
+
const now = Date.now();
|
|
1040
|
+
const retryIntervalMs = config.retryIntervalMs ?? 300000;
|
|
1041
|
+
const caseDir = __rspack_external_node_path_c5b9b54f.join(paths.failedTracesDir, `${safeName(key.slice(0, 16))}.${safeName(trace.source?.tool)}`);
|
|
1042
|
+
const payloadPath = __rspack_external_node_path_c5b9b54f.join(caseDir, 'payload.otlp.json');
|
|
1043
|
+
const manifestPath = __rspack_external_node_path_c5b9b54f.join(caseDir, 'manifest.json');
|
|
1044
|
+
await __rspack_external_node_fs_promises_153e37e0.mkdir(caseDir, {
|
|
1045
|
+
recursive: true
|
|
1046
|
+
});
|
|
1047
|
+
await __rspack_external_node_fs_promises_153e37e0.writeFile(payloadPath, body, {
|
|
1048
|
+
encoding: 'utf8',
|
|
1049
|
+
mode: 384
|
|
1050
|
+
});
|
|
1051
|
+
await __rspack_external_node_fs_promises_153e37e0.chmod(payloadPath, 384);
|
|
1052
|
+
const manifest = {
|
|
1053
|
+
version: 1,
|
|
1054
|
+
state: 'pending',
|
|
1055
|
+
created_at: new Date(now).toISOString(),
|
|
1056
|
+
updated_at: new Date(now).toISOString(),
|
|
1057
|
+
next_retry_at: new Date(now + retryIntervalMs).toISOString(),
|
|
1058
|
+
retry_interval_ms: retryIntervalMs,
|
|
1059
|
+
retry_count: 0,
|
|
1060
|
+
max_retry_count: config.maxRetryCount ?? 1,
|
|
1061
|
+
dedup_key: key,
|
|
1062
|
+
payload_path: payloadPath,
|
|
1063
|
+
source_tool: trace.source?.tool,
|
|
1064
|
+
session_id: trace.source?.sessionId,
|
|
1065
|
+
turn_id: trace.source?.turnId,
|
|
1066
|
+
span_count: (trace.spans?.length ?? 0) + 1,
|
|
1067
|
+
payload_bytes: Buffer.byteLength(body, 'utf8'),
|
|
1068
|
+
trace_export: result
|
|
1069
|
+
};
|
|
1070
|
+
await writeJsonFile(manifestPath, manifest, {
|
|
1071
|
+
mode: 384
|
|
1072
|
+
});
|
|
1073
|
+
return {
|
|
1074
|
+
persisted: true,
|
|
1075
|
+
manifest_path: manifestPath,
|
|
1076
|
+
next_retry_at: manifest.next_retry_at
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
function dedup_hasErrorCode(error, code) {
|
|
1080
|
+
return 'object' == typeof error && null !== error && 'code' in error && error.code === code;
|
|
1081
|
+
}
|
|
1082
|
+
function dedupKeyForTrace(trace) {
|
|
1083
|
+
const seed = [
|
|
1084
|
+
trace.source?.tool,
|
|
1085
|
+
trace.source?.sessionId,
|
|
1086
|
+
trace.source?.turnId,
|
|
1087
|
+
trace.source?.threadId,
|
|
1088
|
+
trace.root?.name,
|
|
1089
|
+
trace.root?.startedAt
|
|
1090
|
+
].join(':');
|
|
1091
|
+
return external_node_crypto_createHash('sha256').update(seed).digest('hex');
|
|
1092
|
+
}
|
|
1093
|
+
async function hasUploaded(uploadedFile, key) {
|
|
1094
|
+
return (await readJsonl(uploadedFile)).some((entry)=>entry.key === key);
|
|
1095
|
+
}
|
|
1096
|
+
async function markUploaded(uploadedFile, key, trace, result) {
|
|
1097
|
+
await appendJsonl(uploadedFile, {
|
|
1098
|
+
key,
|
|
1099
|
+
source_tool: trace.source?.tool,
|
|
1100
|
+
session_id: trace.source?.sessionId,
|
|
1101
|
+
turn_id: trace.source?.turnId,
|
|
1102
|
+
uploaded_at: new Date().toISOString(),
|
|
1103
|
+
span_count: (trace.spans?.length ?? 0) + 1,
|
|
1104
|
+
trace_export: {
|
|
1105
|
+
status: result.status,
|
|
1106
|
+
reason: result.reason
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
async function acquireLock(locksDir, key, timeoutMs = 120000) {
|
|
1111
|
+
await __rspack_external_node_fs_promises_153e37e0.mkdir(locksDir, {
|
|
1112
|
+
recursive: true
|
|
1113
|
+
});
|
|
1114
|
+
const lockPath = __rspack_external_node_path_c5b9b54f.join(locksDir, `${key}.lock`);
|
|
1115
|
+
try {
|
|
1116
|
+
const handle = await __rspack_external_node_fs_promises_153e37e0.open(lockPath, 'wx');
|
|
1117
|
+
await handle.writeFile(JSON.stringify({
|
|
1118
|
+
pid: process.pid,
|
|
1119
|
+
created_at: new Date().toISOString()
|
|
1120
|
+
}));
|
|
1121
|
+
await handle.close();
|
|
1122
|
+
return {
|
|
1123
|
+
acquired: true,
|
|
1124
|
+
lockPath
|
|
1125
|
+
};
|
|
1126
|
+
} catch (error) {
|
|
1127
|
+
if (!dedup_hasErrorCode(error, 'EEXIST')) throw error;
|
|
1128
|
+
}
|
|
1129
|
+
const stat = await __rspack_external_node_fs_promises_153e37e0.stat(lockPath).catch(()=>null);
|
|
1130
|
+
if (stat && Date.now() - stat.mtimeMs > timeoutMs) {
|
|
1131
|
+
await __rspack_external_node_fs_promises_153e37e0.rm(lockPath, {
|
|
1132
|
+
force: true
|
|
1133
|
+
});
|
|
1134
|
+
return acquireLock(locksDir, key, timeoutMs);
|
|
1135
|
+
}
|
|
1136
|
+
return {
|
|
1137
|
+
acquired: false,
|
|
1138
|
+
lockPath,
|
|
1139
|
+
reason: 'dedup_lock_timeout'
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
async function releaseLock(lock) {
|
|
1143
|
+
if (lock?.acquired && lock.lockPath) await __rspack_external_node_fs_promises_153e37e0.rm(lock.lockPath, {
|
|
1144
|
+
force: true
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
function paths_defaultDataRoot(source = 'agent') {
|
|
1148
|
+
return __rspack_external_node_path_c5b9b54f.join(homedir(), '.codex', 'plugins', 'data', `storage-tls-${source}`);
|
|
1149
|
+
}
|
|
1150
|
+
function runtimePaths(config = {}) {
|
|
1151
|
+
const source = config.sourceName ?? config.source?.tool ?? 'agent';
|
|
1152
|
+
const dataRoot = __rspack_external_node_path_c5b9b54f.resolve(config.dataRoot ?? process.env.TRACE_TLS_DATA_ROOT ?? process.env.CODEX_TLS_DATA_ROOT ?? paths_defaultDataRoot(source));
|
|
1153
|
+
return {
|
|
1154
|
+
dataRoot,
|
|
1155
|
+
sendLogPath: __rspack_external_node_path_c5b9b54f.join(dataRoot, 'logs', 'send', 'latest.send-log.json'),
|
|
1156
|
+
diagnosticsDir: __rspack_external_node_path_c5b9b54f.join(dataRoot, 'logs', 'diagnostics'),
|
|
1157
|
+
uploadedFile: __rspack_external_node_path_c5b9b54f.join(dataRoot, 'state', 'dedup', 'uploaded.jsonl'),
|
|
1158
|
+
locksDir: __rspack_external_node_path_c5b9b54f.join(dataRoot, 'state', 'dedup', 'locks'),
|
|
1159
|
+
failedTracesDir: __rspack_external_node_path_c5b9b54f.join(dataRoot, 'state', 'failed-traces')
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
function nowIso() {
|
|
1163
|
+
return new Date().toISOString();
|
|
1164
|
+
}
|
|
1165
|
+
function metadataForTrace(trace, otelTrace) {
|
|
1166
|
+
return {
|
|
1167
|
+
span_count: otelTrace.spans.length,
|
|
1168
|
+
trace_id: otelTrace.traceId,
|
|
1169
|
+
source_tool: trace.source?.tool,
|
|
1170
|
+
session_id: trace.source?.sessionId,
|
|
1171
|
+
turn_id: trace.source?.turnId
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
async function writeSendLog(paths, event) {
|
|
1175
|
+
await writeJsonFile(paths.sendLogPath, {
|
|
1176
|
+
generated_at: nowIso(),
|
|
1177
|
+
...event
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
async function runTracePipeline(input, adapter, config = {}) {
|
|
1181
|
+
if (!adapter || 'function' != typeof adapter.load || 'function' != typeof adapter.toCanonical) throw new Error('A SourceAdapter with load() and toCanonical() is required');
|
|
1182
|
+
if ('function' == typeof adapter.detect && !adapter.detect(input)) return {
|
|
1183
|
+
status: 'skipped',
|
|
1184
|
+
reason: 'adapter_not_detected',
|
|
1185
|
+
adapter: adapter.name
|
|
1186
|
+
};
|
|
1187
|
+
const context = {
|
|
1188
|
+
env: config.env ?? process.env,
|
|
1189
|
+
dataRoot: config.dataRoot,
|
|
1190
|
+
now: config.now ?? Date.now,
|
|
1191
|
+
source: config.source ?? {}
|
|
1192
|
+
};
|
|
1193
|
+
const session = await adapter.load(input, context);
|
|
1194
|
+
const trace = assertCanonicalTrace(await adapter.toCanonical(session, context));
|
|
1195
|
+
const paths = runtimePaths({
|
|
1196
|
+
...config,
|
|
1197
|
+
source: trace.source,
|
|
1198
|
+
sourceName: trace.source?.tool
|
|
1199
|
+
});
|
|
1200
|
+
const key = config.dedupKey ?? dedupKeyForTrace(trace);
|
|
1201
|
+
if (false !== config.dedup && await hasUploaded(paths.uploadedFile, key)) {
|
|
1202
|
+
await writeSendLog(paths, {
|
|
1203
|
+
status: 'deduped',
|
|
1204
|
+
dedup_key: key,
|
|
1205
|
+
source: trace.source
|
|
1206
|
+
});
|
|
1207
|
+
return {
|
|
1208
|
+
status: 'deduped',
|
|
1209
|
+
dedup_key: key
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
const lock = false === config.dedup ? {
|
|
1213
|
+
acquired: true
|
|
1214
|
+
} : await acquireLock(paths.locksDir, key, config.dedupLockTimeoutMs);
|
|
1215
|
+
if (!lock.acquired) {
|
|
1216
|
+
await writeSendLog(paths, {
|
|
1217
|
+
status: 'skipped',
|
|
1218
|
+
reason: lock.reason,
|
|
1219
|
+
dedup_key: key,
|
|
1220
|
+
source: trace.source
|
|
1221
|
+
});
|
|
1222
|
+
return {
|
|
1223
|
+
status: 'skipped',
|
|
1224
|
+
reason: lock.reason,
|
|
1225
|
+
dedup_key: key
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
try {
|
|
1229
|
+
const otelTrace = canonicalTraceToOtelTrace(trace, config.otel);
|
|
1230
|
+
let capturedRequestBody = '';
|
|
1231
|
+
const metadata = metadataForTrace(trace, otelTrace);
|
|
1232
|
+
const traceExport = await exportTraceToTls(otelTrace, context.env, {
|
|
1233
|
+
...config.tlsOptions ?? {},
|
|
1234
|
+
onRequestBody: (request)=>{
|
|
1235
|
+
if ('string' == typeof request.body) capturedRequestBody = request.body;
|
|
1236
|
+
config.tlsOptions?.onRequestBody?.(request);
|
|
1237
|
+
}
|
|
1238
|
+
});
|
|
1239
|
+
let failedTraceCache = {
|
|
1240
|
+
persisted: false,
|
|
1241
|
+
reason: 'not_failed'
|
|
1242
|
+
};
|
|
1243
|
+
if ('sent' === traceExport.status && false !== config.dedup) await markUploaded(paths.uploadedFile, key, trace, traceExport);
|
|
1244
|
+
else if ('failed' === traceExport.status) failedTraceCache = await writeFailedTraceCache(paths, key, trace, capturedRequestBody, traceExport, config.failedTraceCache);
|
|
1245
|
+
await writeSendLog(paths, {
|
|
1246
|
+
status: traceExport.status,
|
|
1247
|
+
dedup_key: key,
|
|
1248
|
+
source: trace.source,
|
|
1249
|
+
trace_export: traceExport,
|
|
1250
|
+
failed_trace_cache: failedTraceCache,
|
|
1251
|
+
otlp: metadata
|
|
1252
|
+
});
|
|
1253
|
+
return {
|
|
1254
|
+
status: traceExport.status,
|
|
1255
|
+
dedup_key: key,
|
|
1256
|
+
trace_export: traceExport,
|
|
1257
|
+
failed_trace_cache: failedTraceCache
|
|
1258
|
+
};
|
|
1259
|
+
} finally{
|
|
1260
|
+
await releaseLock(lock);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
new Map();
|
|
1264
|
+
const TLS_APP_TYPE = 'pi';
|
|
1265
|
+
const ROOT_SPAN_NAME = 'agent.turn';
|
|
1266
|
+
const LLM_SPAN_NAME = 'llm.request';
|
|
1267
|
+
const TOOL_SPAN_NAME = 'tool.call';
|
|
1268
|
+
function pi_isRecord(value) {
|
|
1269
|
+
return null != value && 'object' == typeof value && !Array.isArray(value);
|
|
1270
|
+
}
|
|
1271
|
+
function firstString(...values) {
|
|
1272
|
+
for (const value of values)if ('string' == typeof value && value.length > 0) return value;
|
|
1273
|
+
}
|
|
1274
|
+
function numberValue(value) {
|
|
1275
|
+
return 'number' == typeof value && Number.isFinite(value) ? value : void 0;
|
|
1276
|
+
}
|
|
1277
|
+
function compactRecord(input) {
|
|
1278
|
+
return Object.fromEntries(Object.entries(input).filter(([, value])=>null != value && '' !== value));
|
|
1279
|
+
}
|
|
1280
|
+
function captureEnabled(input, context) {
|
|
1281
|
+
if ('boolean' == typeof input.captureContent) return input.captureContent;
|
|
1282
|
+
const raw = context.env?.PI_TLS_CAPTURE_CONTENT ?? context.env?.TRACE_TLS_CAPTURE_CONTENT;
|
|
1283
|
+
if (null == raw || '' === raw) return true;
|
|
1284
|
+
return !/^(0|false|no|n|off)$/i.test(raw);
|
|
1285
|
+
}
|
|
1286
|
+
function safeContent(value, captureContent) {
|
|
1287
|
+
return captureContent ? value : void 0;
|
|
1288
|
+
}
|
|
1289
|
+
function normalizedRole(role) {
|
|
1290
|
+
if ('toolResult' === role || 'tool_result' === role) return 'tool';
|
|
1291
|
+
if ('developer' === role) return 'system';
|
|
1292
|
+
return role;
|
|
1293
|
+
}
|
|
1294
|
+
function rawToolCalls(value) {
|
|
1295
|
+
if (!pi_isRecord(value)) return;
|
|
1296
|
+
const calls = value.toolCalls ?? value.tool_calls;
|
|
1297
|
+
if (!Array.isArray(calls)) return;
|
|
1298
|
+
const normalized = calls.flatMap((call)=>{
|
|
1299
|
+
if (!pi_isRecord(call)) return [];
|
|
1300
|
+
const fn = pi_isRecord(call.function) ? call.function : {};
|
|
1301
|
+
const id = firstString(call.id, call.toolCallId, call.callId);
|
|
1302
|
+
if (!id) return [];
|
|
1303
|
+
return [
|
|
1304
|
+
{
|
|
1305
|
+
id,
|
|
1306
|
+
name: firstString(call.name, call.toolName, fn.name),
|
|
1307
|
+
arguments: call.arguments ?? call.input ?? fn.arguments
|
|
1308
|
+
}
|
|
1309
|
+
];
|
|
1310
|
+
});
|
|
1311
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
1312
|
+
}
|
|
1313
|
+
function contentBlocks(content, toolCalls, captureContent) {
|
|
1314
|
+
const blocks = [];
|
|
1315
|
+
const seenToolCallIds = new Set();
|
|
1316
|
+
const append = (item)=>{
|
|
1317
|
+
if ('string' == typeof item) return void blocks.push(compactRecord({
|
|
1318
|
+
type: 'text',
|
|
1319
|
+
content: safeContent(item, captureContent)
|
|
1320
|
+
}));
|
|
1321
|
+
if (!pi_isRecord(item)) return;
|
|
1322
|
+
const type = firstString(item.type) ?? 'unknown';
|
|
1323
|
+
if ([
|
|
1324
|
+
'text',
|
|
1325
|
+
'input_text',
|
|
1326
|
+
'output_text'
|
|
1327
|
+
].includes(type)) return void blocks.push(compactRecord({
|
|
1328
|
+
type: 'text',
|
|
1329
|
+
content: safeContent(item.text ?? item.content, captureContent)
|
|
1330
|
+
}));
|
|
1331
|
+
if ([
|
|
1332
|
+
'thinking',
|
|
1333
|
+
'reasoning'
|
|
1334
|
+
].includes(type)) return void blocks.push(compactRecord({
|
|
1335
|
+
type: 'reasoning',
|
|
1336
|
+
content: safeContent(item.thinking ?? item.text ?? item.content, captureContent)
|
|
1337
|
+
}));
|
|
1338
|
+
if ([
|
|
1339
|
+
'toolCall',
|
|
1340
|
+
'tool_call',
|
|
1341
|
+
'tool_use',
|
|
1342
|
+
'function_call'
|
|
1343
|
+
].includes(type)) {
|
|
1344
|
+
const fn = pi_isRecord(item.function) ? item.function : {};
|
|
1345
|
+
const id = firstString(item.id, item.toolCallId, item.callId, item.tool_use_id);
|
|
1346
|
+
if (id) seenToolCallIds.add(id);
|
|
1347
|
+
blocks.push(compactRecord({
|
|
1348
|
+
type: 'tool_call',
|
|
1349
|
+
id,
|
|
1350
|
+
name: firstString(item.name, item.toolName, item.tool, fn.name),
|
|
1351
|
+
arguments: safeContent(item.arguments ?? item.input ?? item.args ?? fn.arguments, captureContent)
|
|
1352
|
+
}));
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
if ('image' === type || 'image_url' === type || 'file' === type) {
|
|
1356
|
+
const source = pi_isRecord(item.source) ? item.source : {};
|
|
1357
|
+
blocks.push(compactRecord({
|
|
1358
|
+
type: 'file',
|
|
1359
|
+
id: item.filename ?? item.id,
|
|
1360
|
+
url: safeContent(item.url ?? item.image_url ?? source.data, captureContent),
|
|
1361
|
+
mime_type: item.mime ?? item.mimeType ?? item.media_type ?? source.media_type
|
|
1362
|
+
}));
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
blocks.push(compactRecord({
|
|
1366
|
+
type,
|
|
1367
|
+
value: safeContent(item, captureContent)
|
|
1368
|
+
}));
|
|
1369
|
+
};
|
|
1370
|
+
if (Array.isArray(content)) content.forEach(append);
|
|
1371
|
+
else if (void 0 !== content) append(content);
|
|
1372
|
+
for (const toolCall of toolCalls ?? [])if (!seenToolCallIds.has(toolCall.id)) blocks.push(compactRecord({
|
|
1373
|
+
type: 'tool_call',
|
|
1374
|
+
id: toolCall.id,
|
|
1375
|
+
name: toolCall.name,
|
|
1376
|
+
arguments: safeContent(toolCall.arguments, captureContent)
|
|
1377
|
+
}));
|
|
1378
|
+
return blocks;
|
|
1379
|
+
}
|
|
1380
|
+
function toolResultContent(content) {
|
|
1381
|
+
if (!Array.isArray(content)) return content;
|
|
1382
|
+
const texts = content.map((item)=>{
|
|
1383
|
+
if ('string' == typeof item) return item;
|
|
1384
|
+
if (!pi_isRecord(item) || ![
|
|
1385
|
+
'text',
|
|
1386
|
+
'input_text',
|
|
1387
|
+
'output_text'
|
|
1388
|
+
].includes(String(item.type))) return;
|
|
1389
|
+
return firstString(item.text, item.content);
|
|
1390
|
+
});
|
|
1391
|
+
return texts.every((text)=>void 0 !== text) ? texts.join('\n') : content;
|
|
1392
|
+
}
|
|
1393
|
+
function messageTime(message, fallback) {
|
|
1394
|
+
return numberValue(message.endedAt) ?? numberValue(message.createdAt) ?? fallback;
|
|
1395
|
+
}
|
|
1396
|
+
function usageAttributes(usage) {
|
|
1397
|
+
if (!usage) return {};
|
|
1398
|
+
const input = numberValue(usage.inputTokens) ?? 0;
|
|
1399
|
+
const output = numberValue(usage.outputTokens) ?? 0;
|
|
1400
|
+
const reasoning = numberValue(usage.reasoningOutputTokens) ?? 0;
|
|
1401
|
+
const cacheRead = numberValue(usage.cacheReadInputTokens) ?? 0;
|
|
1402
|
+
const cacheCreation = numberValue(usage.cacheCreationInputTokens) ?? 0;
|
|
1403
|
+
const total = numberValue(usage.totalTokens) ?? (input || output || reasoning ? input + output + reasoning : 0);
|
|
1404
|
+
return compactRecord({
|
|
1405
|
+
'gen_ai.usage.input_tokens': input || void 0,
|
|
1406
|
+
'gen_ai.usage.output_tokens': output || void 0,
|
|
1407
|
+
'gen_ai.usage.reasoning.output_tokens': reasoning || void 0,
|
|
1408
|
+
'gen_ai.usage.cache_read.input_tokens': cacheRead || void 0,
|
|
1409
|
+
'gen_ai.usage.cache_creation.input_tokens': cacheCreation || void 0,
|
|
1410
|
+
'gen_ai.usage.total_tokens': total || void 0
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
function sumUsage(requests) {
|
|
1414
|
+
const keys = [
|
|
1415
|
+
'gen_ai.usage.input_tokens',
|
|
1416
|
+
'gen_ai.usage.output_tokens',
|
|
1417
|
+
'gen_ai.usage.reasoning.output_tokens',
|
|
1418
|
+
'gen_ai.usage.cache_read.input_tokens',
|
|
1419
|
+
'gen_ai.usage.cache_creation.input_tokens',
|
|
1420
|
+
'gen_ai.usage.total_tokens'
|
|
1421
|
+
];
|
|
1422
|
+
const records = requests.map((request)=>usageAttributes(request.usage));
|
|
1423
|
+
return compactRecord(Object.fromEntries(keys.map((key)=>[
|
|
1424
|
+
key,
|
|
1425
|
+
records.reduce((sum, record)=>{
|
|
1426
|
+
const value = record[key];
|
|
1427
|
+
return 'number' == typeof value && Number.isFinite(value) ? sum + value : sum;
|
|
1428
|
+
}, 0) || void 0
|
|
1429
|
+
])));
|
|
1430
|
+
}
|
|
1431
|
+
function toGenAiMessages(messages, captureContent) {
|
|
1432
|
+
return messages.map((message)=>{
|
|
1433
|
+
const role = normalizedRole(message.role);
|
|
1434
|
+
if ('tool' === role) return compactRecord({
|
|
1435
|
+
role,
|
|
1436
|
+
name: message.toolName,
|
|
1437
|
+
tool_call_id: message.toolCallId,
|
|
1438
|
+
content: safeContent(toolResultContent(message.content), captureContent)
|
|
1439
|
+
});
|
|
1440
|
+
return compactRecord({
|
|
1441
|
+
role,
|
|
1442
|
+
parts: contentBlocks(message.content, message.toolCalls, captureContent)
|
|
1443
|
+
});
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
function promptMessages(input, captureContent) {
|
|
1447
|
+
const userMessages = input.messages.filter((message)=>'user' === message.role);
|
|
1448
|
+
if (userMessages.length > 0) return toGenAiMessages(userMessages, captureContent);
|
|
1449
|
+
return [
|
|
1450
|
+
compactRecord({
|
|
1451
|
+
role: 'user',
|
|
1452
|
+
content: safeContent(compactRecord({
|
|
1453
|
+
prompt: input.prompt,
|
|
1454
|
+
images: input.images,
|
|
1455
|
+
context: input.context
|
|
1456
|
+
}), captureContent)
|
|
1457
|
+
})
|
|
1458
|
+
];
|
|
1459
|
+
}
|
|
1460
|
+
function turnOutputMessages(input, captureContent) {
|
|
1461
|
+
return toGenAiMessages(input.messages.filter((message)=>[
|
|
1462
|
+
'assistant',
|
|
1463
|
+
'tool',
|
|
1464
|
+
'toolResult',
|
|
1465
|
+
'tool_result'
|
|
1466
|
+
].includes(message.role)), captureContent);
|
|
1467
|
+
}
|
|
1468
|
+
function toTraceMessages(messages, captureContent) {
|
|
1469
|
+
return messages.map((message)=>({
|
|
1470
|
+
role: message.role,
|
|
1471
|
+
content: safeContent(message.content, captureContent),
|
|
1472
|
+
timestamp: new Date(messageTime(message, Date.now())).toISOString(),
|
|
1473
|
+
attributes: compactRecord({
|
|
1474
|
+
'pi.message_id': message.id,
|
|
1475
|
+
'pi.provider_request_id': message.providerRequestId,
|
|
1476
|
+
'gen_ai.request.model': message.model,
|
|
1477
|
+
'gen_ai.provider.name': message.provider
|
|
1478
|
+
})
|
|
1479
|
+
}));
|
|
1480
|
+
}
|
|
1481
|
+
function lastProviderRequest(input) {
|
|
1482
|
+
return [
|
|
1483
|
+
...input.providerRequests
|
|
1484
|
+
].reverse()[0];
|
|
1485
|
+
}
|
|
1486
|
+
function pi_commonAttributes(input, pluginVersion) {
|
|
1487
|
+
return compactRecord({
|
|
1488
|
+
'tls.app.type': TLS_APP_TYPE,
|
|
1489
|
+
'tls.plugin.version': pluginVersion,
|
|
1490
|
+
'session.id': input.session.id,
|
|
1491
|
+
'gen_ai.conversation.id': input.session.id,
|
|
1492
|
+
'agent.name': 'pi',
|
|
1493
|
+
'agent.version': input.session.version ?? pluginVersion,
|
|
1494
|
+
'user.name': userInfo().username
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
function providerRequestInputMessages(request, input, captureContent) {
|
|
1498
|
+
const payload = pi_isRecord(request.input) ? request.input : {};
|
|
1499
|
+
const rawMessages = Array.isArray(request.input) ? request.input : Array.isArray(payload.messages) ? payload.messages : [];
|
|
1500
|
+
const observedRawMessages = rawMessages.flatMap((message)=>{
|
|
1501
|
+
if (!pi_isRecord(message)) return [];
|
|
1502
|
+
return [
|
|
1503
|
+
{
|
|
1504
|
+
role: firstString(message.role) ?? 'unknown',
|
|
1505
|
+
content: message.content ?? message.parts,
|
|
1506
|
+
toolCallId: firstString(message.toolCallId, message.tool_call_id),
|
|
1507
|
+
toolName: firstString(message.toolName, message.name),
|
|
1508
|
+
toolCalls: rawToolCalls(message)
|
|
1509
|
+
}
|
|
1510
|
+
];
|
|
1511
|
+
});
|
|
1512
|
+
const payloadSystemMessages = observedRawMessages.filter((message)=>'system' === normalizedRole(message.role));
|
|
1513
|
+
const explicitSystem = payload.system ?? payload.instructions;
|
|
1514
|
+
const systemMessages = payloadSystemMessages.length > 0 ? toGenAiMessages(payloadSystemMessages, captureContent) : void 0 !== explicitSystem ? toGenAiMessages([
|
|
1515
|
+
{
|
|
1516
|
+
role: 'system',
|
|
1517
|
+
content: explicitSystem
|
|
1518
|
+
}
|
|
1519
|
+
], captureContent) : [];
|
|
1520
|
+
const outputIndex = input.messages.findIndex((message)=>message.providerRequestId === request.requestId && 'assistant' === message.role);
|
|
1521
|
+
if (outputIndex >= 0) {
|
|
1522
|
+
const currentTurnPrefix = input.messages.slice(0, outputIndex).filter((message)=>'system' !== normalizedRole(message.role));
|
|
1523
|
+
return [
|
|
1524
|
+
...systemMessages,
|
|
1525
|
+
...toGenAiMessages(currentTurnPrefix, captureContent)
|
|
1526
|
+
];
|
|
1527
|
+
}
|
|
1528
|
+
const nonSystemRawMessages = observedRawMessages.filter((message)=>'system' !== normalizedRole(message.role));
|
|
1529
|
+
const lastUserIndex = nonSystemRawMessages.map((message)=>normalizedRole(message.role)).lastIndexOf('user');
|
|
1530
|
+
const currentTurnMessages = lastUserIndex >= 0 ? nonSystemRawMessages.slice(lastUserIndex) : nonSystemRawMessages;
|
|
1531
|
+
return [
|
|
1532
|
+
...systemMessages,
|
|
1533
|
+
...toGenAiMessages(currentTurnMessages, captureContent)
|
|
1534
|
+
];
|
|
1535
|
+
}
|
|
1536
|
+
function providerRequestOutputMessages(request, input, captureContent) {
|
|
1537
|
+
const messages = input.messages.filter((message)=>message.providerRequestId === request.requestId && 'assistant' === message.role);
|
|
1538
|
+
if (messages.length > 0) return toGenAiMessages(messages, captureContent);
|
|
1539
|
+
const rawOutput = pi_isRecord(request.output) ? request.output : void 0;
|
|
1540
|
+
return [
|
|
1541
|
+
...toGenAiMessages([
|
|
1542
|
+
{
|
|
1543
|
+
role: 'assistant',
|
|
1544
|
+
content: rawOutput?.content ?? request.output,
|
|
1545
|
+
toolCalls: rawToolCalls(rawOutput)
|
|
1546
|
+
}
|
|
1547
|
+
], captureContent)
|
|
1548
|
+
];
|
|
1549
|
+
}
|
|
1550
|
+
function detectPiInput(input) {
|
|
1551
|
+
return pi_isRecord(input) && 'pi' === input.source && 'string' == typeof input.turnId && pi_isRecord(input.session);
|
|
1552
|
+
}
|
|
1553
|
+
async function loadPiTurn(input) {
|
|
1554
|
+
if (!detectPiInput(input)) throw new Error('Pi adapter requires a turn snapshot');
|
|
1555
|
+
return input;
|
|
1556
|
+
}
|
|
1557
|
+
async function toCanonicalPiTrace(input, context = {}) {
|
|
1558
|
+
const captureContent = captureEnabled(input, context);
|
|
1559
|
+
const pluginVersion = input.pluginVersion ?? context.version ?? '0.0.0';
|
|
1560
|
+
const now = context.now?.() ?? Date.now();
|
|
1561
|
+
const traceId = model_traceIdFromSeed(`pi:${input.session.id}:${input.turnId}`);
|
|
1562
|
+
const rootSpanId = spanIdFromSeed(traceId, 'pi-turn', input.turnId);
|
|
1563
|
+
const common = pi_commonAttributes(input, pluginVersion);
|
|
1564
|
+
const rootStart = input.startedAt || now;
|
|
1565
|
+
const requestEnds = input.providerRequests.map((request)=>request.endedAt ?? request.startedAt);
|
|
1566
|
+
const toolEnds = input.toolCalls.map((tool)=>tool.endedAt ?? tool.startedAt ?? rootStart);
|
|
1567
|
+
const rootEnd = Math.max(rootStart, input.endedAt ?? 0, ...requestEnds, ...toolEnds);
|
|
1568
|
+
const lastRequest = lastProviderRequest(input);
|
|
1569
|
+
const rootInputMessages = promptMessages(input, captureContent);
|
|
1570
|
+
const rootOutputMessages = turnOutputMessages(input, captureContent);
|
|
1571
|
+
const diagnosticsWarnings = [];
|
|
1572
|
+
const root = {
|
|
1573
|
+
id: rootSpanId,
|
|
1574
|
+
name: ROOT_SPAN_NAME,
|
|
1575
|
+
kind: 'server',
|
|
1576
|
+
type: 'agent',
|
|
1577
|
+
startedAt: rootStart,
|
|
1578
|
+
endedAt: rootEnd,
|
|
1579
|
+
status: input.providerRequests.some((request)=>'error' === request.status) || input.toolCalls.some((tool)=>'error' === tool.status) ? 'error' : 'unset',
|
|
1580
|
+
input: {
|
|
1581
|
+
messages: rootInputMessages
|
|
1582
|
+
},
|
|
1583
|
+
output: {
|
|
1584
|
+
messages: rootOutputMessages
|
|
1585
|
+
},
|
|
1586
|
+
attributes: compactRecord({
|
|
1587
|
+
...common,
|
|
1588
|
+
...sumUsage(input.providerRequests),
|
|
1589
|
+
'gen_ai.operation.name': 'invoke_agent',
|
|
1590
|
+
'gen_ai.request.model': lastRequest?.model ?? input.model,
|
|
1591
|
+
'gen_ai.provider.name': lastRequest?.provider ?? input.provider,
|
|
1592
|
+
'gen_ai.input.messages': rootInputMessages,
|
|
1593
|
+
'gen_ai.output.messages': rootOutputMessages
|
|
1594
|
+
})
|
|
1595
|
+
};
|
|
1596
|
+
const spans = [];
|
|
1597
|
+
const llmSpanIds = new Map();
|
|
1598
|
+
input.providerRequests.forEach((request, index)=>{
|
|
1599
|
+
const llmSpanId = spanIdFromSeed(traceId, 'pi-llm', request.requestId || index);
|
|
1600
|
+
llmSpanIds.set(request.requestId, llmSpanId);
|
|
1601
|
+
const llmInputMessages = providerRequestInputMessages(request, input, captureContent);
|
|
1602
|
+
const llmOutputMessages = providerRequestOutputMessages(request, input, captureContent);
|
|
1603
|
+
const startedAt = request.startedAt || rootStart;
|
|
1604
|
+
const endedAt = Math.max(startedAt, request.endedAt ?? rootEnd);
|
|
1605
|
+
spans.push({
|
|
1606
|
+
id: llmSpanId,
|
|
1607
|
+
parentId: rootSpanId,
|
|
1608
|
+
name: LLM_SPAN_NAME,
|
|
1609
|
+
kind: 'client',
|
|
1610
|
+
type: 'model',
|
|
1611
|
+
startedAt,
|
|
1612
|
+
endedAt,
|
|
1613
|
+
status: 'error' === request.status ? 'error' : 'unset',
|
|
1614
|
+
input: {
|
|
1615
|
+
messages: llmInputMessages
|
|
1616
|
+
},
|
|
1617
|
+
output: {
|
|
1618
|
+
messages: llmOutputMessages
|
|
1619
|
+
},
|
|
1620
|
+
attributes: compactRecord({
|
|
1621
|
+
...common,
|
|
1622
|
+
...usageAttributes(request.usage),
|
|
1623
|
+
'gen_ai.operation.name': 'chat',
|
|
1624
|
+
'gen_ai.request.model': request.model ?? input.model,
|
|
1625
|
+
'gen_ai.provider.name': request.provider ?? input.provider,
|
|
1626
|
+
'gen_ai.input.messages': llmInputMessages,
|
|
1627
|
+
'gen_ai.output.messages': llmOutputMessages,
|
|
1628
|
+
'gen_ai.request.duration_ms': Math.max(0, endedAt - startedAt),
|
|
1629
|
+
'error.message': 'error' === request.status ? request.error : void 0,
|
|
1630
|
+
'pi.provider_request_id': request.requestId
|
|
1631
|
+
})
|
|
1632
|
+
});
|
|
1633
|
+
});
|
|
1634
|
+
for (const tool of input.toolCalls){
|
|
1635
|
+
const parentId = tool.parentProviderRequestId ? llmSpanIds.get(tool.parentProviderRequestId) : void 0;
|
|
1636
|
+
if (!parentId) diagnosticsWarnings.push(`pi.tool.parent_missing:${tool.toolCallId}`);
|
|
1637
|
+
const startedAt = tool.startedAt ?? rootStart;
|
|
1638
|
+
const endedAt = Math.max(startedAt, tool.endedAt ?? rootEnd);
|
|
1639
|
+
spans.push({
|
|
1640
|
+
id: spanIdFromSeed(traceId, 'pi-tool', tool.toolCallId),
|
|
1641
|
+
parentId: parentId ?? rootSpanId,
|
|
1642
|
+
name: TOOL_SPAN_NAME,
|
|
1643
|
+
kind: 'client',
|
|
1644
|
+
type: 'tool',
|
|
1645
|
+
startedAt,
|
|
1646
|
+
endedAt,
|
|
1647
|
+
status: 'error' === tool.status ? 'error' : 'unset',
|
|
1648
|
+
input: safeContent(tool.arguments, captureContent),
|
|
1649
|
+
output: safeContent('error' === tool.status ? tool.error : tool.result, captureContent),
|
|
1650
|
+
attributes: compactRecord({
|
|
1651
|
+
...common,
|
|
1652
|
+
'gen_ai.tool.name': tool.name,
|
|
1653
|
+
'gen_ai.tool.call.id': tool.toolCallId,
|
|
1654
|
+
'gen_ai.tool.call.arguments': safeContent(tool.arguments, captureContent),
|
|
1655
|
+
'gen_ai.tool.call.result': safeContent('error' === tool.status ? tool.error : tool.result, captureContent),
|
|
1656
|
+
'tool.error': 'error' === tool.status || void 0,
|
|
1657
|
+
'error.message': 'error' === tool.status ? tool.error : void 0,
|
|
1658
|
+
'pi.parent_provider_request_id': tool.parentProviderRequestId
|
|
1659
|
+
})
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
return {
|
|
1663
|
+
source: {
|
|
1664
|
+
tool: 'pi',
|
|
1665
|
+
sessionId: input.session.id,
|
|
1666
|
+
turnId: input.turnId,
|
|
1667
|
+
threadId: input.session.id,
|
|
1668
|
+
conversationId: input.session.id,
|
|
1669
|
+
cwd: input.session.cwd,
|
|
1670
|
+
model: lastRequest?.model ?? input.model
|
|
1671
|
+
},
|
|
1672
|
+
root,
|
|
1673
|
+
spans,
|
|
1674
|
+
messages: toTraceMessages(input.messages, captureContent),
|
|
1675
|
+
metrics: {
|
|
1676
|
+
inputTokens: numberValue(root.attributes?.['gen_ai.usage.input_tokens']),
|
|
1677
|
+
outputTokens: numberValue(root.attributes?.['gen_ai.usage.output_tokens']),
|
|
1678
|
+
totalTokens: numberValue(root.attributes?.['gen_ai.usage.total_tokens']),
|
|
1679
|
+
toolCallCount: input.toolCalls.length,
|
|
1680
|
+
latencyMs: Math.max(0, rootEnd - rootStart)
|
|
1681
|
+
},
|
|
1682
|
+
diagnostics: {
|
|
1683
|
+
warnings: diagnosticsWarnings,
|
|
1684
|
+
parser: {
|
|
1685
|
+
provider_request_count: input.providerRequests.length,
|
|
1686
|
+
tool_call_count: input.toolCalls.length
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
const piAdapter = {
|
|
1692
|
+
name: 'pi',
|
|
1693
|
+
detect: detectPiInput,
|
|
1694
|
+
load: loadPiTurn,
|
|
1695
|
+
toCanonical: toCanonicalPiTrace
|
|
1696
|
+
};
|
|
1697
|
+
function utils_isRecord(value) {
|
|
1698
|
+
return null != value && 'object' == typeof value && !Array.isArray(value);
|
|
1699
|
+
}
|
|
1700
|
+
function stringValue(value) {
|
|
1701
|
+
return 'string' == typeof value && value.length > 0 ? value : void 0;
|
|
1702
|
+
}
|
|
1703
|
+
function numericValue(value) {
|
|
1704
|
+
return 'number' == typeof value && Number.isFinite(value) ? value : void 0;
|
|
1705
|
+
}
|
|
1706
|
+
function nowMs() {
|
|
1707
|
+
return Date.now();
|
|
1708
|
+
}
|
|
1709
|
+
function utils_compactRecord(input) {
|
|
1710
|
+
return Object.fromEntries(Object.entries(input).filter(([, value])=>null != value && '' !== value));
|
|
1711
|
+
}
|
|
1712
|
+
function getRequestKey(event, fallback) {
|
|
1713
|
+
return String(event.requestId ?? event.providerRequestId ?? event.messageId ?? event.turnId ?? event.turnIndex ?? event.id ?? fallback);
|
|
1714
|
+
}
|
|
1715
|
+
function getToolCallId(event) {
|
|
1716
|
+
const id = event.toolCallId ?? event.id ?? event.callId ?? event.tool_use_id ?? event.toolUseId;
|
|
1717
|
+
return null == id ? void 0 : String(id);
|
|
1718
|
+
}
|
|
1719
|
+
function getToolName(event) {
|
|
1720
|
+
return String(event.toolName ?? event.name ?? event.tool ?? event.functionName ?? (utils_isRecord(event.call) ? event.call.name : void 0) ?? 'tool');
|
|
1721
|
+
}
|
|
1722
|
+
function getToolInput(event) {
|
|
1723
|
+
return event.input ?? event.args ?? event.arguments ?? event.params ?? (utils_isRecord(event.call) ? event.call.input : void 0) ?? event;
|
|
1724
|
+
}
|
|
1725
|
+
function getProviderPayload(event) {
|
|
1726
|
+
return event.request ?? event.payload ?? event.body ?? event.providerPayload ?? event.messages ?? event;
|
|
1727
|
+
}
|
|
1728
|
+
function getMessageFromEvent(event) {
|
|
1729
|
+
if (utils_isRecord(event.message)) return event.message;
|
|
1730
|
+
if (event.role || event.content) return event;
|
|
1731
|
+
}
|
|
1732
|
+
function extractTextContent(content) {
|
|
1733
|
+
if ('string' == typeof content) return content;
|
|
1734
|
+
if (!Array.isArray(content)) return;
|
|
1735
|
+
const text = content.map((item)=>{
|
|
1736
|
+
if (!utils_isRecord(item)) return '';
|
|
1737
|
+
return 'text' === item.type && 'string' == typeof item.text ? item.text : '';
|
|
1738
|
+
}).filter(Boolean).join('\n');
|
|
1739
|
+
return text || void 0;
|
|
1740
|
+
}
|
|
1741
|
+
function extractAssistantOutput(message) {
|
|
1742
|
+
if (!utils_isRecord(message)) return;
|
|
1743
|
+
const text = extractTextContent(message.content);
|
|
1744
|
+
if (text) return text;
|
|
1745
|
+
const toolCalls = extractToolCalls(message);
|
|
1746
|
+
if (toolCalls.length > 0) return {
|
|
1747
|
+
toolCalls
|
|
1748
|
+
};
|
|
1749
|
+
return message.content ?? message;
|
|
1750
|
+
}
|
|
1751
|
+
function toolCallFromRecord(record) {
|
|
1752
|
+
const id = record.id ?? record.toolCallId ?? record.callId ?? record.tool_use_id ?? record.toolUseId;
|
|
1753
|
+
if (null == id) return;
|
|
1754
|
+
return {
|
|
1755
|
+
id: String(id),
|
|
1756
|
+
name: stringValue(record.name) ?? stringValue(record.toolName) ?? stringValue(record.tool),
|
|
1757
|
+
arguments: record.arguments ?? record.args ?? record.input ?? record.params
|
|
1758
|
+
};
|
|
1759
|
+
}
|
|
1760
|
+
function extractToolCalls(messageOrEvent) {
|
|
1761
|
+
if (!utils_isRecord(messageOrEvent)) return [];
|
|
1762
|
+
const direct = messageOrEvent.toolCalls ?? messageOrEvent.tool_calls ?? messageOrEvent.function_calls;
|
|
1763
|
+
if (Array.isArray(direct)) return direct.map((item)=>{
|
|
1764
|
+
if (!utils_isRecord(item)) return;
|
|
1765
|
+
const fromFunction = utils_isRecord(item.function) ? {
|
|
1766
|
+
...item,
|
|
1767
|
+
name: item.function.name ?? item.name,
|
|
1768
|
+
arguments: item.function.arguments ?? item.arguments
|
|
1769
|
+
} : item;
|
|
1770
|
+
return toolCallFromRecord(fromFunction);
|
|
1771
|
+
}).filter((item)=>Boolean(item));
|
|
1772
|
+
if (!Array.isArray(messageOrEvent.content)) return [];
|
|
1773
|
+
return messageOrEvent.content.map((item)=>{
|
|
1774
|
+
if (!utils_isRecord(item)) return;
|
|
1775
|
+
const type = String(item.type ?? '');
|
|
1776
|
+
if (![
|
|
1777
|
+
'tool_use',
|
|
1778
|
+
'tool_call',
|
|
1779
|
+
'toolCall'
|
|
1780
|
+
].includes(type)) return;
|
|
1781
|
+
return toolCallFromRecord({
|
|
1782
|
+
...item,
|
|
1783
|
+
id: item.id ?? item.tool_use_id,
|
|
1784
|
+
name: item.name ?? item.tool,
|
|
1785
|
+
arguments: item.input ?? item.arguments
|
|
1786
|
+
});
|
|
1787
|
+
}).filter((item)=>Boolean(item));
|
|
1788
|
+
}
|
|
1789
|
+
function normalizeMessage(event) {
|
|
1790
|
+
const message = getMessageFromEvent(event);
|
|
1791
|
+
if (!message) return;
|
|
1792
|
+
const toolCalls = extractToolCalls(message);
|
|
1793
|
+
return {
|
|
1794
|
+
id: stringValue(message.id) ?? stringValue(message.messageId),
|
|
1795
|
+
role: stringValue(message.role) ?? 'unknown',
|
|
1796
|
+
content: message.content,
|
|
1797
|
+
model: stringValue(message.model) ?? stringValue(event.model),
|
|
1798
|
+
provider: stringValue(message.provider) ?? stringValue(event.provider),
|
|
1799
|
+
createdAt: numericValue(message.createdAt) ?? numericValue(event.createdAt),
|
|
1800
|
+
endedAt: numericValue(message.endedAt) ?? numericValue(event.endedAt) ?? nowMs(),
|
|
1801
|
+
providerRequestId: stringValue(event.requestId) ?? stringValue(event.providerRequestId),
|
|
1802
|
+
toolCallId: stringValue(message.toolCallId) ?? stringValue(message.tool_call_id) ?? getToolCallId(event),
|
|
1803
|
+
toolName: stringValue(message.toolName) ?? stringValue(message.name) ?? stringValue(event.toolName),
|
|
1804
|
+
isError: Boolean(message.isError ?? message.error ?? event.isError ?? event.error) || void 0,
|
|
1805
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1808
|
+
function extractUsage(messageOrEvent) {
|
|
1809
|
+
const usage = messageOrEvent.usage ?? (utils_isRecord(messageOrEvent.message) ? messageOrEvent.message.usage : void 0);
|
|
1810
|
+
if (!utils_isRecord(usage)) return;
|
|
1811
|
+
const inputTokens = Number(usage.input ?? usage.inputTokens ?? usage.prompt_tokens ?? usage.promptTokens ?? 0);
|
|
1812
|
+
const outputTokens = Number(usage.output ?? usage.outputTokens ?? usage.completion_tokens ?? usage.completionTokens ?? 0);
|
|
1813
|
+
const cacheReadInputTokens = Number(usage.cacheRead ?? usage.cache_read ?? usage.cachedTokens ?? 0);
|
|
1814
|
+
const cacheCreationInputTokens = Number(usage.cacheWrite ?? usage.cache_write ?? 0);
|
|
1815
|
+
const reasoningOutputTokens = Number(usage.reasoning ?? usage.reasoningTokens ?? usage.reasoning_output_tokens ?? 0);
|
|
1816
|
+
const totalTokens = Number(usage.total ?? usage.totalTokens ?? usage.total_tokens ?? inputTokens + outputTokens);
|
|
1817
|
+
return utils_compactRecord({
|
|
1818
|
+
inputTokens: inputTokens || void 0,
|
|
1819
|
+
outputTokens: outputTokens || void 0,
|
|
1820
|
+
cacheReadInputTokens: cacheReadInputTokens || void 0,
|
|
1821
|
+
cacheCreationInputTokens: cacheCreationInputTokens || void 0,
|
|
1822
|
+
reasoningOutputTokens: reasoningOutputTokens || void 0,
|
|
1823
|
+
totalTokens: totalTokens || void 0
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1826
|
+
function extractResponseMetadata(event) {
|
|
1827
|
+
return utils_compactRecord({
|
|
1828
|
+
status: event.status ?? event.statusCode ?? event.httpStatus,
|
|
1829
|
+
headers: event.headers,
|
|
1830
|
+
responseHeaders: event.responseHeaders,
|
|
1831
|
+
providerMetadata: event.providerMetadata ?? event.metadata,
|
|
1832
|
+
requestId: event.requestId ?? event.providerRequestId
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
function extractToolResult(event) {
|
|
1836
|
+
return event.output ?? event.result ?? event.error ?? event.content ?? event;
|
|
1837
|
+
}
|
|
1838
|
+
function sessionIdFromContext(ctx) {
|
|
1839
|
+
if (!utils_isRecord(ctx)) return;
|
|
1840
|
+
try {
|
|
1841
|
+
const sessionManager = ctx.sessionManager;
|
|
1842
|
+
if (!utils_isRecord(sessionManager) || 'function' != typeof sessionManager.getSessionFile) return;
|
|
1843
|
+
const sessionFile = sessionManager.getSessionFile();
|
|
1844
|
+
if ('string' != typeof sessionFile || !sessionFile) return;
|
|
1845
|
+
const base = sessionFile.split(/[\\/]/).pop() ?? sessionFile;
|
|
1846
|
+
return base.replace(/\.jsonl$/i, '');
|
|
1847
|
+
} catch {
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
function eventSessionId(event, ctx) {
|
|
1852
|
+
return stringValue(event.sessionId) ?? stringValue(event.sessionID) ?? (utils_isRecord(event.session) ? stringValue(event.session.id) : void 0) ?? sessionIdFromContext(ctx) ?? 'pi-session';
|
|
1853
|
+
}
|
|
1854
|
+
function eventCwd(event) {
|
|
1855
|
+
const systemPromptOptions = utils_isRecord(event.systemPromptOptions) ? event.systemPromptOptions : void 0;
|
|
1856
|
+
return stringValue(systemPromptOptions?.cwd) ?? stringValue(event.cwd);
|
|
1857
|
+
}
|
|
1858
|
+
function selectedModel(event, ctx) {
|
|
1859
|
+
const eventModel = utils_isRecord(event.model) ? event.model : void 0;
|
|
1860
|
+
const contextModel = utils_isRecord(ctx) && utils_isRecord(ctx.model) ? ctx.model : void 0;
|
|
1861
|
+
return {
|
|
1862
|
+
model: stringValue(eventModel?.id) ?? stringValue(event.model) ?? stringValue(contextModel?.id),
|
|
1863
|
+
provider: stringValue(eventModel?.provider) ?? stringValue(event.provider) ?? stringValue(contextModel?.provider)
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
function eventTurnId(event, session) {
|
|
1867
|
+
return String(event.turnId ?? event.turnIndex ?? event.messageId ?? event.id ?? `turn-${++session.turnSeq}`);
|
|
1868
|
+
}
|
|
1869
|
+
function requestLookupKey(event) {
|
|
1870
|
+
const key = event.requestId ?? event.providerRequestId ?? event.messageId ?? event.turnId ?? event.turnIndex ?? event.id;
|
|
1871
|
+
return null == key ? void 0 : String(key);
|
|
1872
|
+
}
|
|
1873
|
+
function createSession(sessionId) {
|
|
1874
|
+
return {
|
|
1875
|
+
sessionId,
|
|
1876
|
+
turnSeq: 0,
|
|
1877
|
+
requestSeq: 0,
|
|
1878
|
+
toolSeq: 0,
|
|
1879
|
+
turns: new Map(),
|
|
1880
|
+
pendingExports: []
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
function createTurn(session, turnId, startedAt = nowMs()) {
|
|
1884
|
+
return {
|
|
1885
|
+
turnId,
|
|
1886
|
+
sessionId: session.sessionId,
|
|
1887
|
+
startedAt,
|
|
1888
|
+
model: session.currentModel,
|
|
1889
|
+
provider: session.currentProvider,
|
|
1890
|
+
providerRequests: new Map(),
|
|
1891
|
+
providerOrder: [],
|
|
1892
|
+
toolCalls: new Map(),
|
|
1893
|
+
toolOrder: [],
|
|
1894
|
+
toolToProviderRequest: new Map(),
|
|
1895
|
+
assistantMessages: [],
|
|
1896
|
+
messages: [],
|
|
1897
|
+
exportState: 'open'
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1900
|
+
class PiTlsTracer {
|
|
1901
|
+
config;
|
|
1902
|
+
sessions = new Map();
|
|
1903
|
+
constructor(config){
|
|
1904
|
+
this.config = config;
|
|
1905
|
+
}
|
|
1906
|
+
getSession(sessionId) {
|
|
1907
|
+
const current = this.sessions.get(sessionId);
|
|
1908
|
+
if (current) return current;
|
|
1909
|
+
const created = createSession(sessionId);
|
|
1910
|
+
this.sessions.set(sessionId, created);
|
|
1911
|
+
return created;
|
|
1912
|
+
}
|
|
1913
|
+
getActiveTurn(session) {
|
|
1914
|
+
return session.activeTurnId ? session.turns.get(session.activeTurnId) : void 0;
|
|
1915
|
+
}
|
|
1916
|
+
syncSessionModel(session, event, ctx) {
|
|
1917
|
+
const selected = selectedModel(event, ctx);
|
|
1918
|
+
session.currentModel = selected.model ?? session.currentModel;
|
|
1919
|
+
session.currentProvider = selected.provider ?? session.currentProvider;
|
|
1920
|
+
}
|
|
1921
|
+
getOrCreateActiveTurn(event, ctx) {
|
|
1922
|
+
const session = this.getSession(eventSessionId(event, ctx));
|
|
1923
|
+
this.syncSessionModel(session, event, ctx);
|
|
1924
|
+
let turn = this.getActiveTurn(session);
|
|
1925
|
+
if (!turn || 'open' !== turn.exportState) {
|
|
1926
|
+
const turnId = eventTurnId(event, session);
|
|
1927
|
+
turn = session.turns.get(turnId) ?? createTurn(session, turnId);
|
|
1928
|
+
session.turns.set(turn.turnId, turn);
|
|
1929
|
+
session.activeTurnId = turn.turnId;
|
|
1930
|
+
}
|
|
1931
|
+
turn.model = turn.model ?? session.currentModel;
|
|
1932
|
+
turn.provider = turn.provider ?? session.currentProvider;
|
|
1933
|
+
return {
|
|
1934
|
+
session,
|
|
1935
|
+
turn
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1938
|
+
findRequest(turn, event) {
|
|
1939
|
+
const key = requestLookupKey(event);
|
|
1940
|
+
if (key && turn.providerRequests.has(key)) return turn.providerRequests.get(key);
|
|
1941
|
+
if (turn.activeProviderRequestId) return turn.providerRequests.get(turn.activeProviderRequestId);
|
|
1942
|
+
const lastKey = turn.providerOrder[turn.providerOrder.length - 1];
|
|
1943
|
+
return lastKey ? turn.providerRequests.get(lastKey) : void 0;
|
|
1944
|
+
}
|
|
1945
|
+
bindToolCallToRequest(turn, toolCallId, requestId) {
|
|
1946
|
+
turn.toolToProviderRequest.set(toolCallId, requestId);
|
|
1947
|
+
const tool = turn.toolCalls.get(toolCallId);
|
|
1948
|
+
if (tool) tool.parentProviderRequestId = requestId;
|
|
1949
|
+
const request = turn.providerRequests.get(requestId);
|
|
1950
|
+
if (request && !request.toolCallIds.includes(toolCallId)) request.toolCallIds.push(toolCallId);
|
|
1951
|
+
}
|
|
1952
|
+
getOrCreateTool(turn, event) {
|
|
1953
|
+
const explicitId = getToolCallId(event);
|
|
1954
|
+
const toolCallId = explicitId ?? `tool-${turn.toolOrder.length + 1}`;
|
|
1955
|
+
const current = turn.toolCalls.get(toolCallId);
|
|
1956
|
+
if (current) return current;
|
|
1957
|
+
const created = {
|
|
1958
|
+
toolCallId,
|
|
1959
|
+
name: getToolName(event),
|
|
1960
|
+
parentProviderRequestId: turn.toolToProviderRequest.get(toolCallId)
|
|
1961
|
+
};
|
|
1962
|
+
turn.toolCalls.set(toolCallId, created);
|
|
1963
|
+
turn.toolOrder.push(toolCallId);
|
|
1964
|
+
return created;
|
|
1965
|
+
}
|
|
1966
|
+
snapshot(session, turn) {
|
|
1967
|
+
return {
|
|
1968
|
+
source: 'pi',
|
|
1969
|
+
session: {
|
|
1970
|
+
id: session.sessionId,
|
|
1971
|
+
cwd: turn.cwd ?? session.cwd,
|
|
1972
|
+
version: session.agentVersion
|
|
1973
|
+
},
|
|
1974
|
+
turnId: turn.turnId,
|
|
1975
|
+
startedAt: turn.startedAt,
|
|
1976
|
+
endedAt: turn.endedAt ?? nowMs(),
|
|
1977
|
+
prompt: turn.prompt,
|
|
1978
|
+
images: turn.images,
|
|
1979
|
+
context: turn.context,
|
|
1980
|
+
model: turn.model ?? session.currentModel,
|
|
1981
|
+
provider: turn.provider ?? session.currentProvider,
|
|
1982
|
+
messages: turn.messages,
|
|
1983
|
+
providerRequests: turn.providerOrder.map((key)=>turn.providerRequests.get(key)).filter((request)=>Boolean(request)),
|
|
1984
|
+
toolCalls: turn.toolOrder.map((key)=>turn.toolCalls.get(key)).filter((tool)=>Boolean(tool)),
|
|
1985
|
+
captureContent: this.config.captureContent,
|
|
1986
|
+
pluginVersion: this.config.pluginVersion
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
trackPendingExport(session, task) {
|
|
1990
|
+
session.pendingExports.push(task);
|
|
1991
|
+
task.finally(()=>{
|
|
1992
|
+
session.pendingExports = session.pendingExports.filter((pending)=>pending !== task);
|
|
1993
|
+
});
|
|
1994
|
+
}
|
|
1995
|
+
exportTurn(session, turn) {
|
|
1996
|
+
if ('ready' !== turn.exportState) return;
|
|
1997
|
+
turn.exportState = 'exporting';
|
|
1998
|
+
const snapshot = this.snapshot(session, turn);
|
|
1999
|
+
const task = runTracePipeline(snapshot, piAdapter, {
|
|
2000
|
+
env: this.config.env,
|
|
2001
|
+
dataRoot: this.config.dataRoot,
|
|
2002
|
+
sourceName: 'pi',
|
|
2003
|
+
source: {
|
|
2004
|
+
tool: 'pi',
|
|
2005
|
+
sessionId: snapshot.session.id,
|
|
2006
|
+
turnId: snapshot.turnId,
|
|
2007
|
+
threadId: snapshot.session.id
|
|
2008
|
+
},
|
|
2009
|
+
otel: {
|
|
2010
|
+
serviceName: this.config.serviceName,
|
|
2011
|
+
sdkVersion: this.config.pluginVersion,
|
|
2012
|
+
scopeName: 'tls-observer-pi',
|
|
2013
|
+
scopeVersion: this.config.pluginVersion,
|
|
2014
|
+
resourceAttributes: {
|
|
2015
|
+
'service.instance.id': this.config.serviceInstanceId,
|
|
2016
|
+
'tls.app.type': 'pi'
|
|
2017
|
+
}
|
|
2018
|
+
},
|
|
2019
|
+
failedTraceCache: {
|
|
2020
|
+
maxRetryCount: 1
|
|
2021
|
+
}
|
|
2022
|
+
}).then(()=>{
|
|
2023
|
+
turn.exportState = 'exported';
|
|
2024
|
+
}).catch(()=>{
|
|
2025
|
+
turn.exportState = 'ready';
|
|
2026
|
+
});
|
|
2027
|
+
this.trackPendingExport(session, task);
|
|
2028
|
+
}
|
|
2029
|
+
handleSessionStart(event, ctx) {
|
|
2030
|
+
const session = this.getSession(eventSessionId(event, ctx));
|
|
2031
|
+
this.syncSessionModel(session, event, ctx);
|
|
2032
|
+
session.activeTurnId = void 0;
|
|
2033
|
+
}
|
|
2034
|
+
handleModelSelect(event, ctx) {
|
|
2035
|
+
const session = this.getSession(eventSessionId(event, ctx));
|
|
2036
|
+
this.syncSessionModel(session, event, ctx);
|
|
2037
|
+
}
|
|
2038
|
+
handleAgentStart(event, ctx) {
|
|
2039
|
+
const session = this.getSession(eventSessionId(event, ctx));
|
|
2040
|
+
this.syncSessionModel(session, event, ctx);
|
|
2041
|
+
const selected = selectedModel(event, ctx);
|
|
2042
|
+
const turnId = eventTurnId(event, session);
|
|
2043
|
+
const turn = session.turns.get(turnId) ?? createTurn(session, turnId);
|
|
2044
|
+
turn.startedAt = turn.startedAt || nowMs();
|
|
2045
|
+
turn.cwd = eventCwd(event) ?? turn.cwd;
|
|
2046
|
+
session.cwd = turn.cwd ?? session.cwd;
|
|
2047
|
+
turn.prompt = event.prompt ?? turn.prompt;
|
|
2048
|
+
turn.images = event.images ?? turn.images;
|
|
2049
|
+
turn.context = event.context ?? event.attachments ?? turn.context;
|
|
2050
|
+
turn.model = selected.model ?? turn.model ?? session.currentModel;
|
|
2051
|
+
turn.provider = selected.provider ?? turn.provider ?? session.currentProvider;
|
|
2052
|
+
turn.exportState = 'open';
|
|
2053
|
+
session.turns.set(turn.turnId, turn);
|
|
2054
|
+
session.activeTurnId = turn.turnId;
|
|
2055
|
+
}
|
|
2056
|
+
handleTurnStart(event, ctx) {
|
|
2057
|
+
this.getOrCreateActiveTurn(event, ctx);
|
|
2058
|
+
}
|
|
2059
|
+
handleProviderRequest(event, ctx) {
|
|
2060
|
+
const { session, turn } = this.getOrCreateActiveTurn(event, ctx);
|
|
2061
|
+
const selected = selectedModel(event, ctx);
|
|
2062
|
+
turn.model = selected.model ?? turn.model ?? session.currentModel;
|
|
2063
|
+
turn.provider = selected.provider ?? turn.provider ?? session.currentProvider;
|
|
2064
|
+
const requestId = getRequestKey(event, `request-${++session.requestSeq}`);
|
|
2065
|
+
const request = {
|
|
2066
|
+
requestId,
|
|
2067
|
+
startedAt: nowMs(),
|
|
2068
|
+
model: selected.model ?? turn.model ?? session.currentModel,
|
|
2069
|
+
provider: selected.provider ?? turn.provider ?? session.currentProvider,
|
|
2070
|
+
input: getProviderPayload(event),
|
|
2071
|
+
metadata: utils_compactRecord({
|
|
2072
|
+
url: event.url,
|
|
2073
|
+
method: event.method
|
|
2074
|
+
}),
|
|
2075
|
+
status: 'ok',
|
|
2076
|
+
toolCallIds: []
|
|
2077
|
+
};
|
|
2078
|
+
turn.providerRequests.set(requestId, request);
|
|
2079
|
+
if (!turn.providerOrder.includes(requestId)) turn.providerOrder.push(requestId);
|
|
2080
|
+
turn.activeProviderRequestId = requestId;
|
|
2081
|
+
}
|
|
2082
|
+
handleProviderResponse(event, ctx) {
|
|
2083
|
+
const { turn } = this.getOrCreateActiveTurn(event, ctx);
|
|
2084
|
+
const request = this.findRequest(turn, event);
|
|
2085
|
+
if (!request) return;
|
|
2086
|
+
request.metadata = {
|
|
2087
|
+
...request.metadata ?? {},
|
|
2088
|
+
...extractResponseMetadata(event)
|
|
2089
|
+
};
|
|
2090
|
+
request.usage = extractUsage(event) ?? request.usage;
|
|
2091
|
+
request.endedAt = request.endedAt ?? nowMs();
|
|
2092
|
+
if (event.error || event.isError || Number(event.status ?? 0) >= 400) {
|
|
2093
|
+
request.status = 'error';
|
|
2094
|
+
request.error = event.error ?? event.status;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
handleMessageUpdate(event, ctx) {
|
|
2098
|
+
const { turn } = this.getOrCreateActiveTurn(event, ctx);
|
|
2099
|
+
const request = this.findRequest(turn, event);
|
|
2100
|
+
if (request && !request.completionStartAt) request.completionStartAt = nowMs();
|
|
2101
|
+
const message = normalizeMessage(event);
|
|
2102
|
+
if (message?.role === 'assistant') turn.latestAssistantOutput = extractAssistantOutput(message);
|
|
2103
|
+
}
|
|
2104
|
+
handleMessageEnd(event, ctx) {
|
|
2105
|
+
const { turn } = this.getOrCreateActiveTurn(event, ctx);
|
|
2106
|
+
const request = this.findRequest(turn, event);
|
|
2107
|
+
const message = normalizeMessage(event);
|
|
2108
|
+
if (!message) return;
|
|
2109
|
+
if (request) message.providerRequestId = request.requestId;
|
|
2110
|
+
turn.messages.push(message);
|
|
2111
|
+
if ('assistant' === message.role) turn.assistantMessages.push(message);
|
|
2112
|
+
if (request && 'assistant' === message.role) {
|
|
2113
|
+
request.output = extractAssistantOutput(message);
|
|
2114
|
+
request.endedAt = nowMs();
|
|
2115
|
+
request.usage = extractUsage(event) ?? request.usage;
|
|
2116
|
+
request.status = event.error || event.isError ? 'error' : request.status ?? 'ok';
|
|
2117
|
+
request.error = event.error ?? request.error;
|
|
2118
|
+
for (const toolCall of extractToolCalls(message)){
|
|
2119
|
+
this.bindToolCallToRequest(turn, toolCall.id, request.requestId);
|
|
2120
|
+
const tool = turn.toolCalls.get(toolCall.id) ?? {
|
|
2121
|
+
toolCallId: toolCall.id,
|
|
2122
|
+
name: toolCall.name ?? 'tool'
|
|
2123
|
+
};
|
|
2124
|
+
tool.parentProviderRequestId = request.requestId;
|
|
2125
|
+
tool.name = toolCall.name ?? tool.name;
|
|
2126
|
+
tool.arguments = toolCall.arguments ?? tool.arguments;
|
|
2127
|
+
turn.toolCalls.set(toolCall.id, tool);
|
|
2128
|
+
if (!turn.toolOrder.includes(toolCall.id)) turn.toolOrder.push(toolCall.id);
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
handleToolStart(event, ctx) {
|
|
2133
|
+
const { turn } = this.getOrCreateActiveTurn(event, ctx);
|
|
2134
|
+
const tool = this.getOrCreateTool(turn, event);
|
|
2135
|
+
if (!tool) return;
|
|
2136
|
+
tool.name = getToolName(event);
|
|
2137
|
+
tool.arguments = getToolInput(event);
|
|
2138
|
+
tool.startedAt = tool.startedAt ?? nowMs();
|
|
2139
|
+
const parentRequestId = turn.toolToProviderRequest.get(tool.toolCallId) ?? stringValue(event.providerRequestId);
|
|
2140
|
+
if (parentRequestId) tool.parentProviderRequestId = parentRequestId;
|
|
2141
|
+
}
|
|
2142
|
+
handleToolEnd(event, ctx) {
|
|
2143
|
+
const { turn } = this.getOrCreateActiveTurn(event, ctx);
|
|
2144
|
+
const tool = this.getOrCreateTool(turn, event);
|
|
2145
|
+
if (!tool) return;
|
|
2146
|
+
const isError = Boolean(event.isError ?? event.error ?? 'error' === event.status);
|
|
2147
|
+
tool.result = extractToolResult(event);
|
|
2148
|
+
tool.error = event.error;
|
|
2149
|
+
tool.status = isError ? 'error' : 'ok';
|
|
2150
|
+
tool.endedAt = nowMs();
|
|
2151
|
+
if (!tool.parentProviderRequestId) tool.parentProviderRequestId = turn.toolToProviderRequest.get(tool.toolCallId);
|
|
2152
|
+
}
|
|
2153
|
+
handleTurnEnd(event, ctx) {
|
|
2154
|
+
const { turn } = this.getOrCreateActiveTurn(event, ctx);
|
|
2155
|
+
turn.endedAt = nowMs();
|
|
2156
|
+
}
|
|
2157
|
+
handleAgentEnd(event, ctx) {
|
|
2158
|
+
const session = this.getSession(eventSessionId(event, ctx));
|
|
2159
|
+
const turn = this.getActiveTurn(session);
|
|
2160
|
+
if (!turn) return;
|
|
2161
|
+
const message = normalizeMessage(event);
|
|
2162
|
+
if (message) {
|
|
2163
|
+
if ('assistant' === message.role) {
|
|
2164
|
+
message.providerRequestId = message.providerRequestId ?? turn.activeProviderRequestId;
|
|
2165
|
+
turn.assistantMessages.push(message);
|
|
2166
|
+
}
|
|
2167
|
+
turn.messages.push(message);
|
|
2168
|
+
}
|
|
2169
|
+
const finalOutput = message ? extractAssistantOutput(message) : turn.latestAssistantOutput;
|
|
2170
|
+
const activeRequest = turn.activeProviderRequestId ? turn.providerRequests.get(turn.activeProviderRequestId) : void 0;
|
|
2171
|
+
if (activeRequest && void 0 !== finalOutput && void 0 === activeRequest.output) {
|
|
2172
|
+
activeRequest.output = finalOutput;
|
|
2173
|
+
activeRequest.endedAt = activeRequest.endedAt ?? nowMs();
|
|
2174
|
+
}
|
|
2175
|
+
turn.endedAt = nowMs();
|
|
2176
|
+
turn.exportState = 'ready';
|
|
2177
|
+
this.exportTurn(session, turn);
|
|
2178
|
+
session.activeTurnId = void 0;
|
|
2179
|
+
}
|
|
2180
|
+
async handleSessionShutdown(event, ctx) {
|
|
2181
|
+
const session = this.getSession(eventSessionId(event, ctx));
|
|
2182
|
+
const turn = this.getActiveTurn(session);
|
|
2183
|
+
if (turn && 'open' === turn.exportState) {
|
|
2184
|
+
turn.endedAt = nowMs();
|
|
2185
|
+
turn.exportState = 'ready';
|
|
2186
|
+
this.exportTurn(session, turn);
|
|
2187
|
+
session.activeTurnId = void 0;
|
|
2188
|
+
}
|
|
2189
|
+
await this.flush(session.sessionId);
|
|
2190
|
+
}
|
|
2191
|
+
async flush(sessionID) {
|
|
2192
|
+
const sessions = sessionID ? [
|
|
2193
|
+
this.getSession(sessionID)
|
|
2194
|
+
] : [
|
|
2195
|
+
...this.sessions.values()
|
|
2196
|
+
];
|
|
2197
|
+
await Promise.allSettled(sessions.flatMap((session)=>session.pendingExports));
|
|
2198
|
+
}
|
|
2199
|
+
get observedSessionCount() {
|
|
2200
|
+
return this.sessions.size;
|
|
2201
|
+
}
|
|
2202
|
+
__getSessionForTest(sessionId) {
|
|
2203
|
+
return this.sessions.get(sessionId);
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
const TlsObserverPiPlugin = async (pi)=>{
|
|
2207
|
+
const config = loadConfig();
|
|
2208
|
+
if (!config.enabled) return;
|
|
2209
|
+
const tracer = new PiTlsTracer(config);
|
|
2210
|
+
pi.on('session_start', (event, ctx)=>{
|
|
2211
|
+
tracer.handleSessionStart(event, ctx);
|
|
2212
|
+
});
|
|
2213
|
+
pi.on('model_select', (event, ctx)=>{
|
|
2214
|
+
tracer.handleModelSelect(event, ctx);
|
|
2215
|
+
});
|
|
2216
|
+
pi.on('before_agent_start', (event, ctx)=>{
|
|
2217
|
+
tracer.handleAgentStart(event, ctx);
|
|
2218
|
+
});
|
|
2219
|
+
pi.on('agent_start', (event, ctx)=>{
|
|
2220
|
+
tracer.handleAgentStart(event, ctx);
|
|
2221
|
+
});
|
|
2222
|
+
pi.on('turn_start', (event, ctx)=>{
|
|
2223
|
+
tracer.handleTurnStart(event, ctx);
|
|
2224
|
+
});
|
|
2225
|
+
pi.on('before_provider_request', (event, ctx)=>{
|
|
2226
|
+
tracer.handleProviderRequest(event, ctx);
|
|
2227
|
+
});
|
|
2228
|
+
pi.on('after_provider_response', (event, ctx)=>{
|
|
2229
|
+
tracer.handleProviderResponse(event, ctx);
|
|
2230
|
+
});
|
|
2231
|
+
pi.on('message_update', (event, ctx)=>{
|
|
2232
|
+
tracer.handleMessageUpdate(event, ctx);
|
|
2233
|
+
});
|
|
2234
|
+
pi.on('message_end', (event, ctx)=>{
|
|
2235
|
+
tracer.handleMessageEnd(event, ctx);
|
|
2236
|
+
});
|
|
2237
|
+
pi.on('tool_execution_start', (event, ctx)=>{
|
|
2238
|
+
tracer.handleToolStart(event, ctx);
|
|
2239
|
+
});
|
|
2240
|
+
pi.on('tool_call', (event, ctx)=>{
|
|
2241
|
+
tracer.handleToolStart(event, ctx);
|
|
2242
|
+
});
|
|
2243
|
+
pi.on('tool_result', (event, ctx)=>{
|
|
2244
|
+
tracer.handleToolEnd(event, ctx);
|
|
2245
|
+
});
|
|
2246
|
+
pi.on('tool_execution_end', (event, ctx)=>{
|
|
2247
|
+
tracer.handleToolEnd(event, ctx);
|
|
2248
|
+
});
|
|
2249
|
+
pi.on('turn_end', (event, ctx)=>{
|
|
2250
|
+
tracer.handleTurnEnd(event, ctx);
|
|
2251
|
+
});
|
|
2252
|
+
pi.on('agent_end', (event, ctx)=>{
|
|
2253
|
+
tracer.handleAgentEnd(event, ctx);
|
|
2254
|
+
});
|
|
2255
|
+
pi.on('session_shutdown', async (event, ctx)=>{
|
|
2256
|
+
await tracer.handleSessionShutdown(event, ctx);
|
|
2257
|
+
});
|
|
2258
|
+
};
|
|
2259
|
+
const src = TlsObserverPiPlugin;
|
|
2260
|
+
export default src;
|
|
2261
|
+
export { PiTlsTracer, TlsObserverPiPlugin, loadConfig };
|