neatlogs 1.0.6 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/azure-openai.cjs +344 -0
  2. package/dist/azure-openai.cjs.map +1 -0
  3. package/dist/azure-openai.d.ts +30 -0
  4. package/dist/azure-openai.mjs +318 -0
  5. package/dist/azure-openai.mjs.map +1 -0
  6. package/dist/bedrock.cjs +540 -0
  7. package/dist/bedrock.cjs.map +1 -0
  8. package/dist/bedrock.d.ts +29 -0
  9. package/dist/bedrock.mjs +514 -0
  10. package/dist/bedrock.mjs.map +1 -0
  11. package/dist/browser.cjs +121 -0
  12. package/dist/browser.cjs.map +1 -0
  13. package/dist/browser.d.ts +143 -0
  14. package/dist/browser.mjs +96 -0
  15. package/dist/browser.mjs.map +1 -0
  16. package/dist/claude-agent-sdk.cjs +406 -0
  17. package/dist/claude-agent-sdk.cjs.map +1 -0
  18. package/dist/claude-agent-sdk.d.ts +49 -0
  19. package/dist/claude-agent-sdk.mjs +381 -0
  20. package/dist/claude-agent-sdk.mjs.map +1 -0
  21. package/dist/index.cjs +3642 -1126
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.ts +7 -1
  24. package/dist/index.mjs +3637 -1132
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/langchain.cjs +9 -9
  27. package/dist/langchain.cjs.map +1 -1
  28. package/dist/langchain.mjs +9 -9
  29. package/dist/langchain.mjs.map +1 -1
  30. package/dist/mastra-wrap.cjs +25 -25
  31. package/dist/mastra-wrap.cjs.map +1 -1
  32. package/dist/mastra-wrap.mjs +25 -25
  33. package/dist/mastra-wrap.mjs.map +1 -1
  34. package/dist/openai-agents.cjs +6 -6
  35. package/dist/openai-agents.cjs.map +1 -1
  36. package/dist/openai-agents.mjs +6 -6
  37. package/dist/openai-agents.mjs.map +1 -1
  38. package/dist/opencode-plugin.cjs +682 -0
  39. package/dist/opencode-plugin.cjs.map +1 -0
  40. package/dist/opencode-plugin.d.ts +39 -0
  41. package/dist/opencode-plugin.mjs +644 -0
  42. package/dist/opencode-plugin.mjs.map +1 -0
  43. package/dist/openrouter-agent.cjs +273 -0
  44. package/dist/openrouter-agent.cjs.map +1 -0
  45. package/dist/openrouter-agent.d.ts +34 -0
  46. package/dist/openrouter-agent.mjs +247 -0
  47. package/dist/openrouter-agent.mjs.map +1 -0
  48. package/dist/pi-agent.cjs +10 -10
  49. package/dist/pi-agent.cjs.map +1 -1
  50. package/dist/pi-agent.mjs +10 -10
  51. package/dist/pi-agent.mjs.map +1 -1
  52. package/dist/vertex-ai.cjs +424 -0
  53. package/dist/vertex-ai.cjs.map +1 -0
  54. package/dist/vertex-ai.d.ts +39 -0
  55. package/dist/vertex-ai.mjs +397 -0
  56. package/dist/vertex-ai.mjs.map +1 -0
  57. package/package.json +78 -2
