@raindrop-ai/pi-agent 0.0.2 → 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.
@@ -1,5 +1,5 @@
1
- import { ExtensionAPI } from '@mariozechner/pi-coding-agent';
2
- import { E as EventShipper$1, T as TraceShipper$1, O as OtlpSpan } from './index.d-npTVS9Mf.cjs';
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-Cxs_NTx0.cjs';
3
3
 
4
4
  interface EventMetadata {
5
5
  userId?: string;
@@ -13,6 +13,13 @@ interface RaindropExtensionConfig {
13
13
  debug: boolean;
14
14
  captureSystemPrompt: boolean;
15
15
  eventMetadata?: EventMetadata;
16
+ /**
17
+ * Explicit Workshop / local debugger URL forwarded to both shippers as their
18
+ * `localDebuggerUrl`. `null` opts out (overrides `RAINDROP_LOCAL_DEBUGGER` /
19
+ * `RAINDROP_WORKSHOP` env vars and auto-detect). `undefined` falls through to
20
+ * the env / auto-detect resolution in `@raindrop-ai/core`.
21
+ */
22
+ localWorkshopUrl?: string | null;
16
23
  }
17
24
 
18
25
  declare class EventShipper extends EventShipper$1 {
@@ -49,6 +56,9 @@ declare function registerTracing(pi: ExtensionAPI, config: RaindropExtensionConf
49
56
  * RAINDROP_API_URL — custom endpoint (default: https://api.raindrop.ai/v1)
50
57
  * RAINDROP_DEBUG — enable debug logging
51
58
  * RAINDROP_CAPTURE_SYSTEM_PROMPT — capture system prompts in traces
59
+ * RAINDROP_LOCAL_WORKSHOP_URL — mirror to a local Raindrop Workshop daemon
60
+ * in addition to the cloud endpoint. Pass `null`, `""`, or `"false"` to
61
+ * opt out of all auto-detection.
52
62
  *
53
63
  * Config file locations:
54
64
  * ~/.pi/agent/raindrop.json (global)
@@ -1,5 +1,5 @@
1
- import { ExtensionAPI } from '@mariozechner/pi-coding-agent';
2
- import { E as EventShipper$1, T as TraceShipper$1, O as OtlpSpan } from './index.d-npTVS9Mf.js';
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-Cxs_NTx0.js';
3
3
 
4
4
  interface EventMetadata {
5
5
  userId?: string;
@@ -13,6 +13,13 @@ interface RaindropExtensionConfig {
13
13
  debug: boolean;
14
14
  captureSystemPrompt: boolean;
15
15
  eventMetadata?: EventMetadata;
16
+ /**
17
+ * Explicit Workshop / local debugger URL forwarded to both shippers as their
18
+ * `localDebuggerUrl`. `null` opts out (overrides `RAINDROP_LOCAL_DEBUGGER` /
19
+ * `RAINDROP_WORKSHOP` env vars and auto-detect). `undefined` falls through to
20
+ * the env / auto-detect resolution in `@raindrop-ai/core`.
21
+ */
22
+ localWorkshopUrl?: string | null;
16
23
  }
17
24
 
18
25
  declare class EventShipper extends EventShipper$1 {
@@ -49,6 +56,9 @@ declare function registerTracing(pi: ExtensionAPI, config: RaindropExtensionConf
49
56
  * RAINDROP_API_URL — custom endpoint (default: https://api.raindrop.ai/v1)
50
57
  * RAINDROP_DEBUG — enable debug logging
51
58
  * RAINDROP_CAPTURE_SYSTEM_PROMPT — capture system prompts in traces
59
+ * RAINDROP_LOCAL_WORKSHOP_URL — mirror to a local Raindrop Workshop daemon
60
+ * in addition to the cloud endpoint. Pass `null`, `""`, or `"false"` to
61
+ * opt out of all auto-detection.
52
62
  *
53
63
  * Config file locations:
54
64
  * ~/.pi/agent/raindrop.json (global)
package/dist/extension.js CHANGED
@@ -10,9 +10,10 @@ import {
10
10
  getUsername,
11
11
  libraryVersion,
12
12
  nowUnixNanoString,
13
+ resolveLocalDebuggerBaseUrl,
13
14
  safeStringify,
14
15
  truncate
15
- } from "./chunk-LZIGXU2D.js";
16
+ } from "./chunk-EWIO36KH.js";
16
17
 
17
18
  // src/internal/config.ts
18
19
  import { existsSync, readFileSync } from "fs";
@@ -53,9 +54,20 @@ function loadConfig(projectDirectory) {
53
54
  eventName: (_e = merged.event_name) != null ? _e : "pi_session",
54
55
  debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_f = merged.debug) != null ? _f : false,
55
56
  captureSystemPrompt: process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] !== void 0 ? process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] === "true" : (_g = merged.capture_system_prompt) != null ? _g : false,
56
- eventMetadata
57
+ eventMetadata,
58
+ localWorkshopUrl: resolveLocalWorkshopUrl(merged.local_workshop_url)
57
59
  };
58
60
  }
61
+ function resolveLocalWorkshopUrl(fileValue) {
62
+ const envValue = process.env["RAINDROP_LOCAL_WORKSHOP_URL"];
63
+ if (envValue !== void 0) {
64
+ if (envValue === "" || envValue.toLowerCase() === "null" || envValue.toLowerCase() === "false") {
65
+ return null;
66
+ }
67
+ return envValue;
68
+ }
69
+ return fileValue;
70
+ }
59
71
 
60
72
  // src/internal/extension-tracing.ts
61
73
  function safeParsArgs(argsStr) {
@@ -424,25 +436,33 @@ function extension(pi) {
424
436
  console.log(`[raindrop-ai/pi-agent] [${level}] ${message}`);
425
437
  }
426
438
  appLog("info", `Loading ${PLUGIN_NAME} v${libraryVersion}`);
427
- if (!config.writeKey) {
439
+ const resolvedLocalUrl = resolveLocalDebuggerBaseUrl(config.localWorkshopUrl);
440
+ const hasLocalDestination = resolvedLocalUrl !== null;
441
+ if (!config.writeKey && !hasLocalDestination) {
428
442
  appLog(
429
443
  "warn",
430
- "RAINDROP_WRITE_KEY not set \u2014 Raindrop tracing disabled. Set it in your environment or in ~/.pi/agent/raindrop.json."
444
+ "RAINDROP_WRITE_KEY not set and no local Workshop daemon detected \u2014 Raindrop tracing disabled. Set RAINDROP_WRITE_KEY for cloud, or RAINDROP_LOCAL_WORKSHOP_URL / RAINDROP_LOCAL_DEBUGGER for local-only mode."
431
445
  );
432
446
  return;
433
447
  }
434
448
  if (config.debug) {
435
- appLog("info", `Raindrop tracing enabled (endpoint: ${config.endpoint})`);
449
+ const destinations = [
450
+ config.writeKey ? `cloud (${config.endpoint})` : null,
451
+ resolvedLocalUrl ? `local Workshop (${resolvedLocalUrl})` : null
452
+ ].filter(Boolean);
453
+ appLog("info", `Raindrop tracing enabled \u2014 destinations: ${destinations.join(", ")}`);
436
454
  }
437
455
  const eventShipper = new EventShipper({
438
456
  writeKey: config.writeKey,
439
457
  endpoint: config.endpoint,
440
- debug: config.debug
458
+ debug: config.debug,
459
+ localDebuggerUrl: config.localWorkshopUrl
441
460
  });
442
461
  const traceShipper = new TraceShipper({
443
462
  writeKey: config.writeKey,
444
463
  endpoint: config.endpoint,
445
- debug: config.debug
464
+ debug: config.debug,
465
+ localDebuggerUrl: config.localWorkshopUrl
446
466
  });
447
467
  registerTracing(pi, config, eventShipper, traceShipper);
448
468
  }
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-4UCYIEH4.js
27
+ // ../core/dist/chunk-VUNUOE2X.js
28
28
  function getCrypto() {
29
29
  const c = globalThis.crypto;
30
30
  return c;
@@ -225,6 +225,91 @@ function buildExportTraceServiceRequest(spans, serviceName = "raindrop.core", se
225
225
  ]
226
226
  };
227
227
  }
228
+ var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
229
+ var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
230
+ var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
231
+ function readEnvVar(name) {
232
+ var _a;
233
+ try {
234
+ const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
235
+ if (env && typeof env[name] === "string" && env[name].length > 0) {
236
+ return env[name];
237
+ }
238
+ } catch (e) {
239
+ }
240
+ return void 0;
241
+ }
242
+ function readWorkshopEnv() {
243
+ const raw = readEnvVar(WORKSHOP_ENV_VAR);
244
+ if (raw === void 0) return void 0;
245
+ const trimmed = raw.trim();
246
+ if (trimmed.length === 0) return void 0;
247
+ if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
248
+ if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
249
+ if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
250
+ return void 0;
251
+ }
252
+ function isLocalDevHost(hostname2) {
253
+ if (!hostname2) return false;
254
+ if (hostname2 === "localhost" || hostname2 === "127.0.0.1" || hostname2 === "0.0.0.0" || hostname2 === "::1") {
255
+ return true;
256
+ }
257
+ if (hostname2.endsWith(".localhost")) return true;
258
+ return false;
259
+ }
260
+ function readRuntimeHostname() {
261
+ try {
262
+ const loc = globalThis == null ? void 0 : globalThis.location;
263
+ if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
264
+ return loc.hostname;
265
+ }
266
+ } catch (e) {
267
+ }
268
+ return void 0;
269
+ }
270
+ function shouldAutoEnableLocalWorkshop() {
271
+ if (isLocalDevHost(readRuntimeHostname())) return true;
272
+ if (readEnvVar("NODE_ENV") === "development") return true;
273
+ return false;
274
+ }
275
+ function resolveLocalDebuggerBaseUrl(baseUrl) {
276
+ var _a, _b, _c;
277
+ if (baseUrl === null) return null;
278
+ if (typeof baseUrl === "string" && baseUrl.length > 0) {
279
+ return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
280
+ }
281
+ const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
282
+ if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
283
+ const workshopEnv = readWorkshopEnv();
284
+ if (workshopEnv === "disable") return null;
285
+ if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
286
+ if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
287
+ if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
288
+ return null;
289
+ }
290
+ function mirrorTraceExportToLocalDebugger(body, options = {}) {
291
+ var _a;
292
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
293
+ if (!baseUrl) return;
294
+ void postJson(`${baseUrl}traces`, body, {}, {
295
+ maxAttempts: 1,
296
+ debug: (_a = options.debug) != null ? _a : false,
297
+ sdkName: options.sdkName
298
+ }).catch(() => {
299
+ });
300
+ }
301
+ function mirrorPartialEventToLocalDebugger(event, options = {}) {
302
+ var _a;
303
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
304
+ if (!baseUrl) return;
305
+ const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
306
+ void postJson(`${baseUrl}events/track_partial`, event, headers, {
307
+ maxAttempts: 1,
308
+ debug: (_a = options.debug) != null ? _a : false,
309
+ sdkName: options.sdkName
310
+ }).catch(() => {
311
+ });
312
+ }
228
313
  function mergePatches(target, source) {
229
314
  var _a, _b, _c, _d;
230
315
  const out = { ...target, ...source };
@@ -242,7 +327,7 @@ var EventShipper = class {
242
327
  this.sticky = /* @__PURE__ */ new Map();
243
328
  this.timers = /* @__PURE__ */ new Map();
244
329
  this.inFlight = /* @__PURE__ */ new Set();
245
- var _a, _b, _c, _d, _e, _f, _g;
330
+ var _a, _b, _c, _d, _e, _f, _g, _h;
246
331
  this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
247
332
  this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
248
333
  this.enabled = opts.enabled !== false;
@@ -251,11 +336,15 @@ var EventShipper = class {
251
336
  this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
252
337
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
253
338
  this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
339
+ this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
340
+ if (this.debug && this.localDebuggerUrl) {
341
+ console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
342
+ }
254
343
  const isNode = typeof process !== "undefined" && typeof process.version === "string";
255
344
  this.context = {
256
345
  library: {
257
- name: (_f = opts.libraryName) != null ? _f : "@raindrop-ai/core",
258
- version: (_g = opts.libraryVersion) != null ? _g : "0.0.0"
346
+ name: (_g = opts.libraryName) != null ? _g : "@raindrop-ai/core",
347
+ version: (_h = opts.libraryVersion) != null ? _h : "0.0.0"
259
348
  },
260
349
  metadata: {
261
350
  jsRuntime: isNode ? "node" : "web",
@@ -341,6 +430,7 @@ var EventShipper = class {
341
430
  }
342
431
  }
343
432
  ];
433
+ if (!this.writeKey) return;
344
434
  const url = `${this.baseUrl}signals/track`;
345
435
  try {
346
436
  await postJson(url, body, this.authHeaders(), {
@@ -371,6 +461,7 @@ var EventShipper = class {
371
461
  traits: (_a = user.traits) != null ? _a : {}
372
462
  };
373
463
  });
464
+ if (!this.writeKey) return;
374
465
  if (body.length === 0) return;
375
466
  const url = `${this.baseUrl}users/identify`;
376
467
  try {
@@ -447,6 +538,18 @@ var EventShipper = class {
447
538
  endpoint: url
448
539
  });
449
540
  }
541
+ if (this.localDebuggerUrl) {
542
+ mirrorPartialEventToLocalDebugger(payload, {
543
+ baseUrl: this.localDebuggerUrl,
544
+ writeKey: this.writeKey,
545
+ debug: this.debug,
546
+ sdkName: this.sdkName
547
+ });
548
+ }
549
+ if (!this.writeKey) {
550
+ if (!isPending) this.sticky.delete(eventId);
551
+ return;
552
+ }
450
553
  const p = postJson(url, payload, this.authHeaders(), {
451
554
  maxAttempts: 3,
452
555
  debug: this.debug,
@@ -471,28 +574,100 @@ var EventShipper = class {
471
574
  }
472
575
  }
473
576
  };
474
- var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
475
- function resolveLocalDebuggerBaseUrl(baseUrl) {
476
- var _a, _b, _c;
477
- const resolved = (_b = baseUrl != null ? baseUrl : typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a[LOCAL_DEBUGGER_ENV_VAR] : void 0) != null ? _b : null;
478
- return resolved ? (_c = formatEndpoint(resolved)) != null ? _c : null : null;
577
+ var DEFAULT_SECRET_KEY_NAMES = [
578
+ "apikey",
579
+ "apisecret",
580
+ "apitoken",
581
+ "secretaccesskey",
582
+ "sessiontoken",
583
+ "privatekey",
584
+ "privatekeyid",
585
+ "clientsecret",
586
+ "accesstoken",
587
+ "refreshtoken",
588
+ "oauthtoken",
589
+ "bearertoken",
590
+ "authorization",
591
+ "password",
592
+ "passphrase"
593
+ ];
594
+ var REDACTED_PLACEHOLDER = "[REDACTED]";
595
+ function normalizeKeyName(name) {
596
+ return name.toLowerCase().replace(/[-_.]/g, "");
479
597
  }
480
- function mirrorTraceExportToLocalDebugger(body, options = {}) {
481
- var _a;
482
- const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
483
- if (!baseUrl) return;
484
- void postJson(`${baseUrl}traces`, body, {}, {
485
- maxAttempts: 1,
486
- debug: (_a = options.debug) != null ? _a : false,
487
- sdkName: options.sdkName
488
- }).catch(() => {
489
- });
598
+ function redactSecretsInObject(value, options) {
599
+ var _a, _b;
600
+ const normalizedSecretSet = buildSecretSet((_a = options == null ? void 0 : options.secretKeyNames) != null ? _a : DEFAULT_SECRET_KEY_NAMES);
601
+ const placeholder = (_b = options == null ? void 0 : options.placeholder) != null ? _b : REDACTED_PLACEHOLDER;
602
+ const seen = /* @__PURE__ */ new WeakSet();
603
+ const walk = (node) => {
604
+ if (node === null || typeof node !== "object") return node;
605
+ if (seen.has(node)) return "[CIRCULAR]";
606
+ seen.add(node);
607
+ if (Array.isArray(node)) {
608
+ return node.map((item) => walk(item));
609
+ }
610
+ const out = {};
611
+ for (const [k, v] of Object.entries(node)) {
612
+ if (normalizedSecretSet.has(normalizeKeyName(k))) {
613
+ out[k] = placeholder;
614
+ } else {
615
+ out[k] = walk(v);
616
+ }
617
+ }
618
+ return out;
619
+ };
620
+ return walk(value);
621
+ }
622
+ function buildSecretSet(names) {
623
+ const set = /* @__PURE__ */ new Set();
624
+ for (const name of names) set.add(normalizeKeyName(name));
625
+ return set;
626
+ }
627
+ var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
628
+ "ai.request.providerOptions",
629
+ "ai.response.providerMetadata"
630
+ ];
631
+ function defaultTransformSpan(span) {
632
+ const attrs = span.attributes;
633
+ if (!attrs || attrs.length === 0) return span;
634
+ let nextAttrs;
635
+ for (let i = 0; i < attrs.length; i++) {
636
+ const attr = attrs[i];
637
+ const redacted = redactJsonAttributeValue(attr.key, attr.value);
638
+ if (redacted === void 0) continue;
639
+ if (!nextAttrs) nextAttrs = attrs.slice();
640
+ nextAttrs[i] = { key: attr.key, value: redacted };
641
+ }
642
+ if (!nextAttrs) return span;
643
+ return { ...span, attributes: nextAttrs };
644
+ }
645
+ var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
646
+ function redactJsonAttributeValue(key, value) {
647
+ if (!REDACT_JSON_ATTRIBUTE_KEYS.has(key)) return void 0;
648
+ const json = value.stringValue;
649
+ if (typeof json !== "string" || json.length === 0) return void 0;
650
+ let parsed;
651
+ try {
652
+ parsed = JSON.parse(json);
653
+ } catch (e) {
654
+ return void 0;
655
+ }
656
+ const scrubbed = redactSecretsInObject(parsed);
657
+ let scrubbedJson;
658
+ try {
659
+ scrubbedJson = JSON.stringify(scrubbed);
660
+ } catch (e) {
661
+ return void 0;
662
+ }
663
+ if (scrubbedJson === json) return void 0;
664
+ return { stringValue: scrubbedJson };
490
665
  }
491
666
  var TraceShipper = class {
492
667
  constructor(opts) {
493
668
  this.queue = [];
494
669
  this.inFlight = /* @__PURE__ */ new Set();
495
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
670
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
496
671
  this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
497
672
  this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
498
673
  this.enabled = opts.enabled !== false;
@@ -505,13 +680,46 @@ var TraceShipper = class {
505
680
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
506
681
  this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
507
682
  this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
508
- const localDebugger = typeof process !== "undefined" ? (_i = process.env) == null ? void 0 : _i.RAINDROP_LOCAL_DEBUGGER : void 0;
509
- if (localDebugger) {
510
- this.localDebuggerUrl = (_j = resolveLocalDebuggerBaseUrl(localDebugger)) != null ? _j : void 0;
511
- if (this.debug) {
512
- console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
683
+ this.localDebuggerUrl = (_i = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _i : void 0;
684
+ if (this.debug && this.localDebuggerUrl) {
685
+ console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
686
+ }
687
+ this.transformSpanHook = opts.transformSpan;
688
+ this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
689
+ }
690
+ /**
691
+ * Apply the user `transformSpan` hook (if any) followed by the default
692
+ * redactor (unless disabled). Returns either the (possibly new) span to
693
+ * ship, or `null` to drop the span entirely.
694
+ *
695
+ * Ordering: user hook runs first so callers can rewrite the span freely
696
+ * (rename attrs, add new ones, scrub things the default doesn't know
697
+ * about). The default redactor then runs on whatever the user produced,
698
+ * acting as the always-on floor for documented BYOK secrets. If the user
699
+ * sets `disableDefaultRedaction: true`, the floor is skipped.
700
+ *
701
+ * Fail-closed: if the user hook throws, the span is dropped — a buggy
702
+ * hook can never accidentally ship raw, un-redacted spans.
703
+ */
704
+ redactSpan(span) {
705
+ let current = span;
706
+ if (this.transformSpanHook) {
707
+ try {
708
+ const result = this.transformSpanHook(current);
709
+ if (result === null) return null;
710
+ if (result !== void 0) current = result;
711
+ } catch (err) {
712
+ if (this.debug) {
713
+ const msg = err instanceof Error ? err.message : String(err);
714
+ console.warn(`${this.prefix} transformSpan hook threw: ${msg}`);
715
+ }
716
+ return null;
513
717
  }
514
718
  }
719
+ if (!this.disableDefaultRedaction) {
720
+ current = defaultTransformSpan(current);
721
+ }
722
+ return current;
515
723
  }
516
724
  isDebugEnabled() {
517
725
  return this.debug;
@@ -529,8 +737,8 @@ var TraceShipper = class {
529
737
  ];
530
738
  if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
531
739
  const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
532
- if (this.localDebuggerUrl) {
533
- const openSpan = buildOtlpSpan({
740
+ this.mirrorToLocalDebugger(
741
+ buildOtlpSpan({
534
742
  ids: span.ids,
535
743
  name: span.name,
536
744
  startTimeUnixNano: span.startTimeUnixNano,
@@ -538,16 +746,21 @@ var TraceShipper = class {
538
746
  // placeholder — will be updated on endSpan
539
747
  attributes: span.attributes,
540
748
  status: { code: SpanStatusCode.UNSET }
541
- });
542
- const body = buildExportTraceServiceRequest([openSpan], this.serviceName, this.serviceVersion);
543
- mirrorTraceExportToLocalDebugger(body, {
544
- baseUrl: this.localDebuggerUrl,
545
- debug: false,
546
- sdkName: this.sdkName
547
- });
548
- }
749
+ })
750
+ );
549
751
  return span;
550
752
  }
753
+ mirrorToLocalDebugger(span) {
754
+ if (!this.localDebuggerUrl) return;
755
+ const redacted = this.redactSpan(span);
756
+ if (redacted === null) return;
757
+ const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
758
+ mirrorTraceExportToLocalDebugger(body, {
759
+ baseUrl: this.localDebuggerUrl,
760
+ debug: false,
761
+ sdkName: this.sdkName
762
+ });
763
+ }
551
764
  endSpan(span, extra) {
552
765
  var _a, _b;
553
766
  if (span.endTimeUnixNano) return;
@@ -569,14 +782,7 @@ var TraceShipper = class {
569
782
  status
570
783
  });
571
784
  this.enqueue(otlp);
572
- if (this.localDebuggerUrl) {
573
- const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
574
- mirrorTraceExportToLocalDebugger(body, {
575
- baseUrl: this.localDebuggerUrl,
576
- debug: false,
577
- sdkName: this.sdkName
578
- });
579
- }
785
+ this.mirrorToLocalDebugger(otlp);
580
786
  }
581
787
  createSpan(args) {
582
788
  var _a;
@@ -594,14 +800,7 @@ var TraceShipper = class {
594
800
  status: args.status
595
801
  });
596
802
  this.enqueue(otlp);
597
- if (this.localDebuggerUrl) {
598
- const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
599
- mirrorTraceExportToLocalDebugger(body, {
600
- baseUrl: this.localDebuggerUrl,
601
- debug: false,
602
- sdkName: this.sdkName
603
- });
604
- }
803
+ this.mirrorToLocalDebugger(otlp);
605
804
  }
606
805
  enqueue(span) {
607
806
  if (!this.enabled) return;
@@ -613,10 +812,12 @@ var TraceShipper = class {
613
812
  )}`
614
813
  );
615
814
  }
815
+ const redacted = this.redactSpan(span);
816
+ if (redacted === null) return;
616
817
  if (this.queue.length >= this.maxQueueSize) {
617
818
  this.queue.shift();
618
819
  }
619
- this.queue.push(span);
820
+ this.queue.push(redacted);
620
821
  if (this.queue.length >= this.maxBatchSize) {
621
822
  void this.flush().catch(() => {
622
823
  });
@@ -638,6 +839,7 @@ var TraceShipper = class {
638
839
  }
639
840
  while (this.queue.length > 0) {
640
841
  const batch = this.queue.splice(0, this.maxBatchSize);
842
+ if (!this.writeKey) continue;
641
843
  const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
642
844
  const url = `${this.baseUrl}traces`;
643
845
  if (this.debug) {
@@ -683,7 +885,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
683
885
  // package.json
684
886
  var package_default = {
685
887
  name: "@raindrop-ai/pi-agent",
686
- version: "0.0.2",
888
+ version: "0.0.4",
687
889
  description: "Raindrop observability for Pi Agent \u2014 automatic tracing via subscriber or pi-coding-agent extension",
688
890
  type: "module",
689
891
  license: "MIT",
@@ -733,18 +935,18 @@ var package_default = {
733
935
  "test:watch": "vitest"
734
936
  },
735
937
  peerDependencies: {
736
- "@mariozechner/pi-agent-core": ">=0.60.0",
737
- "@mariozechner/pi-coding-agent": ">=0.65.2"
938
+ "@earendil-works/pi-agent-core": ">=0.74.0",
939
+ "@earendil-works/pi-coding-agent": ">=0.74.0"
738
940
  },
739
941
  peerDependenciesMeta: {
740
- "@mariozechner/pi-coding-agent": {
942
+ "@earendil-works/pi-coding-agent": {
741
943
  optional: true
742
944
  }
743
945
  },
744
946
  devDependencies: {
745
947
  "@raindrop-ai/core": "workspace:*",
746
- "@mariozechner/pi-agent-core": "^0.66.0",
747
- "@mariozechner/pi-coding-agent": "^0.66.0",
948
+ "@earendil-works/pi-agent-core": "^0.78.0",
949
+ "@earendil-works/pi-coding-agent": "^0.78.0",
748
950
  "@types/node": "^20.11.17",
749
951
  msw: "^2.12.7",
750
952
  tsup: "^8.5.1",
@@ -1282,23 +1484,26 @@ function envDebugEnabled() {
1282
1484
  function createRaindropPiAgent(opts) {
1283
1485
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1284
1486
  const hasWriteKey = typeof opts.writeKey === "string" && opts.writeKey.trim().length > 0;
1487
+ const resolvedLocalUrl = resolveLocalDebuggerBaseUrl(opts.localWorkshopUrl);
1488
+ const hasLocalDestination = resolvedLocalUrl !== null;
1285
1489
  const eventsEnabled = ((_a = opts.events) == null ? void 0 : _a.enabled) !== false;
1286
1490
  const tracesEnabled = ((_b = opts.traces) == null ? void 0 : _b.enabled) !== false;
1287
- if (!hasWriteKey && !opts.endpoint) {
1491
+ if (!hasWriteKey && !opts.endpoint && !hasLocalDestination) {
1288
1492
  console.warn(
1289
1493
  "[raindrop-ai/pi-agent] writeKey not provided; telemetry shipping is disabled"
1290
1494
  );
1291
1495
  }
1292
1496
  const envDebug = envDebugEnabled();
1293
1497
  const debug = ((_c = opts.events) == null ? void 0 : _c.debug) === true || ((_d = opts.traces) == null ? void 0 : _d.debug) === true || envDebug;
1294
- const eventShipper = eventsEnabled && (hasWriteKey || opts.endpoint) ? new EventShipper2({
1498
+ const eventShipper = eventsEnabled && (hasWriteKey || opts.endpoint || hasLocalDestination) ? new EventShipper2({
1295
1499
  writeKey: opts.writeKey,
1296
1500
  endpoint: opts.endpoint,
1297
1501
  enabled: true,
1298
1502
  debug: ((_e = opts.events) == null ? void 0 : _e.debug) === true || envDebug,
1299
- partialFlushMs: (_f = opts.events) == null ? void 0 : _f.partialFlushMs
1503
+ partialFlushMs: (_f = opts.events) == null ? void 0 : _f.partialFlushMs,
1504
+ localDebuggerUrl: opts.localWorkshopUrl
1300
1505
  }) : null;
1301
- const traceShipper = tracesEnabled && (hasWriteKey || opts.endpoint) ? new TraceShipper2({
1506
+ const traceShipper = tracesEnabled && (hasWriteKey || opts.endpoint || hasLocalDestination) ? new TraceShipper2({
1302
1507
  writeKey: opts.writeKey,
1303
1508
  endpoint: opts.endpoint,
1304
1509
  enabled: true,
@@ -1306,7 +1511,8 @@ function createRaindropPiAgent(opts) {
1306
1511
  debugSpans: ((_h = opts.traces) == null ? void 0 : _h.debugSpans) === true || envDebug,
1307
1512
  flushIntervalMs: (_i = opts.traces) == null ? void 0 : _i.flushIntervalMs,
1308
1513
  maxBatchSize: (_j = opts.traces) == null ? void 0 : _j.maxBatchSize,
1309
- maxQueueSize: (_k = opts.traces) == null ? void 0 : _k.maxQueueSize
1514
+ maxQueueSize: (_k = opts.traces) == null ? void 0 : _k.maxQueueSize,
1515
+ localDebuggerUrl: opts.localWorkshopUrl
1310
1516
  }) : null;
1311
1517
  const defaultOptions = {
1312
1518
  userId: opts.userId,