@volcengine/tls-observer-cursor 0.0.4

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/dist/index.js ADDED
@@ -0,0 +1,2097 @@
1
+ import { homedir, hostname, networkInterfaces, userInfo } from "node:os";
2
+ import { tlsOpenapi } from "@volcengine/openapi";
3
+ import { createHash as external_node_crypto_createHash, randomUUID } from "node:crypto";
4
+ import * as __rspack_external_node_fs_promises_153e37e0 from "node:fs/promises";
5
+ import * as __rspack_external_node_fs_5ea92f0c from "node:fs";
6
+ import * as __rspack_external_node_path_c5b9b54f from "node:path";
7
+ function firstEnv(...names) {
8
+ for (const name of names){
9
+ const value = process.env[name];
10
+ if ('string' == typeof value && '' !== value.trim()) return value.trim();
11
+ }
12
+ }
13
+ function envFlag(name, defaultValue = false) {
14
+ const value = process.env[name];
15
+ if (void 0 === value || '' === value) return defaultValue;
16
+ return /^(1|true|yes|y|on)$/i.test(value);
17
+ }
18
+ function envDisabled(name) {
19
+ return /^(0|false|no|n|off)$/i.test(process.env[name] ?? '');
20
+ }
21
+ function envPositiveInteger(name, fallback) {
22
+ const value = Number.parseInt(process.env[name] ?? '', 10);
23
+ return Number.isFinite(value) && value > 0 ? value : fallback;
24
+ }
25
+ function defaultDataRoot() {
26
+ return __rspack_external_node_path_c5b9b54f.join(homedir(), '.cursor', 'plugins', 'data', 'tls-observer-cursor');
27
+ }
28
+ function defaultEnvFile() {
29
+ return __rspack_external_node_path_c5b9b54f.join(homedir(), '.cursor', 'tls-observer-cursor.env');
30
+ }
31
+ function parseEnvValue(raw) {
32
+ const value = raw.trim();
33
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1).replace(/\\n/g, '\n').replace(/\\"/g, '"').replace(/\\'/g, "'");
34
+ return value;
35
+ }
36
+ function loadEnvFile(filePath) {
37
+ const target = filePath || firstEnv('CURSOR_TLS_ENV_FILE') || defaultEnvFile();
38
+ let text;
39
+ try {
40
+ text = __rspack_external_node_fs_5ea92f0c.readFileSync(target, 'utf8');
41
+ } catch {
42
+ return false;
43
+ }
44
+ for (const rawLine of text.split(/\r?\n/)){
45
+ const line = rawLine.trim();
46
+ if (!line || line.startsWith('#')) continue;
47
+ const normalized = line.startsWith('export ') ? line.slice(7).trim() : line;
48
+ const match = normalized.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
49
+ if (!match) continue;
50
+ const [, key, rawValue] = match;
51
+ process.env[key] ??= parseEnvValue(rawValue);
52
+ }
53
+ return true;
54
+ }
55
+ function loadConfig() {
56
+ loadEnvFile();
57
+ const dataRoot = firstEnv('CURSOR_TLS_DATA_ROOT') || defaultDataRoot();
58
+ const serviceName = firstEnv('CURSOR_OTEL_SERVICE_NAME') || "cursor-transcript-hook";
59
+ const exportRetries = Math.min(envPositiveInteger('CURSOR_TLS_EXPORT_TIMEOUT_RETRIES', 1), 3);
60
+ return {
61
+ enabled: !envDisabled('CURSOR_TLS_EXPORT'),
62
+ dataRoot,
63
+ stateFilePath: firstEnv('CURSOR_TLS_STATE_FILE') || __rspack_external_node_path_c5b9b54f.join(dataRoot, 'state', 'cursor-state.json'),
64
+ sendLogsDir: __rspack_external_node_path_c5b9b54f.join(dataRoot, 'logs', 'send'),
65
+ artifactsDir: __rspack_external_node_path_c5b9b54f.join(dataRoot, 'artifacts'),
66
+ diagnosticLogMaxBytes: envPositiveInteger('CURSOR_TLS_DIAGNOSTIC_LOG_MAX_BYTES', 5242880),
67
+ captureContent: !envDisabled('CURSOR_TLS_CAPTURE_CONTENT'),
68
+ debugArtifacts: envFlag('CURSOR_TLS_DEBUG_ARTIFACTS', false),
69
+ serviceName,
70
+ serviceInstanceId: `${serviceName}@${hostname()}:${process.pid}`,
71
+ tlsEndpoint: firstEnv('CURSOR_TLS_ENDPOINT', 'TRACE_TLS_ENDPOINT', 'TLS_ENDPOINT'),
72
+ otelEndpoint: firstEnv('CURSOR_TLS_OTEL_ENDPOINT', 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT'),
73
+ region: firstEnv('CURSOR_TLS_REGION', 'TLS_REGION'),
74
+ traceTopicId: firstEnv('CURSOR_TLS_TRACE_TOPIC_ID', 'CURSOR_TLS_TOPIC_ID', 'TLS_TRACE_TOPIC_ID'),
75
+ apiKey: firstEnv('CURSOR_TLS_API_KEY', 'TLS_API_KEY'),
76
+ ak: firstEnv('CURSOR_TLS_AK', 'TLS_AK'),
77
+ sk: firstEnv('CURSOR_TLS_SK', 'TLS_SK'),
78
+ authMode: 'resource' === firstEnv('CURSOR_TLS_AUTH_MODE', 'TLS_AUTH_MODE') ? 'resource' : 'header',
79
+ exportTimeoutMs: envPositiveInteger('CURSOR_TLS_EXPORT_TIMEOUT_MS', 20000),
80
+ exportTimeoutRetries: exportRetries,
81
+ transcriptLookbehindBytes: envPositiveInteger('CURSOR_TLS_TRANSCRIPT_LOOKBEHIND_BYTES', 4194304)
82
+ };
83
+ }
84
+ function localIdentityMetadata() {
85
+ return {
86
+ 'user.name': userInfo().username
87
+ };
88
+ }
89
+ function json_safeJsonStringify(value, fallback = '{}') {
90
+ const seen = new WeakSet();
91
+ try {
92
+ return JSON.stringify(value, (_key, item)=>{
93
+ if (item && 'object' == typeof item) {
94
+ if (seen.has(item)) return '[Circular]';
95
+ seen.add(item);
96
+ }
97
+ return item;
98
+ }) ?? fallback;
99
+ } catch {
100
+ return fallback;
101
+ }
102
+ }
103
+ function compactObject(value) {
104
+ return Object.fromEntries(Object.entries(value).filter(([, item])=>null != item && '' !== item));
105
+ }
106
+ const TLS_ANONYMOUS_IDENTITY_HEADER = 'x-tls-anonymous-identity';
107
+ const NO_RETRY_STATUS_CODES = [
108
+ 400,
109
+ 401,
110
+ 403,
111
+ 404
112
+ ];
113
+ const producerEntries = new Map();
114
+ function firstExternalIpv4() {
115
+ for (const items of Object.values(networkInterfaces()))for (const item of items ?? [])if ('IPv4' === item.family && !item.internal && item.address) return item.address;
116
+ return '127.0.0.1';
117
+ }
118
+ function normalizeProducerEndpoint(endpoint) {
119
+ const raw = endpoint.trim();
120
+ if (!raw) return raw;
121
+ try {
122
+ const url = new URL(/^https?:\/\//.test(raw) ? raw : `https://${raw}`);
123
+ return url.hostname || raw;
124
+ } catch {
125
+ return raw.replace(/^https?:\/\//, '').replace(/\/.*$/, '').replace(/:\d+$/, '');
126
+ }
127
+ }
128
+ function producerKey(config) {
129
+ return JSON.stringify({
130
+ endpoint: normalizeProducerEndpoint(config.endpoint),
131
+ region: config.region,
132
+ apiKey: config.apiKey || '',
133
+ ak: config.ak || '',
134
+ sk: config.sk || ''
135
+ });
136
+ }
137
+ function timeoutError(label, timeoutMs) {
138
+ return new Error(`${label} timed out after ${timeoutMs}ms`);
139
+ }
140
+ function withTimeout(promise, timeoutMs, label) {
141
+ const effectiveTimeoutMs = Number.isFinite(timeoutMs) && Number(timeoutMs) > 0 ? Math.trunc(Number(timeoutMs)) : 20000;
142
+ return new Promise((resolve, reject)=>{
143
+ const timer = setTimeout(()=>reject(timeoutError(label, effectiveTimeoutMs)), effectiveTimeoutMs);
144
+ promise.then((value)=>{
145
+ clearTimeout(timer);
146
+ resolve(value);
147
+ }, (error)=>{
148
+ clearTimeout(timer);
149
+ reject(error);
150
+ });
151
+ });
152
+ }
153
+ function getProducer(config) {
154
+ const key = producerKey(config);
155
+ const existing = producerEntries.get(key);
156
+ if (existing) return existing;
157
+ const producerConfig = tlsOpenapi.Producer.getDefaultProducerConfig();
158
+ producerConfig.Endpoint = normalizeProducerEndpoint(config.endpoint);
159
+ producerConfig.Region = config.region;
160
+ producerConfig.NoRetryStatusCodeList = NO_RETRY_STATUS_CODES;
161
+ if (config.apiKey) {
162
+ producerConfig.AccessKeyID = '';
163
+ producerConfig.AccessKeySecret = '';
164
+ producerConfig.Headers = {
165
+ [TLS_ANONYMOUS_IDENTITY_HEADER]: config.apiKey
166
+ };
167
+ } else {
168
+ producerConfig.AccessKeyID = config.ak || '';
169
+ producerConfig.AccessKeySecret = config.sk || '';
170
+ }
171
+ const producer = new tlsOpenapi.Producer(producerConfig);
172
+ producer.Start();
173
+ const entry = {
174
+ producer,
175
+ source: firstExternalIpv4(),
176
+ timeoutMs: config.timeoutMs ?? 20000
177
+ };
178
+ producerEntries.set(key, entry);
179
+ return entry;
180
+ }
181
+ async function sendTlsLogRecords(config, records, fileName) {
182
+ if (0 === records.length) return;
183
+ const entry = getProducer(config);
184
+ const logGroup = {
185
+ Logs: records.map((record)=>({
186
+ Time: Date.now(),
187
+ Contents: Object.entries(record).map(([Key, Value])=>({
188
+ Key,
189
+ Value
190
+ }))
191
+ })),
192
+ Source: entry.source,
193
+ LogTags: [],
194
+ FileName: fileName
195
+ };
196
+ await withTimeout(new Promise((resolve, reject)=>{
197
+ let settled = false;
198
+ const settle = (fn, value)=>{
199
+ if (settled) return;
200
+ settled = true;
201
+ fn(value);
202
+ };
203
+ const resolveUnknown = resolve;
204
+ const sendPromise = entry.producer.SendLogs('', config.traceTopicId, entry.source, fileName, logGroup, {
205
+ Success: ()=>settle(resolveUnknown),
206
+ Fail: (result)=>{
207
+ const attempts = result?.Attempts ?? [];
208
+ const lastAttempt = attempts[attempts.length - 1] ?? {};
209
+ settle(reject, new Error(`TLS SendLogs failed topicId=${config.traceTopicId} requestId=${String(lastAttempt.RequestId ?? '')} errorCode=${String(lastAttempt.ErrorCode ?? '')} error=${JSON.stringify(lastAttempt)}`));
210
+ }
211
+ });
212
+ Promise.resolve(sendPromise).catch((error)=>settle(reject, error));
213
+ }), config.timeoutMs, 'TLS SendLogs');
214
+ }
215
+ async function closeTraceLogProducers() {
216
+ const entries = [
217
+ ...producerEntries.values()
218
+ ];
219
+ producerEntries.clear();
220
+ await Promise.allSettled(entries.map((entry)=>{
221
+ const closeTimeoutMs = Math.min(Math.max(entry.timeoutMs || 20000, 1000), 5000);
222
+ return withTimeout(Promise.resolve(entry.producer.Close()), closeTimeoutMs, 'TLS Producer Close');
223
+ }));
224
+ }
225
+ var package_namespaceObject = {
226
+ rE: "0.0.4"
227
+ };
228
+ function toOtlpAnyValue(value) {
229
+ if ('string' == typeof value) return {
230
+ stringValue: value
231
+ };
232
+ if ('boolean' == typeof value) return {
233
+ boolValue: value
234
+ };
235
+ if ('number' == typeof value) return Number.isInteger(value) ? {
236
+ intValue: String(value)
237
+ } : {
238
+ doubleValue: value
239
+ };
240
+ if (Array.isArray(value)) return {
241
+ arrayValue: {
242
+ values: value.map((item)=>toOtlpAnyValue(item))
243
+ }
244
+ };
245
+ if (value && 'object' == typeof value) return {
246
+ kvlistValue: {
247
+ values: Object.entries(value).filter(([, item])=>void 0 !== item).map(([key, item])=>({
248
+ key,
249
+ value: toOtlpAnyValue(item)
250
+ }))
251
+ }
252
+ };
253
+ return {
254
+ stringValue: String(value ?? '')
255
+ };
256
+ }
257
+ function toOtlpAttributes(attributes) {
258
+ return Object.entries(attributes).filter(([, value])=>null != value).map(([key, value])=>({
259
+ key,
260
+ value: toOtlpAnyValue(value)
261
+ }));
262
+ }
263
+ function otlpValueToJs(value) {
264
+ if (void 0 !== value.stringValue) return value.stringValue;
265
+ if (void 0 !== value.intValue) return Number(value.intValue);
266
+ if (void 0 !== value.doubleValue) return value.doubleValue;
267
+ if (void 0 !== value.boolValue) return value.boolValue;
268
+ if (value.arrayValue && 'object' == typeof value.arrayValue) {
269
+ const arr = value.arrayValue;
270
+ return (arr.values || []).map(otlpValueToJs);
271
+ }
272
+ if (value.kvlistValue && 'object' == typeof value.kvlistValue) {
273
+ const kv = value.kvlistValue;
274
+ const result = {};
275
+ for (const item of kv.values || [])result[item.key] = otlpValueToJs(item.value);
276
+ return result;
277
+ }
278
+ return value;
279
+ }
280
+ function otlpAttributesToMap(attributes) {
281
+ return attributes.reduce((acc, attr)=>{
282
+ acc[attr.key] = otlpValueToJs(attr.value);
283
+ return acc;
284
+ }, {});
285
+ }
286
+ function toUnixNanoString(ms) {
287
+ return String(BigInt(Math.trunc(Number.isFinite(ms) ? ms : Date.now())) * BigInt(1000000));
288
+ }
289
+ function spanKindToName(kind) {
290
+ const kindMap = {
291
+ 0: 'unspecified',
292
+ 1: 'internal',
293
+ 2: 'server',
294
+ 3: 'client',
295
+ 4: 'producer',
296
+ 5: 'consumer'
297
+ };
298
+ if ('number' == typeof kind) return kindMap[kind] || 'internal';
299
+ const upper = String(kind).toUpperCase();
300
+ if ('SERVER' === upper) return 'server';
301
+ if ('CLIENT' === upper) return 'client';
302
+ return 'internal';
303
+ }
304
+ function statusCodeToName(code) {
305
+ const statusMap = {
306
+ 0: 'UNSET',
307
+ 1: 'OK',
308
+ 2: 'ERROR'
309
+ };
310
+ if (null == code) return 'UNSET';
311
+ const num = Number(code);
312
+ return statusMap[num] ?? 'UNSET';
313
+ }
314
+ function inferSpanTypeFromName(name, explicitType) {
315
+ if ('string' == typeof explicitType && explicitType) return explicitType;
316
+ if ('agent.turn' === name || 'codex_turn' === name) return 'message';
317
+ if ('llm.request' === name) return 'model';
318
+ if ('tool.call' === name) return 'tool';
319
+ }
320
+ function otlpSpanToTlsEnvelope(otlpSpan, resource, scope, options) {
321
+ const status = otlpSpan.status;
322
+ const statusCodeNum = Number(status?.code);
323
+ const isError = 2 === statusCodeNum;
324
+ const attributes = otlpAttributesToMap(otlpSpan.attributes || []);
325
+ const spanType = inferSpanTypeFromName(otlpSpan.name, otlpSpan.type);
326
+ const isTool = 'tool' === spanType;
327
+ if (isTool) attributes['tool.error'] = isError;
328
+ else delete attributes['tool.error'];
329
+ if (isError) {
330
+ attributes['error.type'] = attributes['error.type'] ?? 'error';
331
+ attributes['error.message'] = String(status?.message || '');
332
+ } else {
333
+ attributes['error.type'] = '';
334
+ attributes['error.message'] = '';
335
+ }
336
+ if (options?.filterAttributes) {
337
+ for (const key of Object.keys(attributes))if (!options.filterAttributes(key)) delete attributes[key];
338
+ }
339
+ const resourceAttributes = otlpAttributesToMap(resource.attributes || []);
340
+ const events = (otlpSpan.events || []).map((event)=>({
341
+ timeUnixNano: event.timeUnixNano,
342
+ name: event.name,
343
+ attributes: otlpAttributesToMap(event.attributes || [])
344
+ }));
345
+ const links = (otlpSpan.links || []).map((link)=>({
346
+ traceId: link.traceId,
347
+ spanId: link.spanId,
348
+ traceState: link.traceState,
349
+ attributes: otlpAttributesToMap(link.attributes || [])
350
+ }));
351
+ const startNano = BigInt(String(otlpSpan.startTimeUnixNano));
352
+ const endNano = BigInt(String(otlpSpan.endTimeUnixNano));
353
+ const us = BigInt(1000);
354
+ const durationUs = (endNano - startNano) / us;
355
+ return {
356
+ TraceID: String(otlpSpan.traceId),
357
+ SpanID: String(otlpSpan.spanId),
358
+ ParentSpanID: String(otlpSpan.parentSpanId || ''),
359
+ Name: String(otlpSpan.name),
360
+ Kind: spanKindToName(otlpSpan.kind),
361
+ Start: String(startNano / us),
362
+ End: String(endNano / us),
363
+ Duration: String(durationUs),
364
+ StatusCode: statusCodeToName(statusCodeNum),
365
+ StatusDescription: String(status?.message || ''),
366
+ Attributes: json_safeJsonStringify(attributes),
367
+ Resource: json_safeJsonStringify(resourceAttributes),
368
+ ServiceName: String(resourceAttributes['service.name'] || ''),
369
+ Host: String(resourceAttributes['host.name'] || ''),
370
+ OTLPName: String(scope.name),
371
+ OTLPVersion: String(scope.version),
372
+ Events: json_safeJsonStringify(events),
373
+ Links: json_safeJsonStringify(links),
374
+ TraceState: String(otlpSpan.traceState || '')
375
+ };
376
+ }
377
+ const TLS_AUTH_RESOURCE_KEYS = {
378
+ traceTopicId: 'tls.otel.tracetopic',
379
+ region: 'tls.otel.region'
380
+ };
381
+ const SCHEMA_FIELD_ORDER = [
382
+ 'span.name',
383
+ 'span.kind',
384
+ 'tls.app.type',
385
+ 'session.id',
386
+ 'span.status.code',
387
+ 'start_time_unix_us',
388
+ 'end_time_unix_us',
389
+ 'span.duration_us',
390
+ 'service.name',
391
+ 'service.instance.id',
392
+ 'host.name',
393
+ 'observed_time_unix_ms',
394
+ 'telemetry.sdk.name',
395
+ 'telemetry.sdk.language',
396
+ 'telemetry.sdk.version',
397
+ 'otel.scope.name',
398
+ 'otel.scope.version',
399
+ 'event.name',
400
+ 'user.role',
401
+ 'gen_ai.operation.name',
402
+ 'gen_ai.provider.name',
403
+ 'gen_ai.request.model',
404
+ 'gen_ai.response.model',
405
+ 'gen_ai.request.duration_ms',
406
+ 'gen_ai.input.messages',
407
+ 'gen_ai.output.messages',
408
+ 'gen_ai.usage.input_tokens',
409
+ 'gen_ai.usage.output_tokens',
410
+ 'gen_ai.usage.reasoning.output_tokens',
411
+ 'gen_ai.usage.cache_read.input_tokens',
412
+ 'gen_ai.usage.cache_creation.input_tokens',
413
+ 'gen_ai.usage.total_tokens',
414
+ 'gen_ai.tool.name',
415
+ 'gen_ai.tool.call.id',
416
+ 'gen_ai.tool.call.arguments',
417
+ 'gen_ai.tool.call.result',
418
+ 'error.type',
419
+ 'error.message'
420
+ ];
421
+ function serializeForAttr(value) {
422
+ const serialized = 'string' == typeof value ? value : json_safeJsonStringify(value);
423
+ return String(serialized ?? '').slice(0, 65536);
424
+ }
425
+ function spanKind(span) {
426
+ if ('SERVER' === span.span_kind) return 2;
427
+ if ('CLIENT' === span.span_kind) return 3;
428
+ return 1;
429
+ }
430
+ function spanKindName(span) {
431
+ if ('SERVER' === span.span_kind) return 'server';
432
+ if ('CLIENT' === span.span_kind) return 'client';
433
+ return 'internal';
434
+ }
435
+ function unixMsToUs(ms) {
436
+ return String(1000 * Math.trunc(Number.isFinite(ms) ? ms : Date.now()));
437
+ }
438
+ function hasError(span) {
439
+ if (span.status?.code === 'ERROR') return true;
440
+ return Boolean(span.attributes['error.type']);
441
+ }
442
+ function buildResourceAttributes(trace, config) {
443
+ const attributes = {
444
+ ...trace.resource
445
+ };
446
+ if ('resource' === config.authMode) {
447
+ attributes[TLS_AUTH_RESOURCE_KEYS.traceTopicId] = config.traceTopicId;
448
+ attributes[TLS_AUTH_RESOURCE_KEYS.region] = config.region;
449
+ }
450
+ return attributes;
451
+ }
452
+ function cleanSpanAttributesForOtlp(span) {
453
+ const attributes = {
454
+ ...span.attributes
455
+ };
456
+ for (const key of [
457
+ 'span.name',
458
+ 'span.kind',
459
+ 'span.status.code',
460
+ 'service.name',
461
+ 'service.instance.id',
462
+ 'host.name',
463
+ 'telemetry.sdk.name',
464
+ 'telemetry.sdk.language',
465
+ 'telemetry.sdk.version'
466
+ ])delete attributes[key];
467
+ return attributes;
468
+ }
469
+ function buildOtlpPayload(trace, config) {
470
+ return {
471
+ resourceSpans: [
472
+ {
473
+ schemaUrl: trace.schema_url,
474
+ resource: {
475
+ attributes: toOtlpAttributes(buildResourceAttributes(trace, config))
476
+ },
477
+ scopeSpans: [
478
+ {
479
+ schemaUrl: trace.schema_url,
480
+ scope: {
481
+ name: 'tls-observer-cursor',
482
+ version: package_namespaceObject.rE
483
+ },
484
+ spans: trace.spans.map((span)=>({
485
+ traceId: span.trace_id,
486
+ spanId: span.span_id,
487
+ ...span.parent_span_id ? {
488
+ parentSpanId: span.parent_span_id
489
+ } : {},
490
+ name: span.name,
491
+ kind: spanKind(span),
492
+ startTimeUnixNano: toUnixNanoString(span.start_time_unix_ms),
493
+ endTimeUnixNano: toUnixNanoString(span.end_time_unix_ms),
494
+ attributes: toOtlpAttributes(cleanSpanAttributesForOtlp(span)),
495
+ status: hasError(span) ? {
496
+ code: 2,
497
+ message: span.status?.message || 'error'
498
+ } : {
499
+ code: span.status?.code === 'OK' ? 1 : 0
500
+ }
501
+ }))
502
+ }
503
+ ]
504
+ }
505
+ ]
506
+ };
507
+ }
508
+ function requireExportConfig(config) {
509
+ if (!config.enabled) return {
510
+ ok: false,
511
+ reason: 'disabled'
512
+ };
513
+ const missing = [];
514
+ const endpoint = config.tlsEndpoint || config.otelEndpoint;
515
+ if (!endpoint) missing.push('CURSOR_TLS_ENDPOINT or CURSOR_TLS_OTEL_ENDPOINT');
516
+ if (!config.region) missing.push('CURSOR_TLS_REGION');
517
+ if (!config.traceTopicId) missing.push('CURSOR_TLS_TRACE_TOPIC_ID');
518
+ if (!config.apiKey && (!config.ak || !config.sk)) missing.push('CURSOR_TLS_API_KEY or CURSOR_TLS_AK/CURSOR_TLS_SK');
519
+ if (config.apiKey && (config.ak || config.sk)) return {
520
+ ok: false,
521
+ reason: 'invalid_auth',
522
+ missing: [
523
+ 'use apiKey or ak/sk, not both'
524
+ ]
525
+ };
526
+ if (missing.length > 0) return {
527
+ ok: false,
528
+ reason: 'missing_config',
529
+ missing
530
+ };
531
+ return {
532
+ ok: true,
533
+ url: endpoint
534
+ };
535
+ }
536
+ function exporter_producerConfig(config) {
537
+ return {
538
+ endpoint: config.tlsEndpoint || config.otelEndpoint || '',
539
+ region: config.region || '',
540
+ traceTopicId: config.traceTopicId || '',
541
+ apiKey: config.apiKey,
542
+ ak: config.ak,
543
+ sk: config.sk,
544
+ timeoutMs: config.exportTimeoutMs
545
+ };
546
+ }
547
+ async function exportTraceToTls(trace, config) {
548
+ const required = requireExportConfig(config);
549
+ if (!required.ok) return {
550
+ status: 'skipped',
551
+ reason: required.reason,
552
+ message: required.missing?.join(', '),
553
+ trace_id: trace.trace_id,
554
+ span_count: trace.spans.length
555
+ };
556
+ const otlpPayload = buildOtlpPayload(trace, config);
557
+ const resourceSpan = otlpPayload.resourceSpans[0];
558
+ const resource = resourceSpan.resource;
559
+ const scopeSpan = resourceSpan.scopeSpans[0];
560
+ const scope = scopeSpan.scope;
561
+ const otlpSpans = scopeSpan.spans;
562
+ const records = otlpSpans.map((otlpSpan)=>otlpSpanToTlsEnvelope(otlpSpan, resource, scope));
563
+ const totalPayloadBytes = records.reduce((sum, record)=>sum + Buffer.byteLength(JSON.stringify(record), 'utf8'), 0);
564
+ const startedAt = Date.now();
565
+ const diagnostics = {
566
+ endpoint_host: normalizeProducerEndpoint(required.url),
567
+ endpoint_path: 'SendLogs'
568
+ };
569
+ try {
570
+ await sendTlsLogRecords(exporter_producerConfig(config), records, 'cursor-trace');
571
+ } catch (error) {
572
+ return {
573
+ status: 'failed',
574
+ reason: 'producer_error',
575
+ message: error instanceof Error ? error.message : String(error),
576
+ trace_id: trace.trace_id,
577
+ span_count: trace.spans.length,
578
+ payload_bytes: totalPayloadBytes,
579
+ attempts: 1,
580
+ duration_ms: Date.now() - startedAt,
581
+ ...diagnostics
582
+ };
583
+ }
584
+ return {
585
+ status: 'success',
586
+ trace_id: trace.trace_id,
587
+ span_count: trace.spans.length,
588
+ payload_bytes: totalPayloadBytes,
589
+ attempts: 1,
590
+ duration_ms: Date.now() - startedAt,
591
+ ...diagnostics
592
+ };
593
+ }
594
+ function stableFieldValue(value) {
595
+ if (null == value) return '';
596
+ if ('string' == typeof value || 'number' == typeof value || 'boolean' == typeof value) return value;
597
+ return serializeForAttr(value);
598
+ }
599
+ function inferUserRole(span) {
600
+ const input = span.input;
601
+ const role = input?.messages?.find((message)=>'string' == typeof message?.role)?.role;
602
+ return role || '';
603
+ }
604
+ function inferErrorMessage(span) {
605
+ if ('string' == typeof span.status?.message) return span.status.message;
606
+ const output = span.output;
607
+ if ('string' == typeof output?.error) return output.error;
608
+ return '';
609
+ }
610
+ function buildSchemaAlignedRecord(trace, span, observedTimeUnixMs = Date.now()) {
611
+ const attributes = span.attributes ?? {};
612
+ const resource = trace.resource ?? {};
613
+ const isModel = 'model' === span.type;
614
+ const isError = hasError(span);
615
+ const record = {
616
+ 'span.name': span.name,
617
+ 'span.kind': spanKindName(span),
618
+ 'tls.app.type': attributes['tls.app.type'] ?? '',
619
+ 'session.id': attributes['session.id'] ?? attributes['gen_ai.conversation.id'] ?? '',
620
+ 'span.status.code': isError ? 'ERROR' : 'UNSET',
621
+ start_time_unix_us: unixMsToUs(span.start_time_unix_ms),
622
+ end_time_unix_us: unixMsToUs(span.end_time_unix_ms),
623
+ 'span.duration_us': String(1000 * Math.max(0, Math.trunc(span.duration_ms))),
624
+ 'service.name': resource['service.name'] ?? '',
625
+ 'service.instance.id': resource['service.instance.id'] ?? '',
626
+ 'host.name': resource['host.name'] ?? '',
627
+ observed_time_unix_ms: observedTimeUnixMs,
628
+ 'telemetry.sdk.name': resource['telemetry.sdk.name'] ?? 'tls-observer-cursor',
629
+ 'telemetry.sdk.language': resource['telemetry.sdk.language'] ?? 'nodejs',
630
+ 'telemetry.sdk.version': resource['telemetry.sdk.version'] ?? '',
631
+ 'otel.scope.name': 'tls-observer-cursor',
632
+ 'otel.scope.version': package_namespaceObject.rE,
633
+ 'event.name': span.name,
634
+ 'user.role': inferUserRole(span),
635
+ 'gen_ai.operation.name': attributes['gen_ai.operation.name'] ?? '',
636
+ 'gen_ai.provider.name': attributes['gen_ai.provider.name'] ?? '',
637
+ 'gen_ai.request.model': attributes['gen_ai.request.model'] ?? '',
638
+ 'gen_ai.response.model': attributes['gen_ai.response.model'] ?? '',
639
+ 'gen_ai.request.duration_ms': isModel ? span.duration_ms : '',
640
+ 'gen_ai.input.messages': attributes['gen_ai.input.messages'] ?? '',
641
+ 'gen_ai.output.messages': attributes['gen_ai.output.messages'] ?? '',
642
+ 'gen_ai.usage.input_tokens': attributes['gen_ai.usage.input_tokens'] ?? '',
643
+ 'gen_ai.usage.output_tokens': attributes['gen_ai.usage.output_tokens'] ?? '',
644
+ 'gen_ai.usage.reasoning.output_tokens': attributes['gen_ai.usage.reasoning.output_tokens'] ?? '',
645
+ 'gen_ai.usage.cache_read.input_tokens': attributes['gen_ai.usage.cache_read.input_tokens'] ?? '',
646
+ 'gen_ai.usage.cache_creation.input_tokens': attributes['gen_ai.usage.cache_creation.input_tokens'] ?? '',
647
+ 'gen_ai.usage.total_tokens': attributes['gen_ai.usage.total_tokens'] ?? '',
648
+ 'gen_ai.tool.name': attributes['gen_ai.tool.name'] ?? '',
649
+ 'gen_ai.tool.call.id': attributes['gen_ai.tool.call.id'] ?? '',
650
+ 'gen_ai.tool.call.arguments': attributes['gen_ai.tool.call.arguments'] ?? '',
651
+ 'gen_ai.tool.call.result': attributes['gen_ai.tool.call.result'] ?? '',
652
+ 'error.type': isError ? attributes['error.type'] ?? 'error' : '',
653
+ 'error.message': isError ? attributes['error.message'] || inferErrorMessage(span) || '' : ''
654
+ };
655
+ return Object.fromEntries(SCHEMA_FIELD_ORDER.map((key)=>[
656
+ key,
657
+ stableFieldValue(record[key])
658
+ ]));
659
+ }
660
+ function buildSchemaAlignedRecords(trace, observedTimeUnixMs = Date.now()) {
661
+ return trace.spans.map((span)=>buildSchemaAlignedRecord(trace, span, observedTimeUnixMs));
662
+ }
663
+ function exportSummary(trace, result) {
664
+ return compactObject({
665
+ generated_at: new Date().toISOString(),
666
+ trace_id: trace.trace_id,
667
+ span_count: trace.spans.length,
668
+ export_status: result.status,
669
+ export_reason: result.reason,
670
+ export_message: result.message,
671
+ endpoint_host: result.endpoint_host,
672
+ endpoint_path: result.endpoint_path,
673
+ payload_bytes: result.payload_bytes,
674
+ duration_ms: result.duration_ms
675
+ });
676
+ }
677
+ const LOCK_TIMEOUT_MS = 5000;
678
+ const LOCK_RETRY_MS = 20;
679
+ const SESSION_MAX_AGE_MS = 86400000;
680
+ function sleep(ms) {
681
+ return new Promise((resolve)=>setTimeout(resolve, ms));
682
+ }
683
+ function state_lockPath(stateFilePath) {
684
+ return `${stateFilePath}.lock`;
685
+ }
686
+ async function acquireLock(stateFilePath) {
687
+ const lock = state_lockPath(stateFilePath);
688
+ const deadline = Date.now() + LOCK_TIMEOUT_MS;
689
+ __rspack_external_node_fs_5ea92f0c.mkdirSync(__rspack_external_node_path_c5b9b54f.dirname(stateFilePath), {
690
+ recursive: true
691
+ });
692
+ while(Date.now() < deadline)try {
693
+ const fd = __rspack_external_node_fs_5ea92f0c.openSync(lock, 'wx');
694
+ __rspack_external_node_fs_5ea92f0c.closeSync(fd);
695
+ return;
696
+ } catch {
697
+ await sleep(LOCK_RETRY_MS);
698
+ }
699
+ try {
700
+ __rspack_external_node_fs_5ea92f0c.unlinkSync(lock);
701
+ } catch {}
702
+ const fd = __rspack_external_node_fs_5ea92f0c.openSync(lock, 'wx');
703
+ __rspack_external_node_fs_5ea92f0c.closeSync(fd);
704
+ }
705
+ function releaseLock(stateFilePath) {
706
+ try {
707
+ __rspack_external_node_fs_5ea92f0c.unlinkSync(state_lockPath(stateFilePath));
708
+ } catch {}
709
+ }
710
+ function state_loadState(stateFilePath) {
711
+ try {
712
+ return JSON.parse(__rspack_external_node_fs_5ea92f0c.readFileSync(stateFilePath, 'utf8'));
713
+ } catch {
714
+ return {};
715
+ }
716
+ }
717
+ function state_getSessionState(state, sessionId) {
718
+ return state[sessionId] ?? {
719
+ last_line: -1,
720
+ last_offset: 0,
721
+ turn_count: 0,
722
+ updated: '',
723
+ tool_start_times: {},
724
+ tool_observations: {},
725
+ tool_observation_order: [],
726
+ pending_subagent_traces: [],
727
+ subagent_turn_count: 0
728
+ };
729
+ }
730
+ async function state_atomicUpdateState(stateFilePath, fn) {
731
+ await acquireLock(stateFilePath);
732
+ try {
733
+ const next = pruneOldSessions(fn(state_loadState(stateFilePath)));
734
+ __rspack_external_node_fs_5ea92f0c.mkdirSync(__rspack_external_node_path_c5b9b54f.dirname(stateFilePath), {
735
+ recursive: true
736
+ });
737
+ __rspack_external_node_fs_5ea92f0c.writeFileSync(stateFilePath, JSON.stringify(next, null, 2));
738
+ } finally{
739
+ releaseLock(stateFilePath);
740
+ }
741
+ }
742
+ function pruneOldSessions(state, now = Date.now()) {
743
+ const cutoff = now - SESSION_MAX_AGE_MS;
744
+ const pruned = {};
745
+ for (const [sessionId, session] of Object.entries(state)){
746
+ const updatedMs = session.updated ? new Date(session.updated).getTime() : 0;
747
+ if (updatedMs >= cutoff || session.current_turn_id) pruned[sessionId] = session;
748
+ }
749
+ return pruned;
750
+ }
751
+ const SCHEMA_URL = 'https://opentelemetry.io/schemas/1.41.0';
752
+ const MAX_ATTRIBUTE_TEXT = 65536;
753
+ const TLS_APP_TYPE = 'cursor';
754
+ const ROOT_SPAN_NAME = 'agent.turn';
755
+ const LLM_SPAN_NAME = 'llm.request';
756
+ const TOOL_SPAN_NAME = 'tool.call';
757
+ function hashHex(value, length) {
758
+ const hex = external_node_crypto_createHash('sha256').update(value).digest('hex').slice(0, length);
759
+ if (!/^0+$/.test(hex)) return hex;
760
+ return '1'.padStart(length, '0');
761
+ }
762
+ function toTraceId(value) {
763
+ const compact = value.replace(/-/g, '').toLowerCase();
764
+ if (/^[0-9a-f]{32}$/.test(compact) && compact !== '0'.repeat(32)) return compact;
765
+ return hashHex(value, 32);
766
+ }
767
+ function toSpanId(...parts) {
768
+ return hashHex(parts.map((part)=>String(part ?? '')).join(':'), 16);
769
+ }
770
+ function parseMs(value, fallback) {
771
+ if ('number' == typeof value && Number.isFinite(value)) return Math.trunc(value);
772
+ const parsed = Date.parse(String(value ?? ''));
773
+ return Number.isFinite(parsed) ? parsed : fallback;
774
+ }
775
+ function orderedTiming(startTime, endTime) {
776
+ const fallback = Date.now();
777
+ const startMs = parseMs(startTime, fallback);
778
+ const rawEndMs = parseMs(endTime, startMs);
779
+ if (rawEndMs < startMs) return {
780
+ startMs,
781
+ endMs: startMs
782
+ };
783
+ return {
784
+ startMs,
785
+ endMs: rawEndMs
786
+ };
787
+ }
788
+ function clipString(value) {
789
+ return value.length > MAX_ATTRIBUTE_TEXT ? `${value.slice(0, MAX_ATTRIBUTE_TEXT)}...[truncated]` : value;
790
+ }
791
+ function normalizeValue(value, captureContent) {
792
+ if (!captureContent) return;
793
+ if ('string' == typeof value) return clipString(value);
794
+ if (null == value) return value;
795
+ const text = json_safeJsonStringify(value);
796
+ return text.length > MAX_ATTRIBUTE_TEXT ? `${text.slice(0, MAX_ATTRIBUTE_TEXT)}...[truncated]` : value;
797
+ }
798
+ function stringifyStructuredAttributes(attributes) {
799
+ return Object.fromEntries(Object.entries(attributes).map(([key, value])=>[
800
+ key,
801
+ null !== value && 'object' == typeof value ? clipString(json_safeJsonStringify(value)) : value
802
+ ]));
803
+ }
804
+ function traceSpan(options) {
805
+ const { startMs, endMs } = orderedTiming(options.startTime, options.endTime);
806
+ return {
807
+ trace_id: options.traceId,
808
+ span_id: options.spanId,
809
+ parent_span_id: options.parentSpanId ?? null,
810
+ name: options.name,
811
+ type: options.type,
812
+ span_kind: options.spanKind ?? ('entry' === options.type ? 'SERVER' : 'model' === options.type ? 'CLIENT' : 'INTERNAL'),
813
+ start_time: new Date(startMs).toISOString(),
814
+ end_time: new Date(endMs).toISOString(),
815
+ start_time_unix_ms: startMs,
816
+ end_time_unix_ms: endMs,
817
+ duration_ms: endMs - startMs,
818
+ attributes: compactObject(stringifyStructuredAttributes({
819
+ ...options.attributes ?? {},
820
+ 'tls.app.type': TLS_APP_TYPE
821
+ })),
822
+ input: options.input,
823
+ output: options.output,
824
+ status: options.status
825
+ };
826
+ }
827
+ function modelId(model) {
828
+ if ('string' == typeof model) return model;
829
+ return model?.id ?? model?.display_name;
830
+ }
831
+ function inputModelId(input) {
832
+ return modelId(input.model) ?? input.model_id;
833
+ }
834
+ function contentBlockToPart(block, captureContent) {
835
+ if ('text' === block.type) return {
836
+ type: 'text',
837
+ content: normalizeValue(block.text, captureContent) ?? ''
838
+ };
839
+ if ('thinking' === block.type || 'reasoning' === block.type) return {
840
+ type: 'reasoning',
841
+ content: normalizeValue(block.thinking ?? block.text, captureContent) ?? ''
842
+ };
843
+ if ('tool_use' === block.type) return {
844
+ type: 'tool_call',
845
+ id: block.id,
846
+ name: block.name,
847
+ arguments: normalizeValue(block.input, captureContent)
848
+ };
849
+ return {
850
+ type: 'unknown',
851
+ content: ''
852
+ };
853
+ }
854
+ function messageContentToParts(content, captureContent) {
855
+ if ('string' == typeof content) return [
856
+ {
857
+ type: 'text',
858
+ content: normalizeValue(content, captureContent) ?? ''
859
+ }
860
+ ];
861
+ return content.map((item)=>normalizeValue(item, captureContent));
862
+ }
863
+ function assistantMessage(call, captureContent) {
864
+ return {
865
+ role: 'assistant',
866
+ parts: call.content.map((block)=>contentBlockToPart(block, captureContent))
867
+ };
868
+ }
869
+ function finishReasonForCall(call) {
870
+ if (call.stopReason) return call.stopReason;
871
+ return call.toolCalls.length > 0 ? 'tool_calls' : void 0;
872
+ }
873
+ function assistantOutputMessages(call, captureContent) {
874
+ const message = assistantMessage(call, captureContent);
875
+ const finishReason = finishReasonForCall(call);
876
+ return [
877
+ finishReason ? {
878
+ ...message,
879
+ finish_reason: finishReason
880
+ } : message
881
+ ];
882
+ }
883
+ function messagesAttribute(messages, captureContent) {
884
+ if (!captureContent || !messages) return;
885
+ return clipString(json_safeJsonStringify(messages));
886
+ }
887
+ function toolResultMessage(toolCallId, result, captureContent) {
888
+ const normalizedResult = normalizeValue(result, captureContent);
889
+ return {
890
+ role: 'tool',
891
+ parts: [
892
+ {
893
+ type: 'tool_call_response',
894
+ id: toolCallId,
895
+ response: normalizedResult,
896
+ result: normalizedResult
897
+ }
898
+ ]
899
+ };
900
+ }
901
+ function usageAttributes(usage) {
902
+ const cacheRead = usage.cache_read_input_tokens ?? 0;
903
+ const cacheCreation = usage.cache_creation_input_tokens ?? 0;
904
+ const inputTokens = usage.input_tokens ?? 0;
905
+ const outputTokens = usage.output_tokens ?? 0;
906
+ return compactObject({
907
+ 'gen_ai.usage.input_tokens': inputTokens || void 0,
908
+ 'gen_ai.usage.output_tokens': outputTokens || void 0,
909
+ 'gen_ai.usage.total_tokens': inputTokens || outputTokens ? inputTokens + outputTokens : void 0,
910
+ 'gen_ai.usage.reasoning.output_tokens': usage.reasoning_output_tokens || void 0,
911
+ 'gen_ai.usage.cache_read.input_tokens': cacheRead || void 0,
912
+ 'gen_ai.usage.cache_creation.input_tokens': cacheCreation || void 0
913
+ });
914
+ }
915
+ function sumNumericAttribute(records, key) {
916
+ return records.reduce((sum, record)=>{
917
+ const value = record[key];
918
+ return 'number' == typeof value && Number.isFinite(value) ? sum + value : sum;
919
+ }, 0);
920
+ }
921
+ function rootUsageAttributes(calls) {
922
+ const usageRecords = calls.map((call)=>usageAttributes(call.usage));
923
+ const totalTokens = sumNumericAttribute(usageRecords, 'gen_ai.usage.total_tokens');
924
+ const inputTokens = sumNumericAttribute(usageRecords, 'gen_ai.usage.input_tokens');
925
+ const outputTokens = sumNumericAttribute(usageRecords, 'gen_ai.usage.output_tokens');
926
+ const reasoningOutputTokens = sumNumericAttribute(usageRecords, 'gen_ai.usage.reasoning.output_tokens');
927
+ const cacheReadInputTokens = sumNumericAttribute(usageRecords, 'gen_ai.usage.cache_read.input_tokens');
928
+ return compactObject({
929
+ 'gen_ai.usage.total_tokens': totalTokens || void 0,
930
+ 'gen_ai.usage.input_tokens': inputTokens || void 0,
931
+ 'gen_ai.usage.output_tokens': outputTokens || void 0,
932
+ 'gen_ai.usage.reasoning.output_tokens': reasoningOutputTokens || void 0,
933
+ 'gen_ai.usage.cache_read.input_tokens': cacheReadInputTokens || void 0
934
+ });
935
+ }
936
+ function commonSessionAttributes(input) {
937
+ const conversationId = input.conversation_id ?? input.session_id;
938
+ return compactObject({
939
+ 'session.id': input.session_id ?? input.conversation_id,
940
+ 'gen_ai.conversation.id': conversationId,
941
+ 'cursor.session.id': input.session_id,
942
+ 'cursor.conversation.id': input.conversation_id,
943
+ 'cursor.generation.id': input.generation_id,
944
+ 'cursor.session.name': input.session_name,
945
+ 'cursor.version': input.cursor_version ?? input.version,
946
+ 'cursor.user.email': input.user_email,
947
+ 'cursor.cwd': input.cwd ?? input.workspace?.current_dir,
948
+ 'cursor.workspace.roots': input.workspace_roots,
949
+ 'cursor.workspace.project_dir': input.workspace?.project_dir,
950
+ "cursor.transcript_path": input.transcript_path,
951
+ 'cursor.model.id': inputModelId(input),
952
+ 'cursor.model.params': input.model_params
953
+ });
954
+ }
955
+ function buildResource(config) {
956
+ return compactObject({
957
+ 'service.name': config.serviceName,
958
+ 'service.instance.id': config.serviceInstanceId,
959
+ 'host.name': hostname(),
960
+ 'telemetry.sdk.language': 'nodejs',
961
+ 'telemetry.sdk.name': 'tls-observer-cursor',
962
+ 'telemetry.sdk.version': '0.1.0',
963
+ ...localIdentityMetadata()
964
+ });
965
+ }
966
+ function buildTraceFromTurn(options) {
967
+ const { input, turn, turnIndex, sessionState, config, extraAttributes } = options;
968
+ const traceSeed = [
969
+ input.session_id ?? 'cursor',
970
+ turn.promptId ?? sessionState.current_turn_id ?? turnIndex,
971
+ turn.userTimestamp,
972
+ options.traceSeedSuffix
973
+ ].filter((part)=>void 0 !== part && '' !== part).join(':');
974
+ const traceId = toTraceId(traceSeed);
975
+ const spans = [];
976
+ const fallbackEndMs = parseMs(options.turnEndTimeMs, Date.now());
977
+ const userStartMs = parseMs(turn.userTimestamp, parseMs(sessionState.current_turn_start_ms, fallbackEndMs));
978
+ const turnStartMs = Math.min(userStartMs, parseMs(sessionState.current_turn_start_ms, userStartMs), ...turn.llmCalls.map((call)=>parseMs(call.startTime, userStartMs)));
979
+ const activityEnds = [
980
+ ...turn.llmCalls.map((call)=>parseMs(call.endTime, turnStartMs)),
981
+ ...turn.llmCalls.flatMap((call)=>call.toolCalls.map((tool)=>parseMs(tool.result?.timestamp, turnStartMs)))
982
+ ].filter(Number.isFinite);
983
+ const turnEndMs = Math.max(turnStartMs, ...activityEnds, fallbackEndMs);
984
+ const turnSpanId = toSpanId(traceId, 'turn', turnIndex);
985
+ const sessionAttrs = compactObject({
986
+ ...commonSessionAttributes(input),
987
+ ...extraAttributes ?? {}
988
+ });
989
+ const userMessages = [
990
+ {
991
+ role: 'user',
992
+ parts: messageContentToParts(turn.userContent, config.captureContent)
993
+ }
994
+ ];
995
+ const lastAssistant = turn.llmCalls.at(-1);
996
+ const requestModel = modelId(input.model) ?? lastAssistant?.model;
997
+ const responseModel = lastAssistant?.model ?? requestModel;
998
+ const outputMessages = lastAssistant ? assistantOutputMessages(lastAssistant, config.captureContent) : void 0;
999
+ spans.push(traceSpan({
1000
+ traceId,
1001
+ spanId: turnSpanId,
1002
+ name: ROOT_SPAN_NAME,
1003
+ type: 'entry',
1004
+ startTime: turnStartMs,
1005
+ endTime: turnEndMs,
1006
+ attributes: {
1007
+ ...sessionAttrs,
1008
+ 'gen_ai.operation.name': 'invoke_agent',
1009
+ 'gen_ai.provider.name': 'cursor',
1010
+ 'gen_ai.request.model': requestModel,
1011
+ 'gen_ai.response.model': responseModel,
1012
+ 'gen_ai.input.messages': messagesAttribute(userMessages, config.captureContent),
1013
+ 'gen_ai.output.messages': messagesAttribute(outputMessages, config.captureContent),
1014
+ ...rootUsageAttributes(turn.llmCalls)
1015
+ },
1016
+ input: config.captureContent ? {
1017
+ messages: userMessages
1018
+ } : void 0,
1019
+ output: config.captureContent && outputMessages ? {
1020
+ messages: outputMessages
1021
+ } : void 0,
1022
+ status: turn.isComplete ? {
1023
+ code: 'OK'
1024
+ } : {
1025
+ code: 'ERROR',
1026
+ message: 'interrupted'
1027
+ }
1028
+ }));
1029
+ const accumulatedMessages = [
1030
+ ...userMessages
1031
+ ];
1032
+ for (const [llmIndex, call] of turn.llmCalls.entries()){
1033
+ const llmPointMs = parseMs(call.endTime, parseMs(call.startTime, turnStartMs));
1034
+ const assistant = assistantMessage(call, config.captureContent);
1035
+ const output = assistantOutputMessages(call, config.captureContent);
1036
+ const inputMessages = 0 === llmIndex ? [
1037
+ ...accumulatedMessages
1038
+ ] : void 0;
1039
+ const llmSpanId = toSpanId(traceId, 'llm', llmIndex, call.startTime, call.endTime);
1040
+ const finishReason = finishReasonForCall(call);
1041
+ spans.push(traceSpan({
1042
+ traceId,
1043
+ spanId: llmSpanId,
1044
+ parentSpanId: turnSpanId,
1045
+ name: LLM_SPAN_NAME,
1046
+ type: 'model',
1047
+ spanKind: 'CLIENT',
1048
+ startTime: llmPointMs,
1049
+ endTime: llmPointMs,
1050
+ attributes: {
1051
+ ...sessionAttrs,
1052
+ 'gen_ai.operation.name': 'chat',
1053
+ 'gen_ai.provider.name': 'cursor',
1054
+ 'gen_ai.request.model': modelId(input.model) ?? call.model,
1055
+ 'gen_ai.response.model': call.model,
1056
+ 'gen_ai.response.finish_reasons': finishReason ? [
1057
+ finishReason
1058
+ ] : void 0,
1059
+ 'gen_ai.input.messages': messagesAttribute(inputMessages, config.captureContent),
1060
+ 'gen_ai.output.messages': messagesAttribute(output, config.captureContent),
1061
+ ...usageAttributes(call.usage)
1062
+ },
1063
+ input: config.captureContent && inputMessages ? {
1064
+ messages: inputMessages
1065
+ } : void 0,
1066
+ output: config.captureContent ? {
1067
+ messages: output
1068
+ } : void 0
1069
+ }));
1070
+ accumulatedMessages.push(assistant);
1071
+ for (const toolCall of call.toolCalls){
1072
+ const toolInput = toolCall.tool_use.input;
1073
+ const toolOutput = toolCall.result?.content;
1074
+ const isError = toolCall.result?.is_error;
1075
+ const toolSpanId = toSpanId(traceId, 'tool', toolCall.tool_use.id);
1076
+ const errorType = 'tool_error';
1077
+ spans.push(traceSpan({
1078
+ traceId,
1079
+ spanId: toolSpanId,
1080
+ parentSpanId: llmSpanId,
1081
+ name: TOOL_SPAN_NAME,
1082
+ type: 'tool',
1083
+ spanKind: 'INTERNAL',
1084
+ startTime: llmPointMs,
1085
+ endTime: llmPointMs,
1086
+ attributes: {
1087
+ ...sessionAttrs,
1088
+ 'gen_ai.operation.name': 'execute_tool',
1089
+ 'gen_ai.tool.name': toolCall.tool_use.name,
1090
+ 'gen_ai.tool.call.id': toolCall.tool_use.id,
1091
+ 'gen_ai.tool.call.arguments': normalizeValue(toolInput, config.captureContent),
1092
+ 'gen_ai.tool.call.result': normalizeValue(toolOutput, config.captureContent),
1093
+ 'error.type': isError ? errorType : void 0,
1094
+ 'error.message': isError ? normalizeValue(toolOutput, config.captureContent) : void 0
1095
+ },
1096
+ input: normalizeValue(toolInput, config.captureContent),
1097
+ output: normalizeValue(toolOutput, config.captureContent),
1098
+ status: isError ? {
1099
+ code: 'ERROR',
1100
+ message: errorType
1101
+ } : {
1102
+ code: 'OK'
1103
+ }
1104
+ }));
1105
+ if (void 0 !== toolOutput) accumulatedMessages.push(toolResultMessage(toolCall.tool_use.id, toolOutput, config.captureContent));
1106
+ }
1107
+ }
1108
+ return {
1109
+ schema_url: SCHEMA_URL,
1110
+ source: options.source ?? "cursor_stop_hook_transcript",
1111
+ trace_id: traceId,
1112
+ resource: buildResource(config),
1113
+ spans: spans.sort((left, right)=>{
1114
+ const leftRoot = null === left.parent_span_id ? -1 : 0;
1115
+ const rightRoot = null === right.parent_span_id ? -1 : 0;
1116
+ return leftRoot - rightRoot || left.start_time_unix_ms - right.start_time_unix_ms || left.end_time_unix_ms - right.end_time_unix_ms;
1117
+ })
1118
+ };
1119
+ }
1120
+ function hookTextMessages(role, text, captureContent) {
1121
+ if (!text) return;
1122
+ return [
1123
+ {
1124
+ role,
1125
+ parts: [
1126
+ {
1127
+ type: 'text',
1128
+ content: normalizeValue(text, captureContent) ?? ''
1129
+ }
1130
+ ]
1131
+ }
1132
+ ];
1133
+ }
1134
+ function buildTraceFromHookState(options) {
1135
+ const { input, sessionState, config, turnIndex, extraAttributes } = options;
1136
+ const prompt = sessionState.current_prompt;
1137
+ const responseText = input.last_assistant_message;
1138
+ const stopMs = options.stopTimeMs ?? Date.now();
1139
+ const activityStarts = [
1140
+ sessionState.current_turn_start_ms
1141
+ ].filter((value)=>'number' == typeof value && Number.isFinite(value));
1142
+ if (!prompt && !responseText) return;
1143
+ const turnStartMs = Math.min(...activityStarts, stopMs);
1144
+ const turnEndMs = Math.max(turnStartMs, stopMs);
1145
+ const traceSeed = [
1146
+ input.session_id ?? input.conversation_id ?? 'cursor',
1147
+ input.generation_id,
1148
+ sessionState.current_turn_id ?? turnIndex,
1149
+ sessionState.current_turn_start_ms ?? turnStartMs,
1150
+ options.traceSeedSuffix
1151
+ ].filter((part)=>void 0 !== part && '' !== part).join(':');
1152
+ const traceId = toTraceId(traceSeed);
1153
+ const turnSpanId = toSpanId(traceId, 'turn', turnIndex);
1154
+ const llmSpanId = toSpanId(traceId, 'llm', 'hook', turnIndex);
1155
+ const sessionAttrs = compactObject({
1156
+ ...commonSessionAttributes(input),
1157
+ ...extraAttributes ?? {}
1158
+ });
1159
+ const userMessages = hookTextMessages('user', prompt, config.captureContent);
1160
+ const outputMessages = hookTextMessages('assistant', responseText, config.captureContent);
1161
+ const requestModel = inputModelId(input);
1162
+ const responseModel = requestModel;
1163
+ const spans = [];
1164
+ spans.push(traceSpan({
1165
+ traceId,
1166
+ spanId: turnSpanId,
1167
+ name: ROOT_SPAN_NAME,
1168
+ type: 'entry',
1169
+ startTime: turnStartMs,
1170
+ endTime: turnEndMs,
1171
+ attributes: {
1172
+ ...sessionAttrs,
1173
+ 'gen_ai.operation.name': 'invoke_agent',
1174
+ 'gen_ai.provider.name': 'cursor',
1175
+ 'gen_ai.request.model': requestModel,
1176
+ 'gen_ai.response.model': responseModel,
1177
+ 'gen_ai.input.messages': messagesAttribute(userMessages, config.captureContent),
1178
+ 'gen_ai.output.messages': messagesAttribute(outputMessages, config.captureContent)
1179
+ },
1180
+ input: config.captureContent && userMessages ? {
1181
+ messages: userMessages
1182
+ } : void 0,
1183
+ output: config.captureContent && outputMessages ? {
1184
+ messages: outputMessages
1185
+ } : void 0,
1186
+ status: 'error' === input.reason ? {
1187
+ code: 'ERROR',
1188
+ message: 'error'
1189
+ } : {
1190
+ code: 'OK'
1191
+ }
1192
+ }));
1193
+ spans.push(traceSpan({
1194
+ traceId,
1195
+ spanId: llmSpanId,
1196
+ parentSpanId: turnSpanId,
1197
+ name: LLM_SPAN_NAME,
1198
+ type: 'model',
1199
+ spanKind: 'CLIENT',
1200
+ startTime: turnEndMs,
1201
+ endTime: turnEndMs,
1202
+ attributes: {
1203
+ ...sessionAttrs,
1204
+ 'gen_ai.operation.name': 'chat',
1205
+ 'gen_ai.provider.name': 'cursor',
1206
+ 'gen_ai.request.model': requestModel,
1207
+ 'gen_ai.response.model': responseModel,
1208
+ 'gen_ai.input.messages': messagesAttribute(userMessages, config.captureContent),
1209
+ 'gen_ai.output.messages': messagesAttribute(outputMessages, config.captureContent)
1210
+ },
1211
+ input: config.captureContent && userMessages ? {
1212
+ messages: userMessages
1213
+ } : void 0,
1214
+ output: config.captureContent && outputMessages ? {
1215
+ messages: outputMessages
1216
+ } : void 0,
1217
+ status: {
1218
+ code: 'OK'
1219
+ }
1220
+ }));
1221
+ return {
1222
+ schema_url: SCHEMA_URL,
1223
+ source: options.source ?? 'cursor_stop_hook_events',
1224
+ trace_id: traceId,
1225
+ resource: buildResource(config),
1226
+ spans: spans.sort((left, right)=>{
1227
+ const leftRoot = null === left.parent_span_id ? -1 : 0;
1228
+ const rightRoot = null === right.parent_span_id ? -1 : 0;
1229
+ return leftRoot - rightRoot || left.start_time_unix_ms - right.start_time_unix_ms || left.end_time_unix_ms - right.end_time_unix_ms;
1230
+ })
1231
+ };
1232
+ }
1233
+ async function writeTextFileAtomic(filePath, text, options = {}) {
1234
+ await __rspack_external_node_fs_promises_153e37e0.mkdir(__rspack_external_node_path_c5b9b54f.dirname(filePath), {
1235
+ recursive: true
1236
+ });
1237
+ const tempPath = __rspack_external_node_path_c5b9b54f.join(__rspack_external_node_path_c5b9b54f.dirname(filePath), `.${__rspack_external_node_path_c5b9b54f.basename(filePath)}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`);
1238
+ let renamed = false;
1239
+ try {
1240
+ await __rspack_external_node_fs_promises_153e37e0.writeFile(tempPath, text, {
1241
+ encoding: 'utf8',
1242
+ ...void 0 === options.mode ? {} : {
1243
+ mode: options.mode
1244
+ }
1245
+ });
1246
+ await __rspack_external_node_fs_promises_153e37e0.rename(tempPath, filePath);
1247
+ renamed = true;
1248
+ if (void 0 !== options.mode) await __rspack_external_node_fs_promises_153e37e0.chmod(filePath, options.mode);
1249
+ } finally{
1250
+ if (!renamed) await __rspack_external_node_fs_promises_153e37e0.rm(tempPath, {
1251
+ force: true
1252
+ }).catch(()=>void 0);
1253
+ }
1254
+ }
1255
+ async function writeJsonFileAtomic(filePath, value, options = {}) {
1256
+ await writeTextFileAtomic(filePath, `${JSON.stringify(value, null, 2)}\n`, options);
1257
+ }
1258
+ const DEFAULT_MAX_FILES = 3;
1259
+ const LOCK_STALE_MS = 30000;
1260
+ const jsonl_LOCK_TIMEOUT_MS = 5000;
1261
+ const jsonl_LOCK_RETRY_MS = 25;
1262
+ async function appendJsonlFile(filePath, values) {
1263
+ const entries = Array.isArray(values) ? values : [
1264
+ values
1265
+ ];
1266
+ if (0 === entries.length) return;
1267
+ await __rspack_external_node_fs_promises_153e37e0.mkdir(__rspack_external_node_path_c5b9b54f.dirname(filePath), {
1268
+ recursive: true
1269
+ });
1270
+ await __rspack_external_node_fs_promises_153e37e0.appendFile(filePath, `${entries.map((entry)=>JSON.stringify(entry)).join('\n')}\n`, 'utf8');
1271
+ }
1272
+ async function delay(ms) {
1273
+ await new Promise((resolve)=>setTimeout(resolve, ms));
1274
+ }
1275
+ async function withJsonlFileLock(filePath, action) {
1276
+ await __rspack_external_node_fs_promises_153e37e0.mkdir(__rspack_external_node_path_c5b9b54f.dirname(filePath), {
1277
+ recursive: true
1278
+ });
1279
+ const lockPath = `${filePath}.lock`;
1280
+ const startedAt = Date.now();
1281
+ while(true)try {
1282
+ const handle = await __rspack_external_node_fs_promises_153e37e0.open(lockPath, 'wx');
1283
+ try {
1284
+ await handle.writeFile(`${process.pid}:${new Date().toISOString()}\n`, 'utf8');
1285
+ return await action();
1286
+ } finally{
1287
+ await handle.close().catch(()=>void 0);
1288
+ await __rspack_external_node_fs_promises_153e37e0.rm(lockPath, {
1289
+ force: true
1290
+ }).catch(()=>void 0);
1291
+ }
1292
+ } catch (error) {
1293
+ if ('EEXIST' !== error.code) throw error;
1294
+ try {
1295
+ const stat = await __rspack_external_node_fs_promises_153e37e0.stat(lockPath);
1296
+ if (Date.now() - stat.mtimeMs > LOCK_STALE_MS) {
1297
+ await __rspack_external_node_fs_promises_153e37e0.rm(lockPath, {
1298
+ force: true
1299
+ }).catch(()=>void 0);
1300
+ continue;
1301
+ }
1302
+ } catch (statError) {
1303
+ if ('ENOENT' === statError.code) continue;
1304
+ throw statError;
1305
+ }
1306
+ if (Date.now() - startedAt > jsonl_LOCK_TIMEOUT_MS) throw new Error(`Timed out waiting for JSONL file lock: ${lockPath}`);
1307
+ await delay(jsonl_LOCK_RETRY_MS);
1308
+ }
1309
+ }
1310
+ function rotatedFileName(filePath, now = new Date()) {
1311
+ const dir = __rspack_external_node_path_c5b9b54f.dirname(filePath);
1312
+ const ext = __rspack_external_node_path_c5b9b54f.extname(filePath);
1313
+ const base = __rspack_external_node_path_c5b9b54f.basename(filePath, ext);
1314
+ const stamp = now.toISOString().replace(/[-:.TZ]/g, '').slice(0, 17);
1315
+ return __rspack_external_node_path_c5b9b54f.join(dir, `${base}.${stamp}.${process.pid}.${randomUUID()}${ext || '.jsonl'}`);
1316
+ }
1317
+ function rotatedFilePattern(filePath) {
1318
+ const ext = __rspack_external_node_path_c5b9b54f.extname(filePath);
1319
+ const base = __rspack_external_node_path_c5b9b54f.basename(filePath, ext).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1320
+ const escapedExt = (ext || '.jsonl').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1321
+ return new RegExp(`^${base}\\.\\d{17}\\.\\d+\\.[0-9a-f-]+${escapedExt}$`);
1322
+ }
1323
+ async function pruneRotatedJsonlFiles(filePath, maxFiles) {
1324
+ const maxRotatedFiles = Math.max(0, maxFiles - 1);
1325
+ const dir = __rspack_external_node_path_c5b9b54f.dirname(filePath);
1326
+ const pattern = rotatedFilePattern(filePath);
1327
+ let entries;
1328
+ try {
1329
+ entries = await __rspack_external_node_fs_promises_153e37e0.readdir(dir);
1330
+ } catch (error) {
1331
+ if ('ENOENT' === error.code) return;
1332
+ throw error;
1333
+ }
1334
+ const rotated = entries.filter((entry)=>pattern.test(entry)).sort((left, right)=>right.localeCompare(left)).map((entry)=>__rspack_external_node_path_c5b9b54f.join(dir, entry));
1335
+ await Promise.all(rotated.slice(maxRotatedFiles).map((target)=>__rspack_external_node_fs_promises_153e37e0.rm(target, {
1336
+ force: true
1337
+ })));
1338
+ }
1339
+ async function rotateActiveJsonlFile(filePath, maxFiles) {
1340
+ await __rspack_external_node_fs_promises_153e37e0.rename(filePath, rotatedFileName(filePath)).catch(async (error)=>{
1341
+ if ('ENOENT' !== error.code) throw error;
1342
+ });
1343
+ await pruneRotatedJsonlFiles(filePath, maxFiles);
1344
+ }
1345
+ async function trimJsonlFileByBytes(filePath, maxBytes) {
1346
+ if (maxBytes <= 0) return void await __rspack_external_node_fs_promises_153e37e0.rm(filePath, {
1347
+ force: true
1348
+ }).catch(()=>void 0);
1349
+ let stat;
1350
+ try {
1351
+ stat = await __rspack_external_node_fs_promises_153e37e0.stat(filePath);
1352
+ } catch (error) {
1353
+ if ('ENOENT' === error.code) return;
1354
+ throw error;
1355
+ }
1356
+ if (stat.size <= maxBytes) return;
1357
+ const lines = (await __rspack_external_node_fs_promises_153e37e0.readFile(filePath, 'utf8')).split(/\r?\n/).filter(Boolean);
1358
+ const kept = [];
1359
+ let keptBytes = 0;
1360
+ for(let index = lines.length - 1; index >= 0; index -= 1){
1361
+ const line = lines[index];
1362
+ const lineBytes = Buffer.byteLength(`${line}\n`, 'utf8');
1363
+ if (kept.length > 0 && keptBytes + lineBytes > maxBytes) break;
1364
+ kept.push(line);
1365
+ keptBytes += lineBytes;
1366
+ if (keptBytes >= maxBytes) break;
1367
+ }
1368
+ await writeTextFileAtomic(filePath, kept.length ? `${kept.reverse().join('\n')}\n` : '');
1369
+ }
1370
+ async function appendBoundedJsonlFile(filePath, values, maxBytes, options = {}) {
1371
+ const entries = Array.isArray(values) ? values : [
1372
+ values
1373
+ ];
1374
+ if (0 === entries.length) return;
1375
+ if ('rotate' !== options.strategy) return void await withJsonlFileLock(filePath, async ()=>{
1376
+ await appendJsonlFile(filePath, entries);
1377
+ await trimJsonlFileByBytes(filePath, maxBytes);
1378
+ });
1379
+ await withJsonlFileLock(filePath, async ()=>{
1380
+ const maxFiles = options.maxFiles ?? DEFAULT_MAX_FILES;
1381
+ if (maxBytes <= 0 || maxFiles <= 0) {
1382
+ await __rspack_external_node_fs_promises_153e37e0.rm(filePath, {
1383
+ force: true
1384
+ }).catch(()=>void 0);
1385
+ await pruneRotatedJsonlFiles(filePath, 0);
1386
+ return;
1387
+ }
1388
+ let currentBytes = 0;
1389
+ try {
1390
+ currentBytes = (await __rspack_external_node_fs_promises_153e37e0.stat(filePath)).size;
1391
+ } catch (error) {
1392
+ if ('ENOENT' !== error.code) throw error;
1393
+ }
1394
+ for (const entry of entries){
1395
+ const line = `${JSON.stringify(entry)}\n`;
1396
+ const lineBytes = Buffer.byteLength(line, 'utf8');
1397
+ if (currentBytes > 0 && currentBytes + lineBytes > maxBytes) {
1398
+ await rotateActiveJsonlFile(filePath, maxFiles);
1399
+ currentBytes = 0;
1400
+ }
1401
+ await __rspack_external_node_fs_promises_153e37e0.appendFile(filePath, line, 'utf8');
1402
+ currentBytes += lineBytes;
1403
+ }
1404
+ await pruneRotatedJsonlFiles(filePath, maxFiles);
1405
+ });
1406
+ }
1407
+ function readTranscript(filePath, afterLine = -1, afterOffset = 0, options = {}) {
1408
+ let size;
1409
+ try {
1410
+ size = __rspack_external_node_fs_5ea92f0c.statSync(filePath).size;
1411
+ } catch {
1412
+ return {
1413
+ messages: [],
1414
+ lastLine: afterLine,
1415
+ lastOffset: Math.max(0, afterOffset)
1416
+ };
1417
+ }
1418
+ const offsetBeyondFile = Number.isFinite(afterOffset) && afterOffset > size;
1419
+ const effectiveAfterLine = offsetBeyondFile ? -1 : afterLine;
1420
+ const requestedOffset = Number.isFinite(afterOffset) && afterOffset > 0 && afterOffset <= size ? Math.trunc(afterOffset) : 0;
1421
+ const lookBehindBytes = Math.max(0, Math.trunc(options.lookBehindBytes ?? 0));
1422
+ const startOffset = requestedOffset > 0 ? Math.max(0, requestedOffset - lookBehindBytes) : 0;
1423
+ const usingOffset = requestedOffset > 0;
1424
+ const fd = __rspack_external_node_fs_5ea92f0c.openSync(filePath, 'r');
1425
+ try {
1426
+ let discardFirstLine = false;
1427
+ if (startOffset > 0) {
1428
+ const previousByte = Buffer.alloc(1);
1429
+ __rspack_external_node_fs_5ea92f0c.readSync(fd, previousByte, 0, 1, startOffset - 1);
1430
+ discardFirstLine = 0x0a !== previousByte[0];
1431
+ }
1432
+ const chunkSize = 2097152;
1433
+ const buf = Buffer.alloc(chunkSize);
1434
+ const messages = [];
1435
+ let lastLine = effectiveAfterLine;
1436
+ let lastOffset = startOffset;
1437
+ let lineIndex = usingOffset ? effectiveAfterLine : -1;
1438
+ let partial = '';
1439
+ let readPos = startOffset;
1440
+ let cursorOffset = startOffset;
1441
+ let bytesRead = 0;
1442
+ while((bytesRead = __rspack_external_node_fs_5ea92f0c.readSync(fd, buf, 0, chunkSize, readPos)) > 0){
1443
+ readPos += bytesRead;
1444
+ const chunk = partial + buf.toString('utf8', 0, bytesRead);
1445
+ const lines = chunk.split('\n');
1446
+ partial = lines.pop() ?? '';
1447
+ for (const line of lines){
1448
+ const rawWithNewline = `${line}\n`;
1449
+ const lineStartOffset = cursorOffset;
1450
+ cursorOffset += Buffer.byteLength(rawWithNewline, 'utf8');
1451
+ if (discardFirstLine) {
1452
+ discardFirstLine = false;
1453
+ continue;
1454
+ }
1455
+ const trimmed = line.trim();
1456
+ if (trimmed) {
1457
+ lineIndex += 1;
1458
+ lastLine = lineIndex;
1459
+ lastOffset = cursorOffset;
1460
+ if (usingOffset || !(lineIndex <= effectiveAfterLine)) try {
1461
+ messages.push({
1462
+ ...JSON.parse(trimmed),
1463
+ __line: lineIndex,
1464
+ __startOffset: lineStartOffset,
1465
+ __endOffset: cursorOffset
1466
+ });
1467
+ } catch {}
1468
+ }
1469
+ }
1470
+ }
1471
+ if (partial.trim()) {
1472
+ const lineStartOffset = cursorOffset;
1473
+ cursorOffset += Buffer.byteLength(partial, 'utf8');
1474
+ if (discardFirstLine) {
1475
+ discardFirstLine = false;
1476
+ return {
1477
+ messages,
1478
+ lastLine,
1479
+ lastOffset: cursorOffset
1480
+ };
1481
+ }
1482
+ lineIndex += 1;
1483
+ lastLine = lineIndex;
1484
+ lastOffset = cursorOffset;
1485
+ if (usingOffset || lineIndex > effectiveAfterLine) try {
1486
+ messages.push({
1487
+ ...JSON.parse(partial.trim()),
1488
+ __line: lineIndex,
1489
+ __startOffset: lineStartOffset,
1490
+ __endOffset: cursorOffset
1491
+ });
1492
+ } catch {}
1493
+ }
1494
+ return {
1495
+ messages,
1496
+ lastLine,
1497
+ lastOffset
1498
+ };
1499
+ } finally{
1500
+ __rspack_external_node_fs_5ea92f0c.closeSync(fd);
1501
+ }
1502
+ }
1503
+ function getTranscriptEndPosition(filePath) {
1504
+ try {
1505
+ return {
1506
+ lastLine: -1,
1507
+ lastOffset: __rspack_external_node_fs_5ea92f0c.statSync(filePath).size
1508
+ };
1509
+ } catch {
1510
+ return {
1511
+ lastLine: -1,
1512
+ lastOffset: 0
1513
+ };
1514
+ }
1515
+ }
1516
+ function hashId(value) {
1517
+ return external_node_crypto_createHash('sha256').update(JSON.stringify(value ?? '')).digest('hex').slice(0, 12);
1518
+ }
1519
+ function syntheticTime(baseMs, entry, blockIndex = 0) {
1520
+ return new Date(baseMs + 10 * Math.max(0, entry.__line ?? 0) + blockIndex).toISOString();
1521
+ }
1522
+ function asUsage(value) {
1523
+ return value && 'object' == typeof value ? value : {};
1524
+ }
1525
+ function textFromUnknown(value) {
1526
+ if ('string' == typeof value) return value;
1527
+ if (Array.isArray(value)) return value.map((item)=>{
1528
+ if ('string' == typeof item) return item;
1529
+ if (item && 'object' == typeof item && 'string' == typeof item.text) return item.text;
1530
+ return '';
1531
+ }).join('');
1532
+ if (null == value) return '';
1533
+ return JSON.stringify(value);
1534
+ }
1535
+ function contentArray(entry) {
1536
+ const content = entry.message?.content;
1537
+ if (Array.isArray(content)) return content;
1538
+ if ('string' == typeof content) return [
1539
+ {
1540
+ type: 'text',
1541
+ text: content
1542
+ }
1543
+ ];
1544
+ return [];
1545
+ }
1546
+ function normalizeToolUse(block, entry, blockIndex) {
1547
+ const name = String(block.name ?? block.tool_name ?? block.tool_type ?? 'tool');
1548
+ const input = block.input && 'object' == typeof block.input ? block.input : block.tool_input && 'object' == typeof block.tool_input ? block.tool_input : {};
1549
+ const id = String(block.id ?? block.tool_use_id ?? block.tool_call_id ?? `cursor-tool-${entry.__line ?? 'unknown'}-${blockIndex}-${hashId({
1550
+ name,
1551
+ input
1552
+ })}`);
1553
+ return {
1554
+ type: 'tool_use',
1555
+ id,
1556
+ name,
1557
+ input
1558
+ };
1559
+ }
1560
+ function normalizeContent(entry) {
1561
+ const blocks = [];
1562
+ const toolResults = [];
1563
+ for (const [index, raw] of contentArray(entry).entries()){
1564
+ if ('string' == typeof raw) {
1565
+ blocks.push({
1566
+ type: 'text',
1567
+ text: raw
1568
+ });
1569
+ continue;
1570
+ }
1571
+ if (!raw || 'object' != typeof raw) continue;
1572
+ const block = raw;
1573
+ const type = String(block.type ?? '');
1574
+ if ('text' === type && 'string' == typeof block.text) {
1575
+ const previous = blocks.at(-1);
1576
+ if (previous?.type === 'text') previous.text += block.text;
1577
+ else blocks.push({
1578
+ type: 'text',
1579
+ text: block.text
1580
+ });
1581
+ continue;
1582
+ }
1583
+ if ('thinking' === type || 'reasoning' === type) {
1584
+ blocks.push({
1585
+ type,
1586
+ thinking: 'string' == typeof block.thinking ? block.thinking : textFromUnknown(block.text)
1587
+ });
1588
+ continue;
1589
+ }
1590
+ if ('tool_use' === type) {
1591
+ blocks.push(normalizeToolUse(block, entry, index));
1592
+ continue;
1593
+ }
1594
+ if ('tool_result' === type) toolResults.push({
1595
+ type: 'tool_result',
1596
+ tool_use_id: 'string' == typeof block.tool_use_id ? block.tool_use_id : void 0,
1597
+ content: block.content,
1598
+ is_error: Boolean(block.is_error)
1599
+ });
1600
+ }
1601
+ return {
1602
+ blocks,
1603
+ toolResults
1604
+ };
1605
+ }
1606
+ function isUserEntry(entry) {
1607
+ return 'user' === entry.role || entry.message?.role === 'user';
1608
+ }
1609
+ function isAssistantEntry(entry) {
1610
+ return 'assistant' === entry.role || entry.message?.role === 'assistant';
1611
+ }
1612
+ function isTurnEnd(entry) {
1613
+ return 'turn_ended' === entry.type;
1614
+ }
1615
+ function userContent(entry) {
1616
+ const normalized = normalizeContent(entry);
1617
+ if (!normalized.blocks.length) return '';
1618
+ if (normalized.blocks.every((block)=>'text' === block.type)) return normalized.blocks.map((block)=>block.text).join('');
1619
+ return normalized.blocks;
1620
+ }
1621
+ function findToolResult(toolUseId, toolResults) {
1622
+ const matched = toolResults.find((item)=>item.result.tool_use_id === toolUseId);
1623
+ if (!matched) return;
1624
+ return {
1625
+ content: textFromUnknown(matched.result.content),
1626
+ timestamp: matched.timestamp,
1627
+ is_error: matched.result.is_error
1628
+ };
1629
+ }
1630
+ function groupIntoTurns(messages, options = {}) {
1631
+ const turns = [];
1632
+ const baseMs = options.fallbackStartMs ?? Date.now();
1633
+ let currentUser = null;
1634
+ let assistantEntries = [];
1635
+ let toolResults = [];
1636
+ let currentTurnEndOffset;
1637
+ let complete = false;
1638
+ function finalize(forceIncomplete = false) {
1639
+ if (!currentUser) return;
1640
+ const llmCalls = assistantEntries.map((entry)=>{
1641
+ const normalized = normalizeContent(entry);
1642
+ const content = normalized.blocks;
1643
+ for (const result of normalized.toolResults)toolResults.push({
1644
+ result,
1645
+ timestamp: entry.timestamp ?? syntheticTime(baseMs, entry)
1646
+ });
1647
+ if (!content.length) return;
1648
+ const startTime = entry.timestamp ?? syntheticTime(baseMs, entry);
1649
+ const endTime = entry.timestamp ?? syntheticTime(baseMs, entry, content.length);
1650
+ const toolCalls = content.filter((block)=>'tool_use' === block.type).map((toolUse)=>{
1651
+ const result = findToolResult(toolUse.id, toolResults);
1652
+ return result ? {
1653
+ tool_use: toolUse,
1654
+ result
1655
+ } : {
1656
+ tool_use: toolUse
1657
+ };
1658
+ });
1659
+ return {
1660
+ content,
1661
+ model: entry.message?.model ?? options.fallbackModel ?? 'cursor-agent',
1662
+ usage: asUsage(entry.message?.usage),
1663
+ startTime,
1664
+ endTime,
1665
+ toolCalls,
1666
+ stopReason: entry.message?.stop_reason
1667
+ };
1668
+ }).filter((call)=>Boolean(call));
1669
+ turns.push({
1670
+ userContent: userContent(currentUser),
1671
+ userTimestamp: currentUser.timestamp ?? syntheticTime(baseMs, currentUser),
1672
+ promptId: currentUser.promptId,
1673
+ llmCalls,
1674
+ isComplete: complete && !forceIncomplete,
1675
+ startOffset: currentUser.__startOffset,
1676
+ endOffset: currentTurnEndOffset ?? assistantEntries.at(-1)?.__endOffset ?? currentUser.__endOffset
1677
+ });
1678
+ }
1679
+ for (const entry of messages){
1680
+ if (isUserEntry(entry)) {
1681
+ finalize();
1682
+ currentUser = entry;
1683
+ assistantEntries = [];
1684
+ toolResults = [];
1685
+ currentTurnEndOffset = void 0;
1686
+ complete = false;
1687
+ continue;
1688
+ }
1689
+ if (currentUser) {
1690
+ if (isAssistantEntry(entry)) {
1691
+ assistantEntries.push(entry);
1692
+ continue;
1693
+ }
1694
+ if (isTurnEnd(entry)) {
1695
+ complete = 'error' !== entry.status;
1696
+ currentTurnEndOffset = entry.__endOffset;
1697
+ finalize();
1698
+ currentUser = null;
1699
+ assistantEntries = [];
1700
+ toolResults = [];
1701
+ currentTurnEndOffset = void 0;
1702
+ complete = false;
1703
+ }
1704
+ }
1705
+ }
1706
+ finalize(true);
1707
+ return turns;
1708
+ }
1709
+ function trace_flow_expandHome(filePath) {
1710
+ if (!filePath) return;
1711
+ if ('~' === filePath) return homedir();
1712
+ if (filePath.startsWith('~/')) return __rspack_external_node_path_c5b9b54f.join(homedir(), filePath.slice(2));
1713
+ return filePath;
1714
+ }
1715
+ function trace_flow_sessionIdForInput(input, transcriptPath) {
1716
+ if (input.session_id) return input.session_id;
1717
+ if (input.conversation_id) return input.conversation_id;
1718
+ const resolvedTranscript = transcriptPath ?? trace_flow_expandHome(input.transcript_path ?? void 0);
1719
+ if (!resolvedTranscript) return;
1720
+ const parts = resolvedTranscript.split(__rspack_external_node_path_c5b9b54f.sep).filter(Boolean);
1721
+ const transcriptFile = parts.at(-1)?.replace(/\.jsonl$/i, '');
1722
+ const parent = parts.at(-2);
1723
+ return transcriptFile || parent;
1724
+ }
1725
+ function sanitizeFileName(value) {
1726
+ return value.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 160) || 'trace';
1727
+ }
1728
+ async function writeSendLog(config, record) {
1729
+ await writeJsonFileAtomic(__rspack_external_node_path_c5b9b54f.join(config.sendLogsDir, 'latest.send-log.json'), record);
1730
+ await appendBoundedJsonlFile(__rspack_external_node_path_c5b9b54f.join(config.dataRoot, 'logs', 'diagnostics', 'send-log.jsonl'), record, config.diagnosticLogMaxBytes);
1731
+ }
1732
+ async function writeDebugArtifacts(config, trace) {
1733
+ if (!config.debugArtifacts) return {};
1734
+ const baseName = sanitizeFileName(`${trace.trace_id}.${Date.now()}`);
1735
+ const tracePath = __rspack_external_node_path_c5b9b54f.join(config.artifactsDir, 'otel-traces', `${baseName}.trace.json`);
1736
+ const payloadPath = __rspack_external_node_path_c5b9b54f.join(config.artifactsDir, 'otlp-payloads', `${baseName}.otlp.json`);
1737
+ const schemaPath = __rspack_external_node_path_c5b9b54f.join(config.artifactsDir, 'schema-aligned', `${baseName}.schema-aligned.json`);
1738
+ await writeJsonFileAtomic(tracePath, {
1739
+ generated_at: new Date().toISOString(),
1740
+ trace
1741
+ });
1742
+ await writeJsonFileAtomic(payloadPath, buildOtlpPayload(trace, config));
1743
+ await writeJsonFileAtomic(schemaPath, {
1744
+ generated_at: new Date().toISOString(),
1745
+ records: buildSchemaAlignedRecords(trace)
1746
+ });
1747
+ return {
1748
+ trace_path: tracePath,
1749
+ otlp_payload_path: payloadPath,
1750
+ schema_aligned_path: schemaPath
1751
+ };
1752
+ }
1753
+ async function trace_flow_exportTraceAndLog(options) {
1754
+ const { config, trace, hookEventName } = options;
1755
+ const artifacts = await writeDebugArtifacts(config, trace);
1756
+ const result = await exportTraceToTls(trace, config);
1757
+ await writeSendLog(config, {
1758
+ ...exportSummary(trace, result),
1759
+ hook_event_name: hookEventName,
1760
+ capture_content: config.captureContent,
1761
+ debug_artifacts: config.debugArtifacts,
1762
+ ...artifacts
1763
+ });
1764
+ }
1765
+ function synthesizeMissingFinalAssistant(turns, input, turnEndTimeMs) {
1766
+ if (!turns.length || !input.last_assistant_message) return;
1767
+ const lastTurn = turns[turns.length - 1];
1768
+ const lastLlm = lastTurn.llmCalls.at(-1);
1769
+ if (!lastLlm || 0 === lastLlm.toolCalls.length) return;
1770
+ const lastToolEnd = Date.parse(lastLlm.endTime);
1771
+ lastTurn.llmCalls.push({
1772
+ content: [
1773
+ {
1774
+ type: 'text',
1775
+ text: input.last_assistant_message
1776
+ }
1777
+ ],
1778
+ model: lastLlm.model,
1779
+ usage: {},
1780
+ startTime: new Date(lastToolEnd).toISOString(),
1781
+ endTime: new Date(turnEndTimeMs).toISOString(),
1782
+ toolCalls: [],
1783
+ synthetic: true
1784
+ });
1785
+ }
1786
+ async function exportTranscriptTurns(options) {
1787
+ const turnEndTimeMs = options.turnEndTimeMs ?? Date.now();
1788
+ const { messages, lastLine, lastOffset } = readTranscript(options.transcriptPath, options.afterLine, options.afterOffset ?? 0, {
1789
+ lookBehindBytes: options.config.transcriptLookbehindBytes
1790
+ });
1791
+ const parsedTurns = groupIntoTurns(messages, {
1792
+ fallbackStartMs: options.session.current_turn_start_ms,
1793
+ fallbackModel: 'string' == typeof options.input.model ? options.input.model : options.input.model?.id ?? options.input.model?.display_name
1794
+ });
1795
+ const afterOffset = options.afterOffset ?? 0;
1796
+ const turns = afterOffset > 0 ? parsedTurns.filter((turn)=>void 0 === turn.endOffset || turn.endOffset > afterOffset) : parsedTurns;
1797
+ if (options.synthesizeFinalAssistant) synthesizeMissingFinalAssistant(turns, options.input, turnEndTimeMs);
1798
+ let tracedTurns = 0;
1799
+ for (const turn of turns){
1800
+ const trace = buildTraceFromTurn({
1801
+ input: options.input,
1802
+ turn,
1803
+ turnIndex: (options.turnIndexBase ?? options.session.turn_count) + tracedTurns + 1,
1804
+ sessionState: options.session,
1805
+ config: options.config,
1806
+ turnEndTimeMs,
1807
+ source: options.source,
1808
+ traceSeedSuffix: options.traceSeedSuffix,
1809
+ extraAttributes: options.extraAttributes
1810
+ });
1811
+ await trace_flow_exportTraceAndLog({
1812
+ config: options.config,
1813
+ trace,
1814
+ hookEventName: options.hookEventName
1815
+ });
1816
+ tracedTurns += 1;
1817
+ }
1818
+ return {
1819
+ lastLine,
1820
+ lastOffset,
1821
+ tracedTurns
1822
+ };
1823
+ }
1824
+ const TRANSCRIPT_FLUSH_DELAY_MS = 200;
1825
+ function clearedActiveSession(session, updates = {}) {
1826
+ return {
1827
+ ...session,
1828
+ ...updates,
1829
+ current_turn_id: void 0,
1830
+ current_turn_start_ms: void 0,
1831
+ current_prompt: void 0,
1832
+ tool_start_times: {},
1833
+ tool_observations: {},
1834
+ tool_observation_order: [],
1835
+ pending_subagent_traces: [],
1836
+ updated: new Date().toISOString()
1837
+ };
1838
+ }
1839
+ async function waitForTranscriptFlush() {
1840
+ await new Promise((resolve)=>setTimeout(resolve, TRANSCRIPT_FLUSH_DELAY_MS));
1841
+ }
1842
+ async function handleStop(input, config) {
1843
+ if (input.stop_hook_active) return;
1844
+ const stopTimeMs = Date.now();
1845
+ const transcriptPath = trace_flow_expandHome(input.transcript_path ?? void 0);
1846
+ const sessionId = trace_flow_sessionIdForInput(input, transcriptPath);
1847
+ if (!sessionId) return;
1848
+ await waitForTranscriptFlush();
1849
+ const session = state_getSessionState(state_loadState(config.stateFilePath), sessionId);
1850
+ let tracedTurns = 0;
1851
+ let lastLine = session.last_line;
1852
+ let lastOffset = session.last_offset ?? 0;
1853
+ const stopInput = {
1854
+ ...input,
1855
+ hook_event_name: 'stop',
1856
+ session_id: input.session_id ?? sessionId,
1857
+ conversation_id: input.conversation_id ?? sessionId,
1858
+ transcript_path: transcriptPath
1859
+ };
1860
+ if (transcriptPath) {
1861
+ const result = await exportTranscriptTurns({
1862
+ input: stopInput,
1863
+ config,
1864
+ transcriptPath,
1865
+ afterLine: session.last_line,
1866
+ afterOffset: session.last_offset ?? 0,
1867
+ session,
1868
+ hookEventName: input.hook_event_name,
1869
+ turnEndTimeMs: stopTimeMs,
1870
+ source: "cursor_stop_hook_transcript"
1871
+ });
1872
+ tracedTurns = result.tracedTurns;
1873
+ lastLine = result.lastLine;
1874
+ lastOffset = result.lastOffset;
1875
+ }
1876
+ if (0 === tracedTurns) {
1877
+ const hookTrace = buildTraceFromHookState({
1878
+ input: stopInput,
1879
+ sessionState: session,
1880
+ config,
1881
+ turnIndex: session.turn_count + 1,
1882
+ stopTimeMs,
1883
+ source: 'cursor_stop_hook_state'
1884
+ });
1885
+ if (hookTrace) {
1886
+ await trace_flow_exportTraceAndLog({
1887
+ config,
1888
+ trace: hookTrace,
1889
+ hookEventName: input.hook_event_name
1890
+ });
1891
+ tracedTurns = 1;
1892
+ }
1893
+ }
1894
+ await state_atomicUpdateState(config.stateFilePath, (state)=>{
1895
+ const latestSession = state_getSessionState(state, sessionId);
1896
+ return {
1897
+ ...state,
1898
+ [sessionId]: clearedActiveSession(latestSession, {
1899
+ last_line: lastLine,
1900
+ last_offset: lastOffset,
1901
+ turn_count: latestSession.turn_count + tracedTurns,
1902
+ subagent_turn_count: latestSession.subagent_turn_count ?? 0
1903
+ })
1904
+ };
1905
+ });
1906
+ }
1907
+ async function handleSessionEnd(input, config) {
1908
+ const stopTimeMs = Date.now();
1909
+ const transcriptPath = trace_flow_expandHome(input.transcript_path ?? void 0);
1910
+ const sessionId = trace_flow_sessionIdForInput(input, transcriptPath);
1911
+ if (!sessionId) return;
1912
+ const session = state_getSessionState(state_loadState(config.stateFilePath), sessionId);
1913
+ if (!session.current_turn_id) return;
1914
+ await waitForTranscriptFlush();
1915
+ let tracedTurns = 0;
1916
+ let lastLine = session.last_line;
1917
+ let lastOffset = session.last_offset ?? 0;
1918
+ const stopInput = {
1919
+ ...input,
1920
+ hook_event_name: 'stop',
1921
+ session_id: input.session_id ?? sessionId,
1922
+ conversation_id: input.conversation_id ?? sessionId,
1923
+ transcript_path: transcriptPath
1924
+ };
1925
+ if (session.current_turn_id && transcriptPath) {
1926
+ const result = await exportTranscriptTurns({
1927
+ input: stopInput,
1928
+ config,
1929
+ transcriptPath,
1930
+ afterLine: session.last_line,
1931
+ afterOffset: session.last_offset ?? 0,
1932
+ session,
1933
+ hookEventName: input.hook_event_name,
1934
+ turnEndTimeMs: stopTimeMs,
1935
+ source: "cursor_session_end_transcript",
1936
+ traceSeedSuffix: `session-end:${input.reason ?? 'unknown'}`
1937
+ });
1938
+ tracedTurns = result.tracedTurns;
1939
+ lastLine = result.lastLine;
1940
+ lastOffset = result.lastOffset;
1941
+ }
1942
+ if (session.current_turn_id && 0 === tracedTurns) {
1943
+ const hookTrace = buildTraceFromHookState({
1944
+ input: stopInput,
1945
+ sessionState: session,
1946
+ config,
1947
+ turnIndex: session.turn_count + 1,
1948
+ stopTimeMs,
1949
+ source: 'cursor_session_end_hook_state',
1950
+ traceSeedSuffix: `session-end:${input.reason ?? 'unknown'}`
1951
+ });
1952
+ if (hookTrace) {
1953
+ await trace_flow_exportTraceAndLog({
1954
+ config,
1955
+ trace: hookTrace,
1956
+ hookEventName: input.hook_event_name
1957
+ });
1958
+ tracedTurns = 1;
1959
+ }
1960
+ }
1961
+ await state_atomicUpdateState(config.stateFilePath, (state)=>{
1962
+ const latestSession = state_getSessionState(state, sessionId);
1963
+ return {
1964
+ ...state,
1965
+ [sessionId]: clearedActiveSession(latestSession, {
1966
+ last_line: lastLine,
1967
+ last_offset: lastOffset,
1968
+ turn_count: latestSession.turn_count + tracedTurns,
1969
+ subagent_turn_count: latestSession.subagent_turn_count ?? 0
1970
+ })
1971
+ };
1972
+ });
1973
+ }
1974
+ function promptText(input) {
1975
+ if ('string' == typeof input.prompt) return input.prompt;
1976
+ if ('string' == typeof input.user_prompt) return input.user_prompt;
1977
+ if ('string' == typeof input.query) return input.query;
1978
+ if ('string' == typeof input.text) return input.text;
1979
+ }
1980
+ async function handleUserPromptSubmit(input, config) {
1981
+ const transcriptPath = trace_flow_expandHome(input.transcript_path ?? void 0);
1982
+ const sessionId = trace_flow_sessionIdForInput(input, transcriptPath);
1983
+ if (!sessionId) return;
1984
+ const startMs = Date.now();
1985
+ let interruptedLastLine;
1986
+ let interruptedLastOffset;
1987
+ let interruptedTurns = 0;
1988
+ const previousSession = state_getSessionState(state_loadState(config.stateFilePath), sessionId);
1989
+ if (previousSession.current_turn_id) {
1990
+ const stopInput = {
1991
+ ...input,
1992
+ hook_event_name: 'stop',
1993
+ session_id: input.session_id ?? sessionId,
1994
+ conversation_id: input.conversation_id ?? sessionId,
1995
+ transcript_path: transcriptPath
1996
+ };
1997
+ if (transcriptPath) {
1998
+ const result = await exportTranscriptTurns({
1999
+ input: stopInput,
2000
+ config,
2001
+ transcriptPath,
2002
+ afterLine: previousSession.last_line,
2003
+ afterOffset: previousSession.last_offset ?? 0,
2004
+ session: previousSession,
2005
+ hookEventName: input.hook_event_name,
2006
+ source: "cursor_interrupted_turn_transcript",
2007
+ traceSeedSuffix: 'interrupted-before-new-prompt'
2008
+ });
2009
+ interruptedTurns = result.tracedTurns;
2010
+ interruptedLastLine = result.lastLine;
2011
+ interruptedLastOffset = result.lastOffset;
2012
+ }
2013
+ }
2014
+ const endPosition = transcriptPath ? getTranscriptEndPosition(transcriptPath) : {
2015
+ lastLine: -1,
2016
+ lastOffset: 0
2017
+ };
2018
+ await state_atomicUpdateState(config.stateFilePath, (state)=>{
2019
+ const session = state_getSessionState(state, sessionId);
2020
+ return {
2021
+ ...state,
2022
+ [sessionId]: {
2023
+ ...session,
2024
+ last_line: interruptedLastLine ?? endPosition.lastLine,
2025
+ last_offset: interruptedLastOffset ?? endPosition.lastOffset,
2026
+ turn_count: session.turn_count + interruptedTurns,
2027
+ subagent_turn_count: session.subagent_turn_count ?? 0,
2028
+ current_turn_id: `${sessionId}:${startMs}`,
2029
+ current_turn_start_ms: startMs,
2030
+ current_prompt: promptText(input),
2031
+ tool_start_times: {},
2032
+ tool_observations: {},
2033
+ tool_observation_order: [],
2034
+ pending_subagent_traces: [],
2035
+ updated: new Date().toISOString()
2036
+ }
2037
+ };
2038
+ });
2039
+ }
2040
+ function eventName(input) {
2041
+ return String(input.hook_event_name ?? input.event_name ?? '');
2042
+ }
2043
+ async function dispatchHook(input, config) {
2044
+ switch(eventName(input)){
2045
+ case 'beforeSubmitPrompt':
2046
+ case 'UserPromptSubmit':
2047
+ await handleUserPromptSubmit(input, config);
2048
+ return;
2049
+ case 'stop':
2050
+ case 'Stop':
2051
+ await handleStop(input, config);
2052
+ return;
2053
+ case 'sessionEnd':
2054
+ case 'SessionEnd':
2055
+ await handleSessionEnd(input, config);
2056
+ return;
2057
+ default:
2058
+ return;
2059
+ }
2060
+ }
2061
+ async function readStdinJson() {
2062
+ const text = await new Promise((resolve, reject)=>{
2063
+ let input = '';
2064
+ process.stdin.setEncoding('utf8');
2065
+ process.stdin.on('data', (chunk)=>{
2066
+ input += chunk;
2067
+ });
2068
+ process.stdin.on('end', ()=>resolve(input));
2069
+ process.stdin.on('error', reject);
2070
+ });
2071
+ if (!text.trim()) return {};
2072
+ return JSON.parse(text);
2073
+ }
2074
+ async function main() {
2075
+ try {
2076
+ const input = await readStdinJson();
2077
+ const config = loadConfig();
2078
+ await __rspack_external_node_fs_promises_153e37e0.mkdir(config.dataRoot, {
2079
+ recursive: true
2080
+ });
2081
+ await dispatchHook(input, config);
2082
+ } finally{
2083
+ await closeTraceLogProducers();
2084
+ }
2085
+ }
2086
+ main().catch(async (error)=>{
2087
+ try {
2088
+ const config = loadConfig();
2089
+ await writeSendLog(config, {
2090
+ generated_at: new Date().toISOString(),
2091
+ export_status: 'failed',
2092
+ reason: 'hook_error',
2093
+ message: error instanceof Error ? error.stack || error.message : String(error)
2094
+ });
2095
+ } catch {}
2096
+ process.exit(0);
2097
+ });