@@ -0,0 +1,644 @@
1
+ // src/opencode-trace-shipper.ts
2
+ import protobuf from "protobufjs";
3
+
4
+ // src/version.ts
5
+ var __version__ = "1.0.9";
6
+
7
+ // src/opencode-trace-shipper.ts
8
+ var PACKAGE_NAME = "neatlogs.opencode";
9
+ var OTLP_PROTO_JSON = {
10
+ nested: {
11
+ opentelemetry: {
12
+ nested: {
13
+ proto: {
14
+ nested: {
15
+ common: {
16
+ nested: {
17
+ v1: {
18
+ nested: {
19
+ AnyValue: {
20
+ oneofs: {
21
+ value: {
22
+ oneof: [
23
+ "stringValue",
24
+ "boolValue",
25
+ "intValue",
26
+ "doubleValue",
27
+ "arrayValue",
28
+ "kvlistValue",
29
+ "bytesValue"
30
+ ]
31
+ }
32
+ },
33
+ fields: {
34
+ stringValue: { type: "string", id: 1 },
35
+ boolValue: { type: "bool", id: 2 },
36
+ intValue: { type: "int64", id: 3 },
37
+ doubleValue: { type: "double", id: 4 },
38
+ arrayValue: { type: "ArrayValue", id: 5 },
39
+ kvlistValue: { type: "KeyValueList", id: 6 },
40
+ bytesValue: { type: "bytes", id: 7 }
41
+ }
42
+ },
43
+ ArrayValue: { fields: { values: { rule: "repeated", type: "AnyValue", id: 1 } } },
44
+ KeyValueList: { fields: { values: { rule: "repeated", type: "KeyValue", id: 1 } } },
45
+ KeyValue: {
46
+ fields: { key: { type: "string", id: 1 }, value: { type: "AnyValue", id: 2 } }
47
+ },
48
+ InstrumentationScope: {
49
+ fields: { name: { type: "string", id: 1 }, version: { type: "string", id: 2 } }
50
+ }
51
+ }
52
+ }
53
+ }
54
+ },
55
+ resource: {
56
+ nested: {
57
+ v1: {
58
+ nested: {
59
+ Resource: {
60
+ fields: {
61
+ attributes: {
62
+ rule: "repeated",
63
+ type: "opentelemetry.proto.common.v1.KeyValue",
64
+ id: 1
65
+ }
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ },
72
+ trace: {
73
+ nested: {
74
+ v1: {
75
+ nested: {
76
+ ResourceSpans: {
77
+ fields: {
78
+ resource: { type: "opentelemetry.proto.resource.v1.Resource", id: 1 },
79
+ scopeSpans: { rule: "repeated", type: "ScopeSpans", id: 2 }
80
+ }
81
+ },
82
+ ScopeSpans: {
83
+ fields: {
84
+ scope: { type: "opentelemetry.proto.common.v1.InstrumentationScope", id: 1 },
85
+ spans: { rule: "repeated", type: "Span", id: 2 }
86
+ }
87
+ },
88
+ Span: {
89
+ fields: {
90
+ traceId: { type: "bytes", id: 1 },
91
+ spanId: { type: "bytes", id: 2 },
92
+ traceState: { type: "string", id: 3 },
93
+ parentSpanId: { type: "bytes", id: 4 },
94
+ name: { type: "string", id: 5 },
95
+ kind: { type: "SpanKind", id: 6 },
96
+ startTimeUnixNano: { type: "fixed64", id: 7 },
97
+ endTimeUnixNano: { type: "fixed64", id: 8 },
98
+ attributes: {
99
+ rule: "repeated",
100
+ type: "opentelemetry.proto.common.v1.KeyValue",
101
+ id: 9
102
+ },
103
+ droppedAttributesCount: { type: "uint32", id: 10 },
104
+ status: { type: "Status", id: 15 }
105
+ }
106
+ },
107
+ Status: {
108
+ fields: { message: { type: "string", id: 2 }, code: { type: "StatusCode", id: 3 } }
109
+ },
110
+ StatusCode: {
111
+ values: { STATUS_CODE_UNSET: 0, STATUS_CODE_OK: 1, STATUS_CODE_ERROR: 2 }
112
+ },
113
+ SpanKind: {
114
+ values: {
115
+ SPAN_KIND_UNSPECIFIED: 0,
116
+ SPAN_KIND_INTERNAL: 1,
117
+ SPAN_KIND_SERVER: 2,
118
+ SPAN_KIND_CLIENT: 3,
119
+ SPAN_KIND_PRODUCER: 4,
120
+ SPAN_KIND_CONSUMER: 5
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ },
127
+ collector: {
128
+ nested: {
129
+ trace: {
130
+ nested: {
131
+ v1: {
132
+ nested: {
133
+ ExportTraceServiceRequest: {
134
+ fields: {
135
+ resourceSpans: {
136
+ rule: "repeated",
137
+ type: "opentelemetry.proto.trace.v1.ResourceSpans",
138
+ id: 1
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
153
+ };
154
+ var protoRoot = protobuf.Root.fromJSON(OTLP_PROTO_JSON);
155
+ var ExportTraceServiceRequest = protoRoot.lookupType(
156
+ "opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest"
157
+ );
158
+ var SpanStatusCode = { UNSET: 0, OK: 1, ERROR: 2 };
159
+ function randomBytes(length) {
160
+ const out = new Uint8Array(length);
161
+ const crypto = globalThis.crypto;
162
+ if (crypto && typeof crypto.getRandomValues === "function") {
163
+ crypto.getRandomValues(out);
164
+ return out;
165
+ }
166
+ for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
167
+ return out;
168
+ }
169
+ function generateTraceId() {
170
+ return randomBytes(16);
171
+ }
172
+ function generateSpanId() {
173
+ return randomBytes(8);
174
+ }
175
+ function nowNanoString() {
176
+ return (BigInt(Date.now()) * 1000000n).toString();
177
+ }
178
+ function msToNanoString(ms) {
179
+ return (BigInt(Math.floor(ms)) * 1000000n).toString();
180
+ }
181
+ function attrStr(key, value) {
182
+ if (value === void 0 || value === null) return void 0;
183
+ return { key, value: { stringValue: String(value) } };
184
+ }
185
+ function attrInt(key, value) {
186
+ if (value === void 0 || value === null || !Number.isFinite(value)) return void 0;
187
+ return { key, value: { intValue: String(Math.trunc(value)) } };
188
+ }
189
+ function attrDouble(key, value) {
190
+ if (value === void 0 || value === null || !Number.isFinite(value)) return void 0;
191
+ return { key, value: { doubleValue: value } };
192
+ }
193
+ var TraceShipper = class {
194
+ apiKey;
195
+ endpoint;
196
+ debug;
197
+ maxRetries;
198
+ workflowName;
199
+ queue = [];
200
+ prefix = "[neatlogs/opencode]";
201
+ constructor(opts) {
202
+ this.apiKey = opts.apiKey;
203
+ this.endpoint = opts.endpoint.endsWith("/") ? opts.endpoint.slice(0, -1) : opts.endpoint;
204
+ this.debug = !!opts.debug;
205
+ this.maxRetries = opts.maxRetries ?? 3;
206
+ this.workflowName = opts.workflowName || "";
207
+ }
208
+ enqueue(span) {
209
+ this.queue.push(span);
210
+ }
211
+ get pending() {
212
+ return this.queue.length;
213
+ }
214
+ /**
215
+ * Ship all queued spans in a single awaited POST. Resolves only once the HTTP
216
+ * response is received (or all retries are exhausted) — so a short-lived host
217
+ * can safely exit immediately after awaiting this.
218
+ */
219
+ async flush() {
220
+ if (this.queue.length === 0) return;
221
+ if (!this.apiKey) {
222
+ this.queue = [];
223
+ return;
224
+ }
225
+ const spans = this.queue.splice(0);
226
+ const payload = this.buildProtobuf(spans);
227
+ const url = `${this.endpoint}/v1/traces`;
228
+ if (this.debug) console.log(`${this.prefix} Shipping ${spans.length} spans to ${url}`);
229
+ for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
230
+ try {
231
+ const resp = await fetch(url, {
232
+ method: "POST",
233
+ headers: {
234
+ "Content-Type": "application/x-protobuf",
235
+ "x-api-key": this.apiKey
236
+ },
237
+ body: payload
238
+ });
239
+ if (resp.ok) {
240
+ if (this.debug) console.log(`${this.prefix} Shipped ${spans.length} spans`);
241
+ return;
242
+ }
243
+ if (resp.status === 401) {
244
+ console.warn(`${this.prefix} Invalid API key (401) \u2014 dropping ${spans.length} spans`);
245
+ return;
246
+ }
247
+ if (resp.status < 500 && resp.status !== 429) {
248
+ if (this.debug) console.warn(`${this.prefix} HTTP ${resp.status} \u2014 dropping spans`);
249
+ return;
250
+ }
251
+ } catch (err) {
252
+ if (this.debug) {
253
+ console.warn(
254
+ `${this.prefix} Attempt ${attempt}/${this.maxRetries}: ${err.message}`
255
+ );
256
+ }
257
+ }
258
+ if (attempt < this.maxRetries) {
259
+ await new Promise((r) => setTimeout(r, 500 * 2 ** (attempt - 1)));
260
+ }
261
+ }
262
+ console.warn(`${this.prefix} Failed to ship ${spans.length} spans after ${this.maxRetries} attempts`);
263
+ }
264
+ buildProtobuf(spans) {
265
+ const protoSpans = spans.map((span) => ({
266
+ traceId: span.traceId,
267
+ spanId: span.spanId,
268
+ parentSpanId: span.parentSpanId || void 0,
269
+ name: span.name,
270
+ kind: span.kind,
271
+ startTimeUnixNano: nanoToLong(span.startTimeUnixNano),
272
+ endTimeUnixNano: nanoToLong(span.endTimeUnixNano),
273
+ attributes: span.attributes.map((a) => ({
274
+ key: a.key,
275
+ value: a.value.intValue !== void 0 ? { intValue: nanoToLong(a.value.intValue) } : a.value
276
+ })),
277
+ status: span.status ? { code: span.status.code, message: span.status.message } : void 0
278
+ }));
279
+ const resourceAttributes = [
280
+ { key: "service.name", value: { stringValue: "neatlogs.opencode" } },
281
+ { key: "service.version", value: { stringValue: __version__ } }
282
+ ];
283
+ if (this.workflowName) {
284
+ resourceAttributes.push({
285
+ key: "neatlogs.workflow_name",
286
+ value: { stringValue: this.workflowName }
287
+ });
288
+ }
289
+ const message = {
290
+ resourceSpans: [
291
+ {
292
+ resource: { attributes: resourceAttributes },
293
+ scopeSpans: [
294
+ {
295
+ scope: { name: PACKAGE_NAME, version: __version__ },
296
+ spans: protoSpans
297
+ }
298
+ ]
299
+ }
300
+ ]
301
+ };
302
+ const errMsg = ExportTraceServiceRequest.verify(message);
303
+ if (errMsg && this.debug) console.warn(`${this.prefix} Proto verify: ${errMsg}`);
304
+ return ExportTraceServiceRequest.encode(ExportTraceServiceRequest.fromObject(message)).finish();
305
+ }
306
+ };
307
+ function nanoToLong(nanoStr) {
308
+ const big = BigInt(nanoStr);
309
+ const low = Number(big & 0xffffffffn);
310
+ const high = Number(big >> 32n & 0xffffffffn);
311
+ return { low, high, unsigned: true };
312
+ }
313
+
314
+ // src/opencode-plugin.ts
315
+ var DEFAULT_ENDPOINT = "https://staging-cloud.neatlogs.com";
316
+ var NeatlogsOpencodePlugin = async (_ctx) => {
317
+ const apiKey = (process.env.NEATLOGS_API_KEY ?? "").trim();
318
+ const endpoint = (process.env.NEATLOGS_ENDPOINT ?? DEFAULT_ENDPOINT).trim();
319
+ const workflowName = process.env.NEATLOGS_WORKFLOW_NAME || "opencode";
320
+ const captureSystemPrompt = String(process.env.NEATLOGS_CAPTURE_SYSTEM_PROMPT || "").toLowerCase() === "true";
321
+ const debug = String(process.env.NEATLOGS_DEBUG || "").toLowerCase() === "true";
322
+ const shipper = new TraceShipper({ apiKey, endpoint, workflowName, debug });
323
+ const sessions = /* @__PURE__ */ new Map();
324
+ function stateFor(sessionID) {
325
+ let s = sessions.get(sessionID);
326
+ if (!s) {
327
+ s = {
328
+ traceId: generateTraceId(),
329
+ toolStarts: /* @__PURE__ */ new Map(),
330
+ outputParts: /* @__PURE__ */ new Map(),
331
+ toolCalls: /* @__PURE__ */ new Map(),
332
+ processed: /* @__PURE__ */ new Set(),
333
+ currentInput: "",
334
+ lastAssistantText: ""
335
+ };
336
+ sessions.set(sessionID, s);
337
+ }
338
+ return s;
339
+ }
340
+ function startRoot(st, sessionID) {
341
+ if (st.rootSpan) return;
342
+ st.rootSpan = {
343
+ spanId: generateSpanId(),
344
+ name: "opencode.session",
345
+ startNano: nowNanoString(),
346
+ attributes: [
347
+ { key: "neatlogs.span.kind", value: { stringValue: "AGENT" } },
348
+ { key: "neatlogs.agent.framework", value: { stringValue: "opencode" } },
349
+ { key: "neatlogs.conversation.id", value: { stringValue: sessionID } }
350
+ ]
351
+ };
352
+ }
353
+ async function closeAndFlush(st, sessionID) {
354
+ if (st.rootSpan) {
355
+ const attrs = st.rootSpan.attributes.slice();
356
+ setIO(attrs, "AGENT", st.currentInput || void 0, st.lastAssistantText || void 0);
357
+ shipper.enqueue({
358
+ traceId: st.traceId,
359
+ spanId: st.rootSpan.spanId,
360
+ name: st.rootSpan.name,
361
+ kind: 1,
362
+ startTimeUnixNano: st.rootSpan.startNano,
363
+ endTimeUnixNano: nowNanoString(),
364
+ attributes: attrs,
365
+ status: { code: SpanStatusCode.OK }
366
+ });
367
+ const m = nowNanoString();
368
+ shipper.enqueue({
369
+ traceId: st.traceId,
370
+ spanId: generateSpanId(),
371
+ parentSpanId: st.rootSpan.spanId,
372
+ name: "neatlogs.trace.complete",
373
+ kind: 1,
374
+ startTimeUnixNano: m,
375
+ endTimeUnixNano: m,
376
+ attributes: [
377
+ { key: "neatlogs.trace.complete", value: { boolValue: true } },
378
+ { key: "neatlogs.internal", value: { boolValue: true } },
379
+ { key: "neatlogs.span.kind", value: { stringValue: "Neatlogs.INTERNAL" } }
380
+ ]
381
+ });
382
+ st.rootSpan = void 0;
383
+ }
384
+ await shipper.flush();
385
+ void sessionID;
386
+ }
387
+ return {
388
+ /** Fired when the user submits a prompt — open the turn's AGENT root. */
389
+ "chat.message": async (_input, output) => {
390
+ try {
391
+ const sessionID = String(_input?.sessionID ?? output?.sessionID ?? "");
392
+ if (!sessionID) return;
393
+ const st = stateFor(sessionID);
394
+ const parts = output?.parts ?? [];
395
+ const text = Array.isArray(parts) ? parts.filter((p) => p?.type === "text" && typeof p.text === "string").map((p) => p.text).join("\n") : "";
396
+ if (text) st.currentInput = text;
397
+ startRoot(st, sessionID);
398
+ } catch {
399
+ }
400
+ },
401
+ /** Capture the system prompt (opt-in). */
402
+ "experimental.chat.system.transform": async (_input, output) => {
403
+ try {
404
+ if (!captureSystemPrompt) return;
405
+ const sessionID = String(_input?.sessionID ?? "");
406
+ const parts = output?.system ?? output?.parts ?? output;
407
+ const joined = Array.isArray(parts) ? parts.map((p) => typeof p === "string" ? p : p?.text ?? "").join("\n") : String(parts ?? "");
408
+ if (sessionID && joined) stateFor(sessionID).systemPrompt = joined;
409
+ } catch {
410
+ }
411
+ },
412
+ /** Global event bus — message parts, assistant completions, session idle. */
413
+ event: async ({ event }) => {
414
+ try {
415
+ await handleEvent(shipper, sessions, stateFor, startRoot, closeAndFlush, event);
416
+ } catch {
417
+ }
418
+ },
419
+ /** Tool start — record start time + args (span built atomically in `after`). */
420
+ "tool.execute.before": async (input, output) => {
421
+ try {
422
+ const sessionID = String(input?.sessionID ?? "");
423
+ if (!sessionID) return;
424
+ const st = stateFor(sessionID);
425
+ startRoot(st, sessionID);
426
+ const callID = String(input?.callID ?? input?.tool ?? "");
427
+ st.toolStarts.set(callID, {
428
+ spanId: generateSpanId(),
429
+ startNano: nowNanoString(),
430
+ tool: String(input?.tool ?? "tool"),
431
+ args: output?.args
432
+ });
433
+ } catch {
434
+ }
435
+ },
436
+ /** Tool end — enqueue the completed TOOL span (parented to the turn root). */
437
+ "tool.execute.after": async (input, result) => {
438
+ try {
439
+ const sessionID = String(input?.sessionID ?? "");
440
+ if (!sessionID) return;
441
+ const st = stateFor(sessionID);
442
+ const callID = String(input?.callID ?? input?.tool ?? "");
443
+ const start = st.toolStarts.get(callID);
444
+ if (!start) return;
445
+ st.toolStarts.delete(callID);
446
+ const attrs = [
447
+ { key: "neatlogs.span.kind", value: { stringValue: "TOOL" } },
448
+ { key: "neatlogs.tool.name", value: { stringValue: start.tool } },
449
+ { key: "neatlogs.conversation.id", value: { stringValue: sessionID } }
450
+ ];
451
+ if (start.args !== void 0) {
452
+ setIO(attrs, "TOOL", safeStringify(start.args), void 0);
453
+ push(attrs, attrStr("neatlogs.tool.input", safeStringify(start.args)));
454
+ }
455
+ if (result?.title) push(attrs, attrStr("neatlogs.tool.title", String(result.title)));
456
+ const out = result?.output ?? result?.result;
457
+ if (out !== void 0) {
458
+ const o = typeof out === "string" ? out : safeStringify(out);
459
+ setIO(attrs, "TOOL", void 0, o);
460
+ push(attrs, attrStr("neatlogs.tool.output", o));
461
+ }
462
+ if (result?.metadata !== void 0) {
463
+ push(attrs, attrStr("neatlogs.tool.metadata", safeStringify(result.metadata)));
464
+ }
465
+ shipper.enqueue({
466
+ traceId: st.traceId,
467
+ spanId: start.spanId,
468
+ parentSpanId: st.rootSpan?.spanId,
469
+ name: `opencode.tool.${start.tool}`,
470
+ kind: 1,
471
+ startTimeUnixNano: start.startNano,
472
+ endTimeUnixNano: nowNanoString(),
473
+ attributes: attrs,
474
+ status: { code: SpanStatusCode.OK }
475
+ });
476
+ } catch {
477
+ }
478
+ }
479
+ };
480
+ };
481
+ var neatlogsOpencodePlugin = NeatlogsOpencodePlugin;
482
+ var opencode_plugin_default = NeatlogsOpencodePlugin;
483
+ function handleEvent(shipper, sessions, stateFor, startRoot, closeAndFlush, event) {
484
+ const type = event?.type;
485
+ const props = event?.properties ?? {};
486
+ if (type === "message.part.updated" || type === "message.part.completed") {
487
+ const part = props.part ?? props;
488
+ const messageID = part?.messageID ?? part?.message_id;
489
+ const sessionID = part?.sessionID ?? props.sessionID;
490
+ if (!messageID || !sessionID) return void 0;
491
+ const st = stateFor(String(sessionID));
492
+ if (part?.type === "text" && typeof part?.text === "string") {
493
+ st.outputParts.set(String(messageID), part.text);
494
+ } else if (part?.type === "tool" && part?.tool) {
495
+ const list = st.toolCalls.get(String(messageID)) ?? [];
496
+ const callID = String(part.callID ?? part.tool);
497
+ const input = part?.state?.input ?? {};
498
+ const existing = list.find((t) => t.callID === callID);
499
+ if (existing) existing.input = input;
500
+ else list.push({ name: String(part.tool), input, callID });
501
+ st.toolCalls.set(String(messageID), list);
502
+ }
503
+ return void 0;
504
+ }
505
+ if (type === "message.updated" || type === "message.completed") {
506
+ const info = props.info ?? props.message ?? props;
507
+ const sessionID = info?.sessionID ?? info?.session_id;
508
+ if (!sessionID) return void 0;
509
+ if (info?.role !== "assistant") return void 0;
510
+ const completed = info?.time?.completed ?? info?.completed;
511
+ const id = String(info?.id ?? "");
512
+ if (!completed || !id) return void 0;
513
+ const st = stateFor(String(sessionID));
514
+ if (st.processed.has(id)) return void 0;
515
+ st.processed.add(id);
516
+ startRoot(st, String(sessionID));
517
+ emitLlmSpan(shipper, st, info, String(sessionID));
518
+ return shipper.flush().catch(() => void 0);
519
+ }
520
+ if (type === "session.idle" || type === "session.deleted") {
521
+ const sessionID = props.sessionID ?? props.info?.id;
522
+ if (!sessionID) return void 0;
523
+ const st = sessions.get(String(sessionID));
524
+ if (!st) return void 0;
525
+ st.toolStarts.clear();
526
+ const p = closeAndFlush(st, String(sessionID));
527
+ if (type === "session.deleted") sessions.delete(String(sessionID));
528
+ return p;
529
+ }
530
+ return void 0;
531
+ }
532
+ function emitLlmSpan(shipper, st, info, sessionID) {
533
+ const model = info?.modelID ?? info?.model ?? "";
534
+ const provider = info?.providerID ?? info?.provider ?? "";
535
+ const attrs = [
536
+ { key: "neatlogs.span.kind", value: { stringValue: "LLM" } },
537
+ { key: "neatlogs.conversation.id", value: { stringValue: sessionID } }
538
+ ];
539
+ push(attrs, attrStr("neatlogs.llm.model_name", model ? String(model) : void 0));
540
+ push(attrs, attrStr("neatlogs.llm.provider", provider ? String(provider) : void 0));
541
+ let inIdx = 0;
542
+ if (st.systemPrompt) {
543
+ push(attrs, attrStr(`neatlogs.llm.input_messages.${inIdx}.role`, "system"));
544
+ push(attrs, attrStr(`neatlogs.llm.input_messages.${inIdx}.content`, st.systemPrompt));
545
+ inIdx++;
546
+ }
547
+ if (st.currentInput) {
548
+ push(attrs, attrStr(`neatlogs.llm.input_messages.${inIdx}.role`, "user"));
549
+ push(attrs, attrStr(`neatlogs.llm.input_messages.${inIdx}.content`, st.currentInput));
550
+ setIO(attrs, "LLM", st.currentInput, void 0);
551
+ }
552
+ const outText = st.outputParts.get(String(info?.id)) || messageText(info) || "";
553
+ const toolCalls = st.toolCalls.get(String(info?.id)) ?? [];
554
+ if (outText) {
555
+ push(attrs, attrStr("neatlogs.llm.output_messages.0.role", "assistant"));
556
+ push(attrs, attrStr("neatlogs.llm.output_messages.0.content", outText));
557
+ setIO(attrs, "LLM", void 0, outText);
558
+ st.lastAssistantText = outText;
559
+ }
560
+ if (toolCalls.length) {
561
+ toolCalls.forEach((tc, j) => {
562
+ push(attrs, attrStr(`neatlogs.llm.tool_calls.${j}.name`, tc.name));
563
+ push(attrs, attrStr(`neatlogs.llm.tool_calls.${j}.arguments`, safeStringify(tc.input ?? {})));
564
+ });
565
+ if (!outText) {
566
+ const rendered = toolCalls.map((tc) => `\u2192 ${tc.name}(${safeStringify(tc.input ?? {})})`).join("\n");
567
+ push(attrs, attrStr("neatlogs.llm.output_messages.0.role", "assistant"));
568
+ push(attrs, attrStr("neatlogs.llm.output_messages.0.content", rendered));
569
+ setIO(attrs, "LLM", void 0, rendered);
570
+ }
571
+ }
572
+ const tokens = info?.tokens;
573
+ if (tokens) {
574
+ push(attrs, attrInt("neatlogs.llm.token_count.prompt", tokens.input));
575
+ push(attrs, attrInt("neatlogs.llm.token_count.completion", tokens.output));
576
+ if (tokens.input != null && tokens.output != null) {
577
+ push(attrs, attrInt("neatlogs.llm.token_count.total", tokens.input + tokens.output));
578
+ }
579
+ push(attrs, attrInt("neatlogs.llm.token_count.reasoning", tokens.reasoning));
580
+ push(attrs, attrInt("neatlogs.llm.token_count.cache_read", tokens.cache?.read));
581
+ push(attrs, attrInt("neatlogs.llm.token_count.cache_write", tokens.cache?.write));
582
+ }
583
+ push(attrs, attrDouble("neatlogs.llm.cost_usd", info?.cost));
584
+ const createdMs = info?.time?.created;
585
+ const startNano = typeof createdMs === "number" ? msToNanoString(Math.floor(createdMs)) : nowNanoString();
586
+ shipper.enqueue({
587
+ traceId: st.traceId,
588
+ spanId: generateSpanId(),
589
+ parentSpanId: st.rootSpan?.spanId,
590
+ name: `opencode.llm.${model || "model"}`,
591
+ kind: 1,
592
+ startTimeUnixNano: startNano,
593
+ endTimeUnixNano: nowNanoString(),
594
+ attributes: attrs,
595
+ status: { code: SpanStatusCode.OK }
596
+ });
597
+ if (info?.id) {
598
+ st.outputParts.delete(String(info.id));
599
+ st.toolCalls.delete(String(info.id));
600
+ }
601
+ }
602
+ function push(arr, kv) {
603
+ if (kv) arr.push(kv);
604
+ }
605
+ function setIO(arr, kind, input, output) {
606
+ const k = kind.toLowerCase();
607
+ if (input !== void 0) {
608
+ push(arr, attrStr("input.value", input));
609
+ push(arr, attrStr("neatlogs.input.value", input));
610
+ push(arr, attrStr(`neatlogs.${k}.input`, input));
611
+ }
612
+ if (output !== void 0) {
613
+ push(arr, attrStr("output.value", output));
614
+ push(arr, attrStr("neatlogs.output.value", output));
615
+ push(arr, attrStr(`neatlogs.${k}.output`, output));
616
+ }
617
+ }
618
+ function messageText(info) {
619
+ if (!info) return "";
620
+ if (typeof info.text === "string") return info.text;
621
+ const parts = info.parts ?? info.content;
622
+ if (typeof parts === "string") return parts;
623
+ if (!Array.isArray(parts)) return "";
624
+ const out = [];
625
+ for (const p of parts) {
626
+ if (typeof p === "string") out.push(p);
627
+ else if (p && typeof p === "object" && typeof p.text === "string") out.push(p.text);
628
+ }
629
+ return out.join("");
630
+ }
631
+ function safeStringify(value) {
632
+ if (typeof value === "string") return value;
633
+ try {
634
+ return JSON.stringify(value) ?? "";
635
+ } catch {
636
+ return String(value);
637
+ }
638
+ }
639
+ export {
640
+ NeatlogsOpencodePlugin,
641
+ opencode_plugin_default as default,
642
+ neatlogsOpencodePlugin
643
+ };
644
+ //# sourceMappingURL=opencode-plugin.mjs.map