@raindrop-ai/pi-agent 0.0.8 → 0.0.10

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.
@@ -1,4 +1,4 @@
1
- // ../core/dist/chunk-SK6EJEO7.js
1
+ // ../core/dist/chunk-267IXHO5.js
2
2
  function getCrypto() {
3
3
  const c = globalThis.crypto;
4
4
  return c;
@@ -54,7 +54,21 @@ function base64Encode(bytes) {
54
54
  return out;
55
55
  }
56
56
  function generateId() {
57
- return base64Encode(randomBytes(12)).replace(/[+/=]/g, "").slice(0, 16);
57
+ return randomUUID();
58
+ }
59
+ function runWithTracingSuppressed(fn) {
60
+ const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
61
+ if (typeof hook !== "function") return fn();
62
+ let started = false;
63
+ try {
64
+ return hook(() => {
65
+ started = true;
66
+ return fn();
67
+ });
68
+ } catch (err) {
69
+ if (started) throw err;
70
+ return fn();
71
+ }
58
72
  }
59
73
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
60
74
  var MAX_RETRY_DELAY_MS = 3e4;
@@ -166,15 +180,17 @@ async function postJson(url, body, headers, opts) {
166
180
  const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
167
181
  await withRetry(
168
182
  async () => {
169
- const resp = await fetch(url, {
170
- method: "POST",
171
- headers: {
172
- "Content-Type": "application/json",
173
- ...headers
174
- },
175
- body: JSON.stringify(body),
176
- signal: AbortSignal.timeout(timeoutMs)
177
- });
183
+ const resp = await runWithTracingSuppressed(
184
+ () => fetch(url, {
185
+ method: "POST",
186
+ headers: {
187
+ "Content-Type": "application/json",
188
+ ...headers
189
+ },
190
+ body: JSON.stringify(body),
191
+ signal: AbortSignal.timeout(timeoutMs)
192
+ })
193
+ );
178
194
  if (!resp.ok) {
179
195
  const text = await resp.text().catch(() => "");
180
196
  const err = new Error(
@@ -375,13 +391,16 @@ function projectIdHeaders(projectId) {
375
391
  var SHUTDOWN_DEADLINE_MS = 1e4;
376
392
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
377
393
  function mergePatches(target, source) {
378
- var _a, _b, _c, _d;
394
+ var _a, _b, _c, _d, _e, _f;
379
395
  const out = { ...target, ...source };
380
396
  if (target.properties || source.properties) {
381
397
  out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
382
398
  }
399
+ if (target.featureFlags || source.featureFlags) {
400
+ out.featureFlags = { ...(_c = target.featureFlags) != null ? _c : {}, ...(_d = source.featureFlags) != null ? _d : {} };
401
+ }
383
402
  if (target.attachments || source.attachments) {
384
- out.attachments = [...(_c = target.attachments) != null ? _c : [], ...(_d = source.attachments) != null ? _d : []];
403
+ out.attachments = [...(_e = target.attachments) != null ? _e : [], ...(_f = source.attachments) != null ? _f : []];
385
404
  }
386
405
  return out;
387
406
  }
@@ -651,6 +670,7 @@ var EventShipper = class {
651
670
  ...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
652
671
  $context: this.context
653
672
  },
673
+ ...accumulated.featureFlags && Object.keys(accumulated.featureFlags).length > 0 ? { feature_flags: accumulated.featureFlags } : {},
654
674
  attachments: accumulated.attachments,
655
675
  is_pending: isPending
656
676
  };
@@ -1122,11 +1142,36 @@ var TraceShipper = class {
1122
1142
  // ../core/dist/index.node.js
1123
1143
  import { AsyncLocalStorage } from "async_hooks";
1124
1144
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
1145
+ var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1146
+ "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
1147
+ );
1148
+ function findOtelContextManager() {
1149
+ var _a;
1150
+ for (const sym of Object.getOwnPropertySymbols(globalThis)) {
1151
+ if (!((_a = sym.description) == null ? void 0 : _a.startsWith("opentelemetry.js.api."))) continue;
1152
+ const api = globalThis[sym];
1153
+ const cm = api == null ? void 0 : api.context;
1154
+ if (cm && typeof cm.with === "function" && typeof cm.active === "function") {
1155
+ return cm;
1156
+ }
1157
+ }
1158
+ return void 0;
1159
+ }
1160
+ function installTracingSuppressionHook() {
1161
+ if (typeof globalThis.RAINDROP_SUPPRESS_TRACING === "function") return;
1162
+ const hook = (fn) => {
1163
+ const cm = findOtelContextManager();
1164
+ if (!cm) return fn();
1165
+ return cm.with(cm.active().setValue(SUPPRESS_TRACING_KEY, true), fn);
1166
+ };
1167
+ globalThis.RAINDROP_SUPPRESS_TRACING = hook;
1168
+ }
1169
+ installTracingSuppressionHook();
1125
1170
 
1126
1171
  // package.json
1127
1172
  var package_default = {
1128
1173
  name: "@raindrop-ai/pi-agent",
1129
- version: "0.0.8",
1174
+ version: "0.0.10",
1130
1175
  description: "Raindrop observability for Pi Agent \u2014 automatic tracing via subscriber or pi-coding-agent extension",
1131
1176
  type: "module",
1132
1177
  license: "MIT",
@@ -25,7 +25,7 @@ __export(extension_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(extension_exports);
27
27
 
28
- // ../core/dist/chunk-SK6EJEO7.js
28
+ // ../core/dist/chunk-267IXHO5.js
29
29
  function getCrypto() {
30
30
  const c = globalThis.crypto;
31
31
  return c;
@@ -40,6 +40,17 @@ function randomBytes(length) {
40
40
  for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
41
41
  return out;
42
42
  }
43
+ function randomUUID() {
44
+ const cryptoObj = getCrypto();
45
+ if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
46
+ return cryptoObj.randomUUID();
47
+ }
48
+ const b = randomBytes(16);
49
+ b[6] = b[6] & 15 | 64;
50
+ b[8] = b[8] & 63 | 128;
51
+ const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
52
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
53
+ }
43
54
  function base64Encode(bytes) {
44
55
  const maybeBuffer = globalThis.Buffer;
45
56
  if (maybeBuffer) {
@@ -70,7 +81,21 @@ function base64Encode(bytes) {
70
81
  return out;
71
82
  }
72
83
  function generateId() {
73
- return base64Encode(randomBytes(12)).replace(/[+/=]/g, "").slice(0, 16);
84
+ return randomUUID();
85
+ }
86
+ function runWithTracingSuppressed(fn) {
87
+ const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
88
+ if (typeof hook !== "function") return fn();
89
+ let started = false;
90
+ try {
91
+ return hook(() => {
92
+ started = true;
93
+ return fn();
94
+ });
95
+ } catch (err) {
96
+ if (started) throw err;
97
+ return fn();
98
+ }
74
99
  }
75
100
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
76
101
  var MAX_RETRY_DELAY_MS = 3e4;
@@ -182,15 +207,17 @@ async function postJson(url, body, headers, opts) {
182
207
  const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
183
208
  await withRetry(
184
209
  async () => {
185
- const resp = await fetch(url, {
186
- method: "POST",
187
- headers: {
188
- "Content-Type": "application/json",
189
- ...headers
190
- },
191
- body: JSON.stringify(body),
192
- signal: AbortSignal.timeout(timeoutMs)
193
- });
210
+ const resp = await runWithTracingSuppressed(
211
+ () => fetch(url, {
212
+ method: "POST",
213
+ headers: {
214
+ "Content-Type": "application/json",
215
+ ...headers
216
+ },
217
+ body: JSON.stringify(body),
218
+ signal: AbortSignal.timeout(timeoutMs)
219
+ })
220
+ );
194
221
  if (!resp.ok) {
195
222
  const text = await resp.text().catch(() => "");
196
223
  const err = new Error(
@@ -391,13 +418,16 @@ function projectIdHeaders(projectId) {
391
418
  var SHUTDOWN_DEADLINE_MS = 1e4;
392
419
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
393
420
  function mergePatches(target, source) {
394
- var _a, _b, _c, _d;
421
+ var _a, _b, _c, _d, _e, _f;
395
422
  const out = { ...target, ...source };
396
423
  if (target.properties || source.properties) {
397
424
  out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
398
425
  }
426
+ if (target.featureFlags || source.featureFlags) {
427
+ out.featureFlags = { ...(_c = target.featureFlags) != null ? _c : {}, ...(_d = source.featureFlags) != null ? _d : {} };
428
+ }
399
429
  if (target.attachments || source.attachments) {
400
- out.attachments = [...(_c = target.attachments) != null ? _c : [], ...(_d = source.attachments) != null ? _d : []];
430
+ out.attachments = [...(_e = target.attachments) != null ? _e : [], ...(_f = source.attachments) != null ? _f : []];
401
431
  }
402
432
  return out;
403
433
  }
@@ -667,6 +697,7 @@ var EventShipper = class {
667
697
  ...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
668
698
  $context: this.context
669
699
  },
700
+ ...accumulated.featureFlags && Object.keys(accumulated.featureFlags).length > 0 ? { feature_flags: accumulated.featureFlags } : {},
670
701
  attachments: accumulated.attachments,
671
702
  is_pending: isPending
672
703
  };
@@ -1138,6 +1169,31 @@ var TraceShipper = class {
1138
1169
  // ../core/dist/index.node.js
1139
1170
  var import_async_hooks = require("async_hooks");
1140
1171
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
1172
+ var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1173
+ "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
1174
+ );
1175
+ function findOtelContextManager() {
1176
+ var _a;
1177
+ for (const sym of Object.getOwnPropertySymbols(globalThis)) {
1178
+ if (!((_a = sym.description) == null ? void 0 : _a.startsWith("opentelemetry.js.api."))) continue;
1179
+ const api = globalThis[sym];
1180
+ const cm = api == null ? void 0 : api.context;
1181
+ if (cm && typeof cm.with === "function" && typeof cm.active === "function") {
1182
+ return cm;
1183
+ }
1184
+ }
1185
+ return void 0;
1186
+ }
1187
+ function installTracingSuppressionHook() {
1188
+ if (typeof globalThis.RAINDROP_SUPPRESS_TRACING === "function") return;
1189
+ const hook = (fn) => {
1190
+ const cm = findOtelContextManager();
1191
+ if (!cm) return fn();
1192
+ return cm.with(cm.active().setValue(SUPPRESS_TRACING_KEY, true), fn);
1193
+ };
1194
+ globalThis.RAINDROP_SUPPRESS_TRACING = hook;
1195
+ }
1196
+ installTracingSuppressionHook();
1141
1197
 
1142
1198
  // src/internal/config.ts
1143
1199
  var import_node_fs = require("fs");
@@ -1197,7 +1253,7 @@ function resolveLocalWorkshopUrl(fileValue) {
1197
1253
  // package.json
1198
1254
  var package_default = {
1199
1255
  name: "@raindrop-ai/pi-agent",
1200
- version: "0.0.8",
1256
+ version: "0.0.10",
1201
1257
  description: "Raindrop observability for Pi Agent \u2014 automatic tracing via subscriber or pi-coding-agent extension",
1202
1258
  type: "module",
1203
1259
  license: "MIT",
@@ -1715,11 +1771,12 @@ function registerTracing(pi, config, eventShipper, traceShipper) {
1715
1771
  state.currentRootSpan = void 0;
1716
1772
  await eventShipper.finish((_h = state.currentEventRequestId) != null ? _h : state.currentEventId, {
1717
1773
  userId: getUserId(state, config.eventMetadata),
1718
- model: modelName,
1774
+ ...modelId ? { model: modelId } : {},
1719
1775
  ...outputText.trim() ? { output: outputText } : {},
1720
1776
  properties: {
1721
1777
  sdk_version: libraryVersion,
1722
1778
  stop_reason: message.stopReason,
1779
+ ...provider ? { "ai.provider": provider } : {},
1723
1780
  ...state.totalInputTokens > 0 ? { "ai.usage.prompt_tokens": state.totalInputTokens } : {},
1724
1781
  ...state.totalOutputTokens > 0 ? { "ai.usage.completion_tokens": state.totalOutputTokens } : {},
1725
1782
  ...state.totalCacheReadTokens > 0 ? { "ai.usage.cache_read_tokens": state.totalCacheReadTokens } : {}
@@ -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-D0J2tEXx.cjs';
2
+ import { E as EventShipper$1, T as TraceShipper$1, O as OtlpSpan } from './index.d-DBLTz2Nz.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-D0J2tEXx.js';
2
+ import { E as EventShipper$1, T as TraceShipper$1, O as OtlpSpan } from './index.d-DBLTz2Nz.js';
3
3
 
4
4
  interface EventMetadata {
5
5
  userId?: string;
package/dist/extension.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  resolveLocalDebuggerBaseUrl,
15
15
  safeStringify,
16
16
  truncate
17
- } from "./chunk-C4YB7RUR.js";
17
+ } from "./chunk-GXL5WZE5.js";
18
18
 
19
19
  // src/internal/config.ts
20
20
  import { existsSync, readFileSync } from "fs";
@@ -334,11 +334,12 @@ function registerTracing(pi, config, eventShipper, traceShipper) {
334
334
  state.currentRootSpan = void 0;
335
335
  await eventShipper.finish((_h = state.currentEventRequestId) != null ? _h : state.currentEventId, {
336
336
  userId: getUserId(state, config.eventMetadata),
337
- model: modelName,
337
+ ...modelId ? { model: modelId } : {},
338
338
  ...outputText.trim() ? { output: outputText } : {},
339
339
  properties: {
340
340
  sdk_version: libraryVersion,
341
341
  stop_reason: message.stopReason,
342
+ ...provider ? { "ai.provider": provider } : {},
342
343
  ...state.totalInputTokens > 0 ? { "ai.usage.prompt_tokens": state.totalInputTokens } : {},
343
344
  ...state.totalOutputTokens > 0 ? { "ai.usage.completion_tokens": state.totalOutputTokens } : {},
344
345
  ...state.totalCacheReadTokens > 0 ? { "ai.usage.cache_read_tokens": state.totalCacheReadTokens } : {}
package/dist/index.cjs CHANGED
@@ -24,7 +24,7 @@ __export(index_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(index_exports);
26
26
 
27
- // ../core/dist/chunk-SK6EJEO7.js
27
+ // ../core/dist/chunk-267IXHO5.js
28
28
  function getCrypto() {
29
29
  const c = globalThis.crypto;
30
30
  return c;
@@ -79,6 +79,20 @@ function base64Encode(bytes) {
79
79
  }
80
80
  return out;
81
81
  }
82
+ function runWithTracingSuppressed(fn) {
83
+ const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
84
+ if (typeof hook !== "function") return fn();
85
+ let started = false;
86
+ try {
87
+ return hook(() => {
88
+ started = true;
89
+ return fn();
90
+ });
91
+ } catch (err) {
92
+ if (started) throw err;
93
+ return fn();
94
+ }
95
+ }
82
96
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
83
97
  var MAX_RETRY_DELAY_MS = 3e4;
84
98
  function wait(ms) {
@@ -189,15 +203,17 @@ async function postJson(url, body, headers, opts) {
189
203
  const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
190
204
  await withRetry(
191
205
  async () => {
192
- const resp = await fetch(url, {
193
- method: "POST",
194
- headers: {
195
- "Content-Type": "application/json",
196
- ...headers
197
- },
198
- body: JSON.stringify(body),
199
- signal: AbortSignal.timeout(timeoutMs)
200
- });
206
+ const resp = await runWithTracingSuppressed(
207
+ () => fetch(url, {
208
+ method: "POST",
209
+ headers: {
210
+ "Content-Type": "application/json",
211
+ ...headers
212
+ },
213
+ body: JSON.stringify(body),
214
+ signal: AbortSignal.timeout(timeoutMs)
215
+ })
216
+ );
201
217
  if (!resp.ok) {
202
218
  const text = await resp.text().catch(() => "");
203
219
  const err = new Error(
@@ -398,13 +414,16 @@ function projectIdHeaders(projectId) {
398
414
  var SHUTDOWN_DEADLINE_MS = 1e4;
399
415
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
400
416
  function mergePatches(target, source) {
401
- var _a, _b, _c, _d;
417
+ var _a, _b, _c, _d, _e, _f;
402
418
  const out = { ...target, ...source };
403
419
  if (target.properties || source.properties) {
404
420
  out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
405
421
  }
422
+ if (target.featureFlags || source.featureFlags) {
423
+ out.featureFlags = { ...(_c = target.featureFlags) != null ? _c : {}, ...(_d = source.featureFlags) != null ? _d : {} };
424
+ }
406
425
  if (target.attachments || source.attachments) {
407
- out.attachments = [...(_c = target.attachments) != null ? _c : [], ...(_d = source.attachments) != null ? _d : []];
426
+ out.attachments = [...(_e = target.attachments) != null ? _e : [], ...(_f = source.attachments) != null ? _f : []];
408
427
  }
409
428
  return out;
410
429
  }
@@ -674,6 +693,7 @@ var EventShipper = class {
674
693
  ...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
675
694
  $context: this.context
676
695
  },
696
+ ...accumulated.featureFlags && Object.keys(accumulated.featureFlags).length > 0 ? { feature_flags: accumulated.featureFlags } : {},
677
697
  attachments: accumulated.attachments,
678
698
  is_pending: isPending
679
699
  };
@@ -1145,11 +1165,36 @@ var TraceShipper = class {
1145
1165
  // ../core/dist/index.node.js
1146
1166
  var import_async_hooks = require("async_hooks");
1147
1167
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
1168
+ var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1169
+ "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
1170
+ );
1171
+ function findOtelContextManager() {
1172
+ var _a;
1173
+ for (const sym of Object.getOwnPropertySymbols(globalThis)) {
1174
+ if (!((_a = sym.description) == null ? void 0 : _a.startsWith("opentelemetry.js.api."))) continue;
1175
+ const api = globalThis[sym];
1176
+ const cm = api == null ? void 0 : api.context;
1177
+ if (cm && typeof cm.with === "function" && typeof cm.active === "function") {
1178
+ return cm;
1179
+ }
1180
+ }
1181
+ return void 0;
1182
+ }
1183
+ function installTracingSuppressionHook() {
1184
+ if (typeof globalThis.RAINDROP_SUPPRESS_TRACING === "function") return;
1185
+ const hook = (fn) => {
1186
+ const cm = findOtelContextManager();
1187
+ if (!cm) return fn();
1188
+ return cm.with(cm.active().setValue(SUPPRESS_TRACING_KEY, true), fn);
1189
+ };
1190
+ globalThis.RAINDROP_SUPPRESS_TRACING = hook;
1191
+ }
1192
+ installTracingSuppressionHook();
1148
1193
 
1149
1194
  // package.json
1150
1195
  var package_default = {
1151
1196
  name: "@raindrop-ai/pi-agent",
1152
- version: "0.0.8",
1197
+ version: "0.0.10",
1153
1198
  description: "Raindrop observability for Pi Agent \u2014 automatic tracing via subscriber or pi-coding-agent extension",
1154
1199
  type: "module",
1155
1200
  license: "MIT",
@@ -1544,17 +1589,22 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
1544
1589
  }
1545
1590
  if (!("role" in message) || message.role !== "assistant") return;
1546
1591
  const model = extractModelName(message);
1592
+ const provider = "provider" in message && typeof message.provider === "string" ? message.provider : void 0;
1593
+ const bareModel = typeof message.model === "string" ? message.model : void 0;
1547
1594
  const usage = extractTokenUsage(message);
1548
1595
  const assistantText = extractAssistantText(message);
1549
- if (model) currentRun.lastModel = model;
1596
+ if (bareModel) {
1597
+ currentRun.lastModel = bareModel;
1598
+ currentRun.lastProvider = provider;
1599
+ }
1550
1600
  if (usage) {
1551
1601
  currentRun.totalInputTokens += usage.input;
1552
1602
  currentRun.totalOutputTokens += usage.output;
1553
1603
  if (usage.cacheRead) currentRun.totalCacheReadTokens += usage.cacheRead;
1554
1604
  }
1555
1605
  if (!traceShipper || !currentRun.currentTurnSpan) return;
1556
- const rawProvider = "provider" in message && typeof message.provider === "string" ? message.provider : void 0;
1557
- const rawModelId = typeof message.model === "string" ? message.model : void 0;
1606
+ const rawProvider = provider;
1607
+ const rawModelId = bareModel;
1558
1608
  const stopReason = "stopReason" in message && typeof message.stopReason === "string" ? message.stopReason : void 0;
1559
1609
  const llmAttrs = [
1560
1610
  attrString("ai.operationId", "generateText")
@@ -1712,7 +1762,7 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
1712
1762
  attrString("ai.operationId", "generateText")
1713
1763
  ];
1714
1764
  if (run.lastModel) {
1715
- run.rootSpan.name = run.lastModel;
1765
+ run.rootSpan.name = run.lastProvider ? `${run.lastProvider}/${run.lastModel}` : run.lastModel;
1716
1766
  rootAttrs.push(attrString("gen_ai.response.model", run.lastModel));
1717
1767
  }
1718
1768
  if (run.currentInput) {
@@ -1741,6 +1791,7 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
1741
1791
  properties: {
1742
1792
  ...properties,
1743
1793
  sdk_version: libraryVersion,
1794
+ ...run.lastProvider ? { "ai.provider": run.lastProvider } : {},
1744
1795
  ...run.totalInputTokens > 0 ? { "ai.usage.prompt_tokens": run.totalInputTokens } : {},
1745
1796
  ...run.totalOutputTokens > 0 ? { "ai.usage.completion_tokens": run.totalOutputTokens } : {},
1746
1797
  ...run.totalCacheReadTokens > 0 ? { "ai.usage.cache_read_tokens": run.totalCacheReadTokens } : {}
@@ -53,6 +53,7 @@ type Patch = {
53
53
  output?: string;
54
54
  model?: string;
55
55
  properties?: Record<string, unknown>;
56
+ featureFlags?: Record<string, string>;
56
57
  attachments?: Attachment[];
57
58
  isPending?: boolean;
58
59
  timestamp?: string;
@@ -153,6 +154,7 @@ declare class EventShipper {
153
154
  output?: string;
154
155
  model?: string;
155
156
  properties?: Record<string, unknown>;
157
+ featureFlags?: Record<string, string>;
156
158
  userId?: string;
157
159
  }): Promise<void>;
158
160
  flush(): Promise<void>;
@@ -357,6 +359,42 @@ declare class TraceShipper {
357
359
  shutdown(): Promise<void>;
358
360
  }
359
361
 
362
+ /**
363
+ * Run telemetry egress with OpenTelemetry tracing suppressed.
364
+ *
365
+ * Why this exists
366
+ * ---------------
367
+ * Raindrop integrations ship spans/events over HTTP with the global `fetch`
368
+ * (see {@link ../http.ts `postJson`}). When the host app also runs an OTel
369
+ * fetch/undici instrumentation — e.g. `@vercel/otel`'s `registerOTel`, which
370
+ * every Eve agent installs — that instrumentation wraps *our own* telemetry
371
+ * POSTs in a `fetch POST <endpoint>` span. Those spans are then handed to the
372
+ * very exporter that issued the request, so they get shipped right back to
373
+ * Raindrop and Workshop as standalone "runs" (and, because each export issues
374
+ * another fetch, they feed back on themselves). The result is a run list
375
+ * flooded with `fetch POST .../v1/traces`, `.../events/track_partial` and
376
+ * `.../live` entries that drown out the real agent turns — especially with
377
+ * sub-agents, where every sandbox runs its own instrumentation.
378
+ *
379
+ * The OTel-blessed fix is to mark the active context as "tracing suppressed"
380
+ * around the request; instrumentations check `isTracingSuppressed` and return
381
+ * a no-op span instead of recording one. We do this through a hook stashed on
382
+ * `globalThis` by the Node entrypoint ({@link ../index.node.ts}) so that:
383
+ * - `@opentelemetry/api` / `@opentelemetry/core` stay *optional* — core never
384
+ * hard-depends on them, and the hook is simply absent when they (and thus
385
+ * any instrumentation to suppress) are not installed; and
386
+ * - the browser bundle never pulls in `node:module`, mirroring how core
387
+ * injects `AsyncLocalStorage` via `RAINDROP_ASYNC_LOCAL_STORAGE`.
388
+ *
389
+ * When no hook is present the callback runs unchanged, so suppression is a
390
+ * best-effort no-op rather than a hard requirement.
391
+ */
392
+ /** Hook signature: run `fn` with OTel tracing suppressed, returning its value. */
393
+ type SuppressTracingHook = <T>(fn: () => T) => T;
394
+ declare global {
395
+ var RAINDROP_SUPPRESS_TRACING: SuppressTracingHook | undefined;
396
+ }
397
+
360
398
  type ParentSpanContext = {
361
399
  traceIdB64: string;
362
400
  spanIdB64: string;
@@ -53,6 +53,7 @@ type Patch = {
53
53
  output?: string;
54
54
  model?: string;
55
55
  properties?: Record<string, unknown>;
56
+ featureFlags?: Record<string, string>;
56
57
  attachments?: Attachment[];
57
58
  isPending?: boolean;
58
59
  timestamp?: string;
@@ -153,6 +154,7 @@ declare class EventShipper {
153
154
  output?: string;
154
155
  model?: string;
155
156
  properties?: Record<string, unknown>;
157
+ featureFlags?: Record<string, string>;
156
158
  userId?: string;
157
159
  }): Promise<void>;
158
160
  flush(): Promise<void>;
@@ -357,6 +359,42 @@ declare class TraceShipper {
357
359
  shutdown(): Promise<void>;
358
360
  }
359
361
 
362
+ /**
363
+ * Run telemetry egress with OpenTelemetry tracing suppressed.
364
+ *
365
+ * Why this exists
366
+ * ---------------
367
+ * Raindrop integrations ship spans/events over HTTP with the global `fetch`
368
+ * (see {@link ../http.ts `postJson`}). When the host app also runs an OTel
369
+ * fetch/undici instrumentation — e.g. `@vercel/otel`'s `registerOTel`, which
370
+ * every Eve agent installs — that instrumentation wraps *our own* telemetry
371
+ * POSTs in a `fetch POST <endpoint>` span. Those spans are then handed to the
372
+ * very exporter that issued the request, so they get shipped right back to
373
+ * Raindrop and Workshop as standalone "runs" (and, because each export issues
374
+ * another fetch, they feed back on themselves). The result is a run list
375
+ * flooded with `fetch POST .../v1/traces`, `.../events/track_partial` and
376
+ * `.../live` entries that drown out the real agent turns — especially with
377
+ * sub-agents, where every sandbox runs its own instrumentation.
378
+ *
379
+ * The OTel-blessed fix is to mark the active context as "tracing suppressed"
380
+ * around the request; instrumentations check `isTracingSuppressed` and return
381
+ * a no-op span instead of recording one. We do this through a hook stashed on
382
+ * `globalThis` by the Node entrypoint ({@link ../index.node.ts}) so that:
383
+ * - `@opentelemetry/api` / `@opentelemetry/core` stay *optional* — core never
384
+ * hard-depends on them, and the hook is simply absent when they (and thus
385
+ * any instrumentation to suppress) are not installed; and
386
+ * - the browser bundle never pulls in `node:module`, mirroring how core
387
+ * injects `AsyncLocalStorage` via `RAINDROP_ASYNC_LOCAL_STORAGE`.
388
+ *
389
+ * When no hook is present the callback runs unchanged, so suppression is a
390
+ * best-effort no-op rather than a hard requirement.
391
+ */
392
+ /** Hook signature: run `fn` with OTel tracing suppressed, returning its value. */
393
+ type SuppressTracingHook = <T>(fn: () => T) => T;
394
+ declare global {
395
+ var RAINDROP_SUPPRESS_TRACING: SuppressTracingHook | undefined;
396
+ }
397
+
360
398
  type ParentSpanContext = {
361
399
  traceIdB64: string;
362
400
  spanIdB64: string;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Agent } from '@earendil-works/pi-agent-core';
2
- import { A as Attachment } from './index.d-D0J2tEXx.cjs';
2
+ import { A as Attachment } from './index.d-DBLTz2Nz.cjs';
3
3
 
4
4
  /**
5
5
  * Options for the Raindrop Pi Agent client.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Agent } from '@earendil-works/pi-agent-core';
2
- import { A as Attachment } from './index.d-D0J2tEXx.js';
2
+ import { A as Attachment } from './index.d-DBLTz2Nz.js';
3
3
 
4
4
  /**
5
5
  * Options for the Raindrop Pi Agent client.
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  resolveLocalDebuggerBaseUrl,
18
18
  safeStringify,
19
19
  truncate
20
- } from "./chunk-C4YB7RUR.js";
20
+ } from "./chunk-GXL5WZE5.js";
21
21
 
22
22
  // src/internal/subscriber.ts
23
23
  function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, options, debug) {
@@ -116,17 +116,22 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
116
116
  }
117
117
  if (!("role" in message) || message.role !== "assistant") return;
118
118
  const model = extractModelName(message);
119
+ const provider = "provider" in message && typeof message.provider === "string" ? message.provider : void 0;
120
+ const bareModel = typeof message.model === "string" ? message.model : void 0;
119
121
  const usage = extractTokenUsage(message);
120
122
  const assistantText = extractAssistantText(message);
121
- if (model) currentRun.lastModel = model;
123
+ if (bareModel) {
124
+ currentRun.lastModel = bareModel;
125
+ currentRun.lastProvider = provider;
126
+ }
122
127
  if (usage) {
123
128
  currentRun.totalInputTokens += usage.input;
124
129
  currentRun.totalOutputTokens += usage.output;
125
130
  if (usage.cacheRead) currentRun.totalCacheReadTokens += usage.cacheRead;
126
131
  }
127
132
  if (!traceShipper || !currentRun.currentTurnSpan) return;
128
- const rawProvider = "provider" in message && typeof message.provider === "string" ? message.provider : void 0;
129
- const rawModelId = typeof message.model === "string" ? message.model : void 0;
133
+ const rawProvider = provider;
134
+ const rawModelId = bareModel;
130
135
  const stopReason = "stopReason" in message && typeof message.stopReason === "string" ? message.stopReason : void 0;
131
136
  const llmAttrs = [
132
137
  attrString("ai.operationId", "generateText")
@@ -284,7 +289,7 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
284
289
  attrString("ai.operationId", "generateText")
285
290
  ];
286
291
  if (run.lastModel) {
287
- run.rootSpan.name = run.lastModel;
292
+ run.rootSpan.name = run.lastProvider ? `${run.lastProvider}/${run.lastModel}` : run.lastModel;
288
293
  rootAttrs.push(attrString("gen_ai.response.model", run.lastModel));
289
294
  }
290
295
  if (run.currentInput) {
@@ -313,6 +318,7 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
313
318
  properties: {
314
319
  ...properties,
315
320
  sdk_version: libraryVersion,
321
+ ...run.lastProvider ? { "ai.provider": run.lastProvider } : {},
316
322
  ...run.totalInputTokens > 0 ? { "ai.usage.prompt_tokens": run.totalInputTokens } : {},
317
323
  ...run.totalOutputTokens > 0 ? { "ai.usage.completion_tokens": run.totalOutputTokens } : {},
318
324
  ...run.totalCacheReadTokens > 0 ? { "ai.usage.cache_read_tokens": run.totalCacheReadTokens } : {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raindrop-ai/pi-agent",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "Raindrop observability for Pi Agent — automatic tracing via subscriber or pi-coding-agent extension",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -54,7 +54,7 @@
54
54
  "tsup": "^8.5.1",
55
55
  "typescript": "^5.7.3",
56
56
  "vitest": "^2.1.9",
57
- "@raindrop-ai/core": "0.0.4"
57
+ "@raindrop-ai/core": "0.1.2"
58
58
  },
59
59
  "tsup": {
60
60
  "entry": [