@raindrop-ai/pi-agent 0.0.10 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,7 +25,7 @@ __export(extension_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(extension_exports);
27
27
 
28
- // ../core/dist/chunk-267IXHO5.js
28
+ // ../core/dist/chunk-YUIXRQWN.js
29
29
  function getCrypto() {
30
30
  const c = globalThis.crypto;
31
31
  return c;
@@ -83,6 +83,20 @@ function base64Encode(bytes) {
83
83
  function generateId() {
84
84
  return randomUUID();
85
85
  }
86
+ var HANDOFF_ATTRIBUTE_SUFFIXES = {
87
+ mode: "raindrop.handoff.mode",
88
+ childEventId: "raindrop.handoff.childEventId",
89
+ parentEventId: "raindrop.handoff.parentEventId",
90
+ parentSpanId: "raindrop.handoff.parentSpanId",
91
+ name: "raindrop.handoff.name",
92
+ terminal: "raindrop.handoff.terminal",
93
+ agentRole: "raindrop.agent.role"
94
+ };
95
+ var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
96
+ var TRACEPARENT_HEADER = "traceparent";
97
+ var BAGGAGE_HEADER = "baggage";
98
+ var HANDOFF_HEADER = "x-raindrop-handoff";
99
+ var UNRESOLVED_CARRIER_WARNING = `[raindrop] resume: headers were supplied but no hand-off carrier was found on them (searched ${HANDOFF_HEADER}, ${TRACEPARENT_HEADER}, ${BAGGAGE_HEADER}). This run reports as an UNLINKED event, and the launcher that dispatched it will stay on "queued" forever because nothing ever references its child. Passing headers means a parent handed off, so no carrier on them is almost certainly a defect: a gateway or proxy stripping ${HANDOFF_HEADER}, a middleware overwriting ${BAGGAGE_HEADER}, a request forwarded without its headers, or a hand-built carrier whose field names do not match. Send every header the launcher's dispatch returned. Logged once per process; pass no headers for a directly-invoked sub-agent to silence it.`;
86
100
  function runWithTracingSuppressed(fn) {
87
101
  const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
88
102
  if (typeof hook !== "function") return fn();
@@ -418,19 +432,52 @@ function projectIdHeaders(projectId) {
418
432
  var SHUTDOWN_DEADLINE_MS = 1e4;
419
433
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
420
434
  function mergePatches(target, source) {
421
- var _a, _b, _c, _d, _e, _f;
435
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
422
436
  const out = { ...target, ...source };
423
437
  if (target.properties || source.properties) {
424
438
  out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
425
439
  }
440
+ if (target.usage || source.usage) {
441
+ out.usage = { ...(_c = target.usage) != null ? _c : {} };
442
+ if (((_d = source.usage) == null ? void 0 : _d.promptTokens) !== void 0) {
443
+ out.usage.promptTokens = source.usage.promptTokens;
444
+ }
445
+ if (((_e = source.usage) == null ? void 0 : _e.completionTokens) !== void 0) {
446
+ out.usage.completionTokens = source.usage.completionTokens;
447
+ }
448
+ }
449
+ if (source.error === void 0) {
450
+ out.error = target.error;
451
+ }
426
452
  if (target.featureFlags || source.featureFlags) {
427
- out.featureFlags = { ...(_c = target.featureFlags) != null ? _c : {}, ...(_d = source.featureFlags) != null ? _d : {} };
453
+ out.featureFlags = { ...(_f = target.featureFlags) != null ? _f : {}, ...(_g = source.featureFlags) != null ? _g : {} };
428
454
  }
429
455
  if (target.attachments || source.attachments) {
430
- out.attachments = [...(_e = target.attachments) != null ? _e : [], ...(_f = source.attachments) != null ? _f : []];
456
+ out.attachments = [...(_h = target.attachments) != null ? _h : [], ...(_i = source.attachments) != null ? _i : []];
431
457
  }
432
458
  return out;
433
459
  }
460
+ function isErrorLike(value) {
461
+ return value !== null && typeof value === "object";
462
+ }
463
+ function eventTelemetryProperties(patch) {
464
+ var _a, _b;
465
+ const properties = {};
466
+ if (((_a = patch.usage) == null ? void 0 : _a.promptTokens) !== void 0 && Number.isFinite(patch.usage.promptTokens)) {
467
+ properties["ai.usage.prompt_tokens"] = patch.usage.promptTokens;
468
+ }
469
+ if (((_b = patch.usage) == null ? void 0 : _b.completionTokens) !== void 0 && Number.isFinite(patch.usage.completionTokens)) {
470
+ properties["ai.usage.completion_tokens"] = patch.usage.completionTokens;
471
+ }
472
+ if (patch.error !== void 0 && patch.error !== null) {
473
+ const constructorName = patch.error instanceof Error && patch.error.constructor !== Error ? patch.error.constructor.name : void 0;
474
+ const errorType = patch.error instanceof Error ? patch.error.name && patch.error.name !== "Error" ? patch.error.name : constructorName || patch.error.name || "Error" : isErrorLike(patch.error) && typeof patch.error.name === "string" && patch.error.name.length > 0 ? patch.error.name : "Error";
475
+ const errorMessage = patch.error instanceof Error ? patch.error.message : isErrorLike(patch.error) && typeof patch.error.message === "string" ? patch.error.message : String(patch.error);
476
+ properties["error.type"] = errorType;
477
+ properties["error.message"] = errorMessage;
478
+ }
479
+ return properties;
480
+ }
434
481
  var EventShipper = class {
435
482
  constructor(opts) {
436
483
  this.buffers = /* @__PURE__ */ new Map();
@@ -679,6 +726,11 @@ var EventShipper = class {
679
726
  return;
680
727
  }
681
728
  const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
729
+ let telemetryProperties = {};
730
+ try {
731
+ telemetryProperties = eventTelemetryProperties(accumulated);
732
+ } catch (e) {
733
+ }
682
734
  const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
683
735
  const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
684
736
  const payload = {
@@ -688,12 +740,13 @@ var EventShipper = class {
688
740
  timestamp: (_i = accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
689
741
  ai_data: {
690
742
  input: accumulated.input,
691
- output: accumulated.output,
743
+ output: accumulated.error !== void 0 && accumulated.error !== null ? void 0 : accumulated.output,
692
744
  model: accumulated.model,
693
745
  convo_id: convoId
694
746
  },
695
747
  properties: {
696
748
  ...restProperties,
749
+ ...telemetryProperties,
697
750
  ...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
698
751
  $context: this.context
699
752
  },
@@ -762,6 +815,194 @@ var EventShipper = class {
762
815
  }
763
816
  }
764
817
  };
818
+ var MODEL_USAGE_ATTRIBUTES = {
819
+ providerName: "gen_ai.provider.name",
820
+ requestModel: "gen_ai.request.model",
821
+ responseModel: "gen_ai.response.model",
822
+ inputTokens: "gen_ai.usage.input_tokens",
823
+ outputTokens: "gen_ai.usage.output_tokens",
824
+ reasoningTokens: "gen_ai.usage.reasoning_tokens",
825
+ cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
826
+ cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
827
+ nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
828
+ nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
829
+ totalOutputTokens: "raindrop.usage.output_tokens.total",
830
+ reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
831
+ cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
832
+ cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
833
+ };
834
+ var MODEL_PROVIDER_NAMES = [
835
+ ["google.vertex", "google-vertex"],
836
+ ["vertex.anthropic", "google-vertex"],
837
+ ["amazon-bedrock", "amazon-bedrock"],
838
+ ["bedrock", "amazon-bedrock"],
839
+ ["anthropic", "anthropic"],
840
+ ["openai", "openai"],
841
+ ["google", "google"],
842
+ ["azure", "azure"],
843
+ ["openrouter", "openrouter"]
844
+ ];
845
+ function canonicalModelProvider(provider) {
846
+ var _a;
847
+ const normalizedProvider = provider.trim().toLowerCase();
848
+ if (normalizedProvider === "gateway") return "vercel";
849
+ const match = MODEL_PROVIDER_NAMES.find(
850
+ ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
851
+ );
852
+ return (_a = match == null ? void 0 : match[1]) != null ? _a : provider;
853
+ }
854
+ function tokenCount(value) {
855
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
856
+ }
857
+ function exclusiveTokens({
858
+ explicit,
859
+ total,
860
+ parts,
861
+ semantics
862
+ }) {
863
+ if (explicit !== void 0) return explicit;
864
+ if (total === void 0) return void 0;
865
+ if (semantics === "exclusive") return total;
866
+ const observedParts = parts.filter((part) => part !== void 0 && part > 0);
867
+ if (semantics === void 0 && observedParts.length > 0) return void 0;
868
+ const exclusive = total - observedParts.reduce((sum, part) => sum + part, 0);
869
+ return exclusive >= 0 ? exclusive : void 0;
870
+ }
871
+ function buildModelUsageAttributes(usage) {
872
+ const inputTokens = tokenCount(usage.inputTokens);
873
+ const outputTokens = tokenCount(usage.outputTokens);
874
+ const explicitNonCachedInputTokens = tokenCount(usage.nonCachedInputTokens);
875
+ const explicitNonReasoningOutputTokens = tokenCount(usage.nonReasoningOutputTokens);
876
+ const reasoningTokens = tokenCount(usage.reasoningTokens);
877
+ const cacheReadInputTokens = tokenCount(usage.cacheReadInputTokens);
878
+ const cacheWriteInputTokens = tokenCount(usage.cacheWriteInputTokens);
879
+ const nonCachedInputTokens = exclusiveTokens({
880
+ explicit: explicitNonCachedInputTokens,
881
+ total: inputTokens,
882
+ parts: [cacheReadInputTokens, cacheWriteInputTokens],
883
+ semantics: usage.inputTokenSemantics
884
+ });
885
+ const nonReasoningOutputTokens = exclusiveTokens({
886
+ explicit: explicitNonReasoningOutputTokens,
887
+ total: outputTokens,
888
+ parts: [reasoningTokens],
889
+ semantics: usage.outputTokenSemantics
890
+ });
891
+ const exclusivePoolAttributes = nonCachedInputTokens !== void 0 && nonReasoningOutputTokens !== void 0 ? [
892
+ // backend consumes this complete raindrop.* set as one atomic billing-pool contract.
893
+ attrInt(MODEL_USAGE_ATTRIBUTES.nonCachedInputTokens, nonCachedInputTokens),
894
+ attrInt(MODEL_USAGE_ATTRIBUTES.nonReasoningOutputTokens, nonReasoningOutputTokens),
895
+ attrInt(MODEL_USAGE_ATTRIBUTES.reasoningOutputTokens, reasoningTokens),
896
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadTokens, cacheReadInputTokens),
897
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteTokens, cacheWriteInputTokens)
898
+ ] : [];
899
+ return [
900
+ attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, inputTokens),
901
+ attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, outputTokens),
902
+ attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, reasoningTokens),
903
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, cacheReadInputTokens),
904
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, cacheWriteInputTokens),
905
+ ...exclusivePoolAttributes
906
+ ].filter((attribute) => attribute !== void 0);
907
+ }
908
+ var STRING_ALIASES = {
909
+ [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
910
+ };
911
+ var NUMBER_ALIASES = {
912
+ [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
913
+ "gen_ai.usage.prompt_tokens",
914
+ "ai.usage.prompt_tokens",
915
+ "ai.usage.promptTokens",
916
+ "ai.usage.input_tokens",
917
+ "ai.usage.inputTokens"
918
+ ],
919
+ [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
920
+ "gen_ai.usage.completion_tokens",
921
+ "ai.usage.completion_tokens",
922
+ "ai.usage.completionTokens",
923
+ "ai.usage.output_tokens",
924
+ "ai.usage.outputTokens"
925
+ ],
926
+ [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
927
+ "gen_ai.usage.reasoning.output_tokens",
928
+ "ai.usage.reasoningTokens",
929
+ "ai.usage.thoughts_tokens"
930
+ ],
931
+ [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
932
+ "gen_ai.usage.cache_read_tokens",
933
+ "gen_ai.usage.cache_read.input_tokens",
934
+ "ai.usage.cachedInputTokens",
935
+ "ai.usage.cached_tokens",
936
+ "ai.usage.cache_read_tokens",
937
+ "ai.usage.cache_read_input_tokens",
938
+ "ai.usage.cacheReadInputTokens"
939
+ ],
940
+ [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
941
+ "gen_ai.usage.cache_creation_input_tokens",
942
+ "gen_ai.usage.cache_creation.input_tokens",
943
+ "ai.usage.cacheWriteInputTokens",
944
+ "ai.usage.cache_creation_input_tokens",
945
+ "ai.usage.cacheCreationInputTokens"
946
+ ]
947
+ };
948
+ function hasAttribute(attributes, key) {
949
+ return attributes.some((attribute) => attribute.key === key);
950
+ }
951
+ function findStringAttribute(attributes, keys) {
952
+ return attributes.find(
953
+ (attribute) => keys.includes(attribute.key) && typeof attribute.value.stringValue === "string" && attribute.value.stringValue.length > 0
954
+ );
955
+ }
956
+ function findNumberAttribute(attributes, keys) {
957
+ return attributes.find((attribute) => {
958
+ if (!keys.includes(attribute.key)) return false;
959
+ if (typeof attribute.value.doubleValue === "number" && Number.isSafeInteger(attribute.value.doubleValue) && attribute.value.doubleValue >= 0) {
960
+ return true;
961
+ }
962
+ const intValue = attribute.value.intValue;
963
+ return integerValue(intValue) !== void 0;
964
+ });
965
+ }
966
+ function integerValue(value) {
967
+ if (typeof value === "number") {
968
+ return Number.isSafeInteger(value) && value >= 0 ? value : void 0;
969
+ }
970
+ if (typeof value !== "string") return void 0;
971
+ const parsed = Number(value);
972
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
973
+ }
974
+ function appendAlias(attributes, target, source) {
975
+ if (!source || hasAttribute(attributes, target)) return attributes;
976
+ return [...attributes, { key: target, value: { ...source.value } }];
977
+ }
978
+ function appendProviderAlias(attributes) {
979
+ if (hasAttribute(attributes, MODEL_USAGE_ATTRIBUTES.providerName)) return attributes;
980
+ const source = findStringAttribute(attributes, ["gen_ai.system", "ai.model.provider"]);
981
+ if (!(source == null ? void 0 : source.value.stringValue)) return attributes;
982
+ const attribute = attrString(
983
+ MODEL_USAGE_ATTRIBUTES.providerName,
984
+ canonicalModelProvider(source.value.stringValue)
985
+ );
986
+ return attribute ? [...attributes, attribute] : attributes;
987
+ }
988
+ function normalizeModelUsageSpan(span) {
989
+ const original = span.attributes;
990
+ if (!original || original.length === 0) return span;
991
+ const responseModel = findStringAttribute(original, [
992
+ MODEL_USAGE_ATTRIBUTES.responseModel,
993
+ "ai.response.model"
994
+ ]);
995
+ if (!responseModel) return span;
996
+ let attributes = appendAlias(original, MODEL_USAGE_ATTRIBUTES.responseModel, responseModel);
997
+ attributes = appendProviderAlias(attributes);
998
+ for (const [target, aliases] of Object.entries(STRING_ALIASES)) {
999
+ attributes = appendAlias(attributes, target, findStringAttribute(attributes, aliases));
1000
+ }
1001
+ for (const [target, aliases] of Object.entries(NUMBER_ALIASES)) {
1002
+ attributes = appendAlias(attributes, target, findNumberAttribute(attributes, aliases));
1003
+ }
1004
+ return attributes === original ? span : { ...span, attributes };
1005
+ }
765
1006
  var DEFAULT_SECRET_KEY_NAMES = [
766
1007
  "apikey",
767
1008
  "apisecret",
@@ -851,6 +1092,13 @@ function redactJsonAttributeValue(key, value) {
851
1092
  if (scrubbedJson === json) return void 0;
852
1093
  return { stringValue: scrubbedJson };
853
1094
  }
1095
+ function identityAttrs(args) {
1096
+ return [
1097
+ attrString("ai.telemetry.metadata.raindrop.ai.userId", args.userId),
1098
+ attrString("ai.telemetry.metadata.raindrop.convoId", args.convoId),
1099
+ attrString("ai.telemetry.metadata.raindrop.eventName", args.eventName)
1100
+ ];
1101
+ }
854
1102
  function applyOtelSpanAttributeLimit(limit) {
855
1103
  var _a, _b;
856
1104
  try {
@@ -955,6 +1203,10 @@ var TraceShipper = class {
955
1203
  return null;
956
1204
  }
957
1205
  }
1206
+ try {
1207
+ current = normalizeModelUsageSpan(current);
1208
+ } catch (e) {
1209
+ }
958
1210
  if (!this.disableDefaultRedaction) {
959
1211
  current = defaultTransformSpan(current);
960
1212
  }
@@ -975,6 +1227,7 @@ var TraceShipper = class {
975
1227
  const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
976
1228
  const attrs = [
977
1229
  attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1230
+ ...identityAttrs(args),
978
1231
  attrString("ai.operationId", args.operationId)
979
1232
  ];
980
1233
  if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
@@ -1030,7 +1283,8 @@ var TraceShipper = class {
1030
1283
  var _a;
1031
1284
  const ids = createSpanIds(args.parent);
1032
1285
  const attrs = [
1033
- attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId)
1286
+ attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1287
+ ...identityAttrs(args)
1034
1288
  ];
1035
1289
  if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
1036
1290
  const otlp = buildOtlpSpan({
@@ -1253,7 +1507,7 @@ function resolveLocalWorkshopUrl(fileValue) {
1253
1507
  // package.json
1254
1508
  var package_default = {
1255
1509
  name: "@raindrop-ai/pi-agent",
1256
- version: "0.0.10",
1510
+ version: "0.1.1",
1257
1511
  description: "Raindrop observability for Pi Agent \u2014 automatic tracing via subscriber or pi-coding-agent extension",
1258
1512
  type: "module",
1259
1513
  license: "MIT",
@@ -1379,6 +1633,18 @@ var TraceShipper2 = class extends TraceShipper {
1379
1633
 
1380
1634
  // src/internal/helpers.ts
1381
1635
  var import_node_os2 = require("os");
1636
+ function modelSpendSpanAttributes(input) {
1637
+ const providerAttr = input.provider ? attrString("gen_ai.provider.name", canonicalModelProvider(input.provider)) : void 0;
1638
+ const usageAttrs = input.usage ? buildModelUsageAttributes({
1639
+ inputTokens: input.usage.input,
1640
+ outputTokens: input.usage.output,
1641
+ cacheReadInputTokens: input.usage.cacheRead,
1642
+ cacheWriteInputTokens: input.usage.cacheWrite,
1643
+ reasoningTokens: 0,
1644
+ inputTokenSemantics: "exclusive"
1645
+ }) : [];
1646
+ return providerAttr ? [providerAttr, ...usageAttrs] : usageAttrs;
1647
+ }
1382
1648
  function extractAssistantText(message) {
1383
1649
  if (!("role" in message) || message.role !== "assistant") return void 0;
1384
1650
  if (!Array.isArray(message.content)) return void 0;
@@ -1578,7 +1844,9 @@ function getAssistantError(message) {
1578
1844
  var _a;
1579
1845
  if (message.role !== "assistant") return void 0;
1580
1846
  if (message.stopReason !== "error" && message.stopReason !== "aborted") return void 0;
1581
- return (_a = message.errorMessage) != null ? _a : `Assistant ${message.stopReason}`;
1847
+ const error = new Error((_a = message.errorMessage) != null ? _a : `Assistant ${message.stopReason}`);
1848
+ error.name = message.stopReason === "aborted" ? "AbortError" : "PiAgentError";
1849
+ return error;
1582
1850
  }
1583
1851
  function getState(stateRef, ctx) {
1584
1852
  const sessionId = ctx.sessionManager.getSessionId();
@@ -1705,7 +1973,7 @@ function registerTracing(pi, config, eventShipper, traceShipper) {
1705
1973
  }
1706
1974
  });
1707
1975
  pi.on("message_end", async (event, ctx) => {
1708
- var _a, _b, _c, _d, _e, _f, _g, _h;
1976
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1709
1977
  try {
1710
1978
  if (((_a = event.message) == null ? void 0 : _a.role) !== "assistant") return;
1711
1979
  const state = getState(stateRef, ctx);
@@ -1719,19 +1987,26 @@ function registerTracing(pi, config, eventShipper, traceShipper) {
1719
1987
  const outputText = capText2(getAssistantText(message));
1720
1988
  const inputTokens = (_d = message.usage) == null ? void 0 : _d.input;
1721
1989
  const outputTokens = (_e = message.usage) == null ? void 0 : _e.output;
1722
- const cacheReadTokens = (_f = message.usage) == null ? void 0 : _f.cacheRead;
1990
+ const cacheReadTokens = typeof ((_f = message.usage) == null ? void 0 : _f.cacheRead) === "number" ? message.usage.cacheRead : 0;
1991
+ const cacheWriteTokens = typeof ((_g = message.usage) == null ? void 0 : _g.cacheWrite) === "number" ? message.usage.cacheWrite : 0;
1723
1992
  if (typeof inputTokens === "number") state.totalInputTokens += inputTokens;
1724
1993
  if (typeof outputTokens === "number") state.totalOutputTokens += outputTokens;
1725
- if (typeof cacheReadTokens === "number" && cacheReadTokens > 0) state.totalCacheReadTokens += cacheReadTokens;
1726
- const turnParent = (_g = state.currentTurnSpan) != null ? _g : state.currentRootSpan;
1994
+ if (cacheReadTokens > 0) state.totalCacheReadTokens += cacheReadTokens;
1995
+ const turnParent = (_h = state.currentTurnSpan) != null ? _h : state.currentRootSpan;
1727
1996
  const llmAttrs = [
1728
1997
  attrString("ai.operationId", "generateText"),
1729
1998
  provider ? attrString("gen_ai.system", provider) : void 0,
1730
1999
  modelId ? attrString("gen_ai.request.model", modelId) : void 0,
1731
2000
  modelId ? attrString("gen_ai.response.model", modelId) : void 0,
1732
- typeof inputTokens === "number" ? attrInt("gen_ai.usage.input_tokens", inputTokens) : void 0,
1733
- typeof outputTokens === "number" ? attrInt("gen_ai.usage.output_tokens", outputTokens) : void 0,
1734
- typeof cacheReadTokens === "number" && cacheReadTokens > 0 ? attrInt("gen_ai.usage.cache_read_tokens", cacheReadTokens) : void 0,
2001
+ ...modelSpendSpanAttributes({
2002
+ provider: provider || void 0,
2003
+ usage: typeof inputTokens === "number" && typeof outputTokens === "number" ? {
2004
+ input: inputTokens,
2005
+ output: outputTokens,
2006
+ cacheRead: cacheReadTokens,
2007
+ cacheWrite: cacheWriteTokens
2008
+ } : void 0
2009
+ }),
1735
2010
  outputText ? attrString("ai.response.text", truncate(outputText)) : void 0,
1736
2011
  state.currentInput ? attrString("ai.prompt", truncate(state.currentInput)) : void 0,
1737
2012
  message.stopReason ? attrString("ai.stop_reason", message.stopReason) : void 0,
@@ -1769,16 +2044,19 @@ function registerTracing(pi, config, eventShipper, traceShipper) {
1769
2044
  ...errorForSpan ? { error: errorForSpan } : {}
1770
2045
  });
1771
2046
  state.currentRootSpan = void 0;
1772
- await eventShipper.finish((_h = state.currentEventRequestId) != null ? _h : state.currentEventId, {
2047
+ await eventShipper.finish((_i = state.currentEventRequestId) != null ? _i : state.currentEventId, {
1773
2048
  userId: getUserId(state, config.eventMetadata),
1774
2049
  ...modelId ? { model: modelId } : {},
1775
- ...outputText.trim() ? { output: outputText } : {},
2050
+ ...!errorForSpan && outputText.trim() ? { output: outputText } : {},
2051
+ usage: {
2052
+ promptTokens: state.totalInputTokens > 0 ? state.totalInputTokens : void 0,
2053
+ completionTokens: state.totalOutputTokens > 0 ? state.totalOutputTokens : void 0
2054
+ },
2055
+ error: errorForSpan,
1776
2056
  properties: {
1777
2057
  sdk_version: libraryVersion,
1778
2058
  stop_reason: message.stopReason,
1779
2059
  ...provider ? { "ai.provider": provider } : {},
1780
- ...state.totalInputTokens > 0 ? { "ai.usage.prompt_tokens": state.totalInputTokens } : {},
1781
- ...state.totalOutputTokens > 0 ? { "ai.usage.completion_tokens": state.totalOutputTokens } : {},
1782
2060
  ...state.totalCacheReadTokens > 0 ? { "ai.usage.cache_read_tokens": state.totalCacheReadTokens } : {}
1783
2061
  }
1784
2062
  });
@@ -1,5 +1,5 @@
1
1
  import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
2
- import { E as EventShipper$1, T as TraceShipper$1, O as OtlpSpan } from './index.d-DBLTz2Nz.cjs';
2
+ import { E as EventShipper$1, T as TraceShipper$1, O as OtlpSpan } from './index.d-DPYV95P7.cjs';
3
3
 
4
4
  interface EventMetadata {
5
5
  userId?: string;
@@ -1,5 +1,5 @@
1
1
  import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
2
- import { E as EventShipper$1, T as TraceShipper$1, O as OtlpSpan } from './index.d-DBLTz2Nz.js';
2
+ import { E as EventShipper$1, T as TraceShipper$1, O as OtlpSpan } from './index.d-DPYV95P7.js';
3
3
 
4
4
  interface EventMetadata {
5
5
  userId?: string;
package/dist/extension.js CHANGED
@@ -10,11 +10,12 @@ import {
10
10
  getHostname,
11
11
  getUsername,
12
12
  libraryVersion,
13
+ modelSpendSpanAttributes,
13
14
  nowUnixNanoString,
14
15
  resolveLocalDebuggerBaseUrl,
15
16
  safeStringify,
16
17
  truncate
17
- } from "./chunk-GXL5WZE5.js";
18
+ } from "./chunk-GTKXT2J5.js";
18
19
 
19
20
  // src/internal/config.ts
20
21
  import { existsSync, readFileSync } from "fs";
@@ -141,7 +142,9 @@ function getAssistantError(message) {
141
142
  var _a;
142
143
  if (message.role !== "assistant") return void 0;
143
144
  if (message.stopReason !== "error" && message.stopReason !== "aborted") return void 0;
144
- return (_a = message.errorMessage) != null ? _a : `Assistant ${message.stopReason}`;
145
+ const error = new Error((_a = message.errorMessage) != null ? _a : `Assistant ${message.stopReason}`);
146
+ error.name = message.stopReason === "aborted" ? "AbortError" : "PiAgentError";
147
+ return error;
145
148
  }
146
149
  function getState(stateRef, ctx) {
147
150
  const sessionId = ctx.sessionManager.getSessionId();
@@ -268,7 +271,7 @@ function registerTracing(pi, config, eventShipper, traceShipper) {
268
271
  }
269
272
  });
270
273
  pi.on("message_end", async (event, ctx) => {
271
- var _a, _b, _c, _d, _e, _f, _g, _h;
274
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
272
275
  try {
273
276
  if (((_a = event.message) == null ? void 0 : _a.role) !== "assistant") return;
274
277
  const state = getState(stateRef, ctx);
@@ -282,19 +285,26 @@ function registerTracing(pi, config, eventShipper, traceShipper) {
282
285
  const outputText = capText(getAssistantText(message));
283
286
  const inputTokens = (_d = message.usage) == null ? void 0 : _d.input;
284
287
  const outputTokens = (_e = message.usage) == null ? void 0 : _e.output;
285
- const cacheReadTokens = (_f = message.usage) == null ? void 0 : _f.cacheRead;
288
+ const cacheReadTokens = typeof ((_f = message.usage) == null ? void 0 : _f.cacheRead) === "number" ? message.usage.cacheRead : 0;
289
+ const cacheWriteTokens = typeof ((_g = message.usage) == null ? void 0 : _g.cacheWrite) === "number" ? message.usage.cacheWrite : 0;
286
290
  if (typeof inputTokens === "number") state.totalInputTokens += inputTokens;
287
291
  if (typeof outputTokens === "number") state.totalOutputTokens += outputTokens;
288
- if (typeof cacheReadTokens === "number" && cacheReadTokens > 0) state.totalCacheReadTokens += cacheReadTokens;
289
- const turnParent = (_g = state.currentTurnSpan) != null ? _g : state.currentRootSpan;
292
+ if (cacheReadTokens > 0) state.totalCacheReadTokens += cacheReadTokens;
293
+ const turnParent = (_h = state.currentTurnSpan) != null ? _h : state.currentRootSpan;
290
294
  const llmAttrs = [
291
295
  attrString("ai.operationId", "generateText"),
292
296
  provider ? attrString("gen_ai.system", provider) : void 0,
293
297
  modelId ? attrString("gen_ai.request.model", modelId) : void 0,
294
298
  modelId ? attrString("gen_ai.response.model", modelId) : void 0,
295
- typeof inputTokens === "number" ? attrInt("gen_ai.usage.input_tokens", inputTokens) : void 0,
296
- typeof outputTokens === "number" ? attrInt("gen_ai.usage.output_tokens", outputTokens) : void 0,
297
- typeof cacheReadTokens === "number" && cacheReadTokens > 0 ? attrInt("gen_ai.usage.cache_read_tokens", cacheReadTokens) : void 0,
299
+ ...modelSpendSpanAttributes({
300
+ provider: provider || void 0,
301
+ usage: typeof inputTokens === "number" && typeof outputTokens === "number" ? {
302
+ input: inputTokens,
303
+ output: outputTokens,
304
+ cacheRead: cacheReadTokens,
305
+ cacheWrite: cacheWriteTokens
306
+ } : void 0
307
+ }),
298
308
  outputText ? attrString("ai.response.text", truncate(outputText)) : void 0,
299
309
  state.currentInput ? attrString("ai.prompt", truncate(state.currentInput)) : void 0,
300
310
  message.stopReason ? attrString("ai.stop_reason", message.stopReason) : void 0,
@@ -332,16 +342,19 @@ function registerTracing(pi, config, eventShipper, traceShipper) {
332
342
  ...errorForSpan ? { error: errorForSpan } : {}
333
343
  });
334
344
  state.currentRootSpan = void 0;
335
- await eventShipper.finish((_h = state.currentEventRequestId) != null ? _h : state.currentEventId, {
345
+ await eventShipper.finish((_i = state.currentEventRequestId) != null ? _i : state.currentEventId, {
336
346
  userId: getUserId(state, config.eventMetadata),
337
347
  ...modelId ? { model: modelId } : {},
338
- ...outputText.trim() ? { output: outputText } : {},
348
+ ...!errorForSpan && outputText.trim() ? { output: outputText } : {},
349
+ usage: {
350
+ promptTokens: state.totalInputTokens > 0 ? state.totalInputTokens : void 0,
351
+ completionTokens: state.totalOutputTokens > 0 ? state.totalOutputTokens : void 0
352
+ },
353
+ error: errorForSpan,
339
354
  properties: {
340
355
  sdk_version: libraryVersion,
341
356
  stop_reason: message.stopReason,
342
357
  ...provider ? { "ai.provider": provider } : {},
343
- ...state.totalInputTokens > 0 ? { "ai.usage.prompt_tokens": state.totalInputTokens } : {},
344
- ...state.totalOutputTokens > 0 ? { "ai.usage.completion_tokens": state.totalOutputTokens } : {},
345
358
  ...state.totalCacheReadTokens > 0 ? { "ai.usage.cache_read_tokens": state.totalCacheReadTokens } : {}
346
359
  }
347
360
  });