@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.
@@ -25,147 +25,7 @@ __export(extension_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(extension_exports);
27
27
 
28
- // src/internal/config.ts
29
- var import_node_fs = require("fs");
30
- var import_node_os = require("os");
31
- var import_node_path = require("path");
32
- function getPiAgentDirectory() {
33
- var _a;
34
- return (_a = process.env["PI_CODING_AGENT_DIR"]) != null ? _a : (0, import_node_path.join)((0, import_node_os.homedir)(), ".pi", "agent");
35
- }
36
- function loadConfig(projectDirectory) {
37
- var _a, _b, _c, _d, _e, _f, _g;
38
- let merged = {};
39
- const configPaths = [
40
- (0, import_node_path.join)(getPiAgentDirectory(), "raindrop.json"),
41
- (0, import_node_path.join)(projectDirectory, ".pi", "raindrop.json")
42
- ];
43
- for (const configPath of configPaths) {
44
- try {
45
- if ((0, import_node_fs.existsSync)(configPath)) {
46
- const content = (0, import_node_fs.readFileSync)(configPath, "utf-8");
47
- const parsed = JSON.parse(content);
48
- merged = { ...merged, ...parsed };
49
- }
50
- } catch (e) {
51
- }
52
- }
53
- let eventMetadata;
54
- const envMeta = process.env["RAINDROP_EVENT_METADATA"];
55
- if (envMeta) {
56
- try {
57
- eventMetadata = JSON.parse(envMeta);
58
- } catch (e) {
59
- }
60
- }
61
- return {
62
- writeKey: (_b = (_a = process.env["RAINDROP_WRITE_KEY"]) != null ? _a : merged.write_key) != null ? _b : "",
63
- endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : merged.api_url) != null ? _d : "https://api.raindrop.ai/v1",
64
- eventName: (_e = merged.event_name) != null ? _e : "pi_session",
65
- debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_f = merged.debug) != null ? _f : false,
66
- captureSystemPrompt: process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] !== void 0 ? process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] === "true" : (_g = merged.capture_system_prompt) != null ? _g : false,
67
- eventMetadata
68
- };
69
- }
70
-
71
- // package.json
72
- var package_default = {
73
- name: "@raindrop-ai/pi-agent",
74
- version: "0.0.2",
75
- description: "Raindrop observability for Pi Agent \u2014 automatic tracing via subscriber or pi-coding-agent extension",
76
- type: "module",
77
- license: "MIT",
78
- repository: {
79
- type: "git",
80
- url: "git+https://github.com/raindrop-ai/raindrop-js.git",
81
- directory: "packages/pi-agent"
82
- },
83
- homepage: "https://github.com/raindrop-ai/raindrop-js/tree/main/packages/pi-agent#readme",
84
- bugs: {
85
- url: "https://github.com/raindrop-ai/raindrop-js/issues"
86
- },
87
- keywords: [
88
- "pi-package",
89
- "raindrop",
90
- "pi-agent",
91
- "observability",
92
- "tracing"
93
- ],
94
- exports: {
95
- ".": {
96
- types: "./dist/index.d.ts",
97
- import: "./dist/index.js",
98
- require: "./dist/index.cjs"
99
- },
100
- "./extension": {
101
- types: "./dist/extension.d.ts",
102
- import: "./dist/extension.js",
103
- require: "./dist/extension.cjs"
104
- }
105
- },
106
- pi: {
107
- extensions: [
108
- "./dist/extension.js"
109
- ]
110
- },
111
- sideEffects: false,
112
- files: [
113
- "dist/**",
114
- "README.md"
115
- ],
116
- scripts: {
117
- build: "tsup",
118
- dev: "tsup --watch",
119
- clean: "rm -rf dist",
120
- test: "vitest run",
121
- "test:watch": "vitest"
122
- },
123
- peerDependencies: {
124
- "@mariozechner/pi-agent-core": ">=0.60.0",
125
- "@mariozechner/pi-coding-agent": ">=0.65.2"
126
- },
127
- peerDependenciesMeta: {
128
- "@mariozechner/pi-coding-agent": {
129
- optional: true
130
- }
131
- },
132
- devDependencies: {
133
- "@raindrop-ai/core": "workspace:*",
134
- "@mariozechner/pi-agent-core": "^0.66.0",
135
- "@mariozechner/pi-coding-agent": "^0.66.0",
136
- "@types/node": "^20.11.17",
137
- msw: "^2.12.7",
138
- tsup: "^8.5.1",
139
- typescript: "^5.7.3",
140
- vitest: "^2.1.9"
141
- },
142
- tsup: {
143
- entry: [
144
- "src/index.ts",
145
- "src/extension.ts"
146
- ],
147
- format: [
148
- "cjs",
149
- "esm"
150
- ],
151
- dts: {
152
- resolve: true
153
- },
154
- clean: true,
155
- noExternal: [
156
- "@raindrop-ai/core"
157
- ]
158
- },
159
- publishConfig: {
160
- access: "public"
161
- }
162
- };
163
-
164
- // src/version.ts
165
- var libraryName = package_default.name;
166
- var libraryVersion = package_default.version;
167
-
168
- // ../core/dist/chunk-4UCYIEH4.js
28
+ // ../core/dist/chunk-VUNUOE2X.js
169
29
  function getCrypto() {
170
30
  const c = globalThis.crypto;
171
31
  return c;
@@ -358,6 +218,91 @@ function buildExportTraceServiceRequest(spans, serviceName = "raindrop.core", se
358
218
  ]
359
219
  };
360
220
  }
221
+ var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
222
+ var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
223
+ var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
224
+ function readEnvVar(name) {
225
+ var _a;
226
+ try {
227
+ const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
228
+ if (env && typeof env[name] === "string" && env[name].length > 0) {
229
+ return env[name];
230
+ }
231
+ } catch (e) {
232
+ }
233
+ return void 0;
234
+ }
235
+ function readWorkshopEnv() {
236
+ const raw = readEnvVar(WORKSHOP_ENV_VAR);
237
+ if (raw === void 0) return void 0;
238
+ const trimmed = raw.trim();
239
+ if (trimmed.length === 0) return void 0;
240
+ if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
241
+ if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
242
+ if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
243
+ return void 0;
244
+ }
245
+ function isLocalDevHost(hostname2) {
246
+ if (!hostname2) return false;
247
+ if (hostname2 === "localhost" || hostname2 === "127.0.0.1" || hostname2 === "0.0.0.0" || hostname2 === "::1") {
248
+ return true;
249
+ }
250
+ if (hostname2.endsWith(".localhost")) return true;
251
+ return false;
252
+ }
253
+ function readRuntimeHostname() {
254
+ try {
255
+ const loc = globalThis == null ? void 0 : globalThis.location;
256
+ if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
257
+ return loc.hostname;
258
+ }
259
+ } catch (e) {
260
+ }
261
+ return void 0;
262
+ }
263
+ function shouldAutoEnableLocalWorkshop() {
264
+ if (isLocalDevHost(readRuntimeHostname())) return true;
265
+ if (readEnvVar("NODE_ENV") === "development") return true;
266
+ return false;
267
+ }
268
+ function resolveLocalDebuggerBaseUrl(baseUrl) {
269
+ var _a, _b, _c;
270
+ if (baseUrl === null) return null;
271
+ if (typeof baseUrl === "string" && baseUrl.length > 0) {
272
+ return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
273
+ }
274
+ const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
275
+ if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
276
+ const workshopEnv = readWorkshopEnv();
277
+ if (workshopEnv === "disable") return null;
278
+ if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
279
+ if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
280
+ if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
281
+ return null;
282
+ }
283
+ function mirrorTraceExportToLocalDebugger(body, options = {}) {
284
+ var _a;
285
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
286
+ if (!baseUrl) return;
287
+ void postJson(`${baseUrl}traces`, body, {}, {
288
+ maxAttempts: 1,
289
+ debug: (_a = options.debug) != null ? _a : false,
290
+ sdkName: options.sdkName
291
+ }).catch(() => {
292
+ });
293
+ }
294
+ function mirrorPartialEventToLocalDebugger(event, options = {}) {
295
+ var _a;
296
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
297
+ if (!baseUrl) return;
298
+ const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
299
+ void postJson(`${baseUrl}events/track_partial`, event, headers, {
300
+ maxAttempts: 1,
301
+ debug: (_a = options.debug) != null ? _a : false,
302
+ sdkName: options.sdkName
303
+ }).catch(() => {
304
+ });
305
+ }
361
306
  function mergePatches(target, source) {
362
307
  var _a, _b, _c, _d;
363
308
  const out = { ...target, ...source };
@@ -375,7 +320,7 @@ var EventShipper = class {
375
320
  this.sticky = /* @__PURE__ */ new Map();
376
321
  this.timers = /* @__PURE__ */ new Map();
377
322
  this.inFlight = /* @__PURE__ */ new Set();
378
- var _a, _b, _c, _d, _e, _f, _g;
323
+ var _a, _b, _c, _d, _e, _f, _g, _h;
379
324
  this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
380
325
  this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
381
326
  this.enabled = opts.enabled !== false;
@@ -384,11 +329,15 @@ var EventShipper = class {
384
329
  this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
385
330
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
386
331
  this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
332
+ this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
333
+ if (this.debug && this.localDebuggerUrl) {
334
+ console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
335
+ }
387
336
  const isNode = typeof process !== "undefined" && typeof process.version === "string";
388
337
  this.context = {
389
338
  library: {
390
- name: (_f = opts.libraryName) != null ? _f : "@raindrop-ai/core",
391
- version: (_g = opts.libraryVersion) != null ? _g : "0.0.0"
339
+ name: (_g = opts.libraryName) != null ? _g : "@raindrop-ai/core",
340
+ version: (_h = opts.libraryVersion) != null ? _h : "0.0.0"
392
341
  },
393
342
  metadata: {
394
343
  jsRuntime: isNode ? "node" : "web",
@@ -474,6 +423,7 @@ var EventShipper = class {
474
423
  }
475
424
  }
476
425
  ];
426
+ if (!this.writeKey) return;
477
427
  const url = `${this.baseUrl}signals/track`;
478
428
  try {
479
429
  await postJson(url, body, this.authHeaders(), {
@@ -504,6 +454,7 @@ var EventShipper = class {
504
454
  traits: (_a = user.traits) != null ? _a : {}
505
455
  };
506
456
  });
457
+ if (!this.writeKey) return;
507
458
  if (body.length === 0) return;
508
459
  const url = `${this.baseUrl}users/identify`;
509
460
  try {
@@ -580,6 +531,18 @@ var EventShipper = class {
580
531
  endpoint: url
581
532
  });
582
533
  }
534
+ if (this.localDebuggerUrl) {
535
+ mirrorPartialEventToLocalDebugger(payload, {
536
+ baseUrl: this.localDebuggerUrl,
537
+ writeKey: this.writeKey,
538
+ debug: this.debug,
539
+ sdkName: this.sdkName
540
+ });
541
+ }
542
+ if (!this.writeKey) {
543
+ if (!isPending) this.sticky.delete(eventId);
544
+ return;
545
+ }
583
546
  const p = postJson(url, payload, this.authHeaders(), {
584
547
  maxAttempts: 3,
585
548
  debug: this.debug,
@@ -604,28 +567,100 @@ var EventShipper = class {
604
567
  }
605
568
  }
606
569
  };
607
- var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
608
- function resolveLocalDebuggerBaseUrl(baseUrl) {
609
- var _a, _b, _c;
610
- 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;
611
- return resolved ? (_c = formatEndpoint(resolved)) != null ? _c : null : null;
570
+ var DEFAULT_SECRET_KEY_NAMES = [
571
+ "apikey",
572
+ "apisecret",
573
+ "apitoken",
574
+ "secretaccesskey",
575
+ "sessiontoken",
576
+ "privatekey",
577
+ "privatekeyid",
578
+ "clientsecret",
579
+ "accesstoken",
580
+ "refreshtoken",
581
+ "oauthtoken",
582
+ "bearertoken",
583
+ "authorization",
584
+ "password",
585
+ "passphrase"
586
+ ];
587
+ var REDACTED_PLACEHOLDER = "[REDACTED]";
588
+ function normalizeKeyName(name) {
589
+ return name.toLowerCase().replace(/[-_.]/g, "");
612
590
  }
613
- function mirrorTraceExportToLocalDebugger(body, options = {}) {
614
- var _a;
615
- const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
616
- if (!baseUrl) return;
617
- void postJson(`${baseUrl}traces`, body, {}, {
618
- maxAttempts: 1,
619
- debug: (_a = options.debug) != null ? _a : false,
620
- sdkName: options.sdkName
621
- }).catch(() => {
622
- });
591
+ function redactSecretsInObject(value, options) {
592
+ var _a, _b;
593
+ const normalizedSecretSet = buildSecretSet((_a = options == null ? void 0 : options.secretKeyNames) != null ? _a : DEFAULT_SECRET_KEY_NAMES);
594
+ const placeholder = (_b = options == null ? void 0 : options.placeholder) != null ? _b : REDACTED_PLACEHOLDER;
595
+ const seen = /* @__PURE__ */ new WeakSet();
596
+ const walk = (node) => {
597
+ if (node === null || typeof node !== "object") return node;
598
+ if (seen.has(node)) return "[CIRCULAR]";
599
+ seen.add(node);
600
+ if (Array.isArray(node)) {
601
+ return node.map((item) => walk(item));
602
+ }
603
+ const out = {};
604
+ for (const [k, v] of Object.entries(node)) {
605
+ if (normalizedSecretSet.has(normalizeKeyName(k))) {
606
+ out[k] = placeholder;
607
+ } else {
608
+ out[k] = walk(v);
609
+ }
610
+ }
611
+ return out;
612
+ };
613
+ return walk(value);
614
+ }
615
+ function buildSecretSet(names) {
616
+ const set = /* @__PURE__ */ new Set();
617
+ for (const name of names) set.add(normalizeKeyName(name));
618
+ return set;
619
+ }
620
+ var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
621
+ "ai.request.providerOptions",
622
+ "ai.response.providerMetadata"
623
+ ];
624
+ function defaultTransformSpan(span) {
625
+ const attrs = span.attributes;
626
+ if (!attrs || attrs.length === 0) return span;
627
+ let nextAttrs;
628
+ for (let i = 0; i < attrs.length; i++) {
629
+ const attr = attrs[i];
630
+ const redacted = redactJsonAttributeValue(attr.key, attr.value);
631
+ if (redacted === void 0) continue;
632
+ if (!nextAttrs) nextAttrs = attrs.slice();
633
+ nextAttrs[i] = { key: attr.key, value: redacted };
634
+ }
635
+ if (!nextAttrs) return span;
636
+ return { ...span, attributes: nextAttrs };
637
+ }
638
+ var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
639
+ function redactJsonAttributeValue(key, value) {
640
+ if (!REDACT_JSON_ATTRIBUTE_KEYS.has(key)) return void 0;
641
+ const json = value.stringValue;
642
+ if (typeof json !== "string" || json.length === 0) return void 0;
643
+ let parsed;
644
+ try {
645
+ parsed = JSON.parse(json);
646
+ } catch (e) {
647
+ return void 0;
648
+ }
649
+ const scrubbed = redactSecretsInObject(parsed);
650
+ let scrubbedJson;
651
+ try {
652
+ scrubbedJson = JSON.stringify(scrubbed);
653
+ } catch (e) {
654
+ return void 0;
655
+ }
656
+ if (scrubbedJson === json) return void 0;
657
+ return { stringValue: scrubbedJson };
623
658
  }
624
659
  var TraceShipper = class {
625
660
  constructor(opts) {
626
661
  this.queue = [];
627
662
  this.inFlight = /* @__PURE__ */ new Set();
628
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
663
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
629
664
  this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
630
665
  this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
631
666
  this.enabled = opts.enabled !== false;
@@ -638,13 +673,46 @@ var TraceShipper = class {
638
673
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
639
674
  this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
640
675
  this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
641
- const localDebugger = typeof process !== "undefined" ? (_i = process.env) == null ? void 0 : _i.RAINDROP_LOCAL_DEBUGGER : void 0;
642
- if (localDebugger) {
643
- this.localDebuggerUrl = (_j = resolveLocalDebuggerBaseUrl(localDebugger)) != null ? _j : void 0;
644
- if (this.debug) {
645
- console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
676
+ this.localDebuggerUrl = (_i = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _i : void 0;
677
+ if (this.debug && this.localDebuggerUrl) {
678
+ console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
679
+ }
680
+ this.transformSpanHook = opts.transformSpan;
681
+ this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
682
+ }
683
+ /**
684
+ * Apply the user `transformSpan` hook (if any) followed by the default
685
+ * redactor (unless disabled). Returns either the (possibly new) span to
686
+ * ship, or `null` to drop the span entirely.
687
+ *
688
+ * Ordering: user hook runs first so callers can rewrite the span freely
689
+ * (rename attrs, add new ones, scrub things the default doesn't know
690
+ * about). The default redactor then runs on whatever the user produced,
691
+ * acting as the always-on floor for documented BYOK secrets. If the user
692
+ * sets `disableDefaultRedaction: true`, the floor is skipped.
693
+ *
694
+ * Fail-closed: if the user hook throws, the span is dropped — a buggy
695
+ * hook can never accidentally ship raw, un-redacted spans.
696
+ */
697
+ redactSpan(span) {
698
+ let current = span;
699
+ if (this.transformSpanHook) {
700
+ try {
701
+ const result = this.transformSpanHook(current);
702
+ if (result === null) return null;
703
+ if (result !== void 0) current = result;
704
+ } catch (err) {
705
+ if (this.debug) {
706
+ const msg = err instanceof Error ? err.message : String(err);
707
+ console.warn(`${this.prefix} transformSpan hook threw: ${msg}`);
708
+ }
709
+ return null;
646
710
  }
647
711
  }
712
+ if (!this.disableDefaultRedaction) {
713
+ current = defaultTransformSpan(current);
714
+ }
715
+ return current;
648
716
  }
649
717
  isDebugEnabled() {
650
718
  return this.debug;
@@ -662,8 +730,8 @@ var TraceShipper = class {
662
730
  ];
663
731
  if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
664
732
  const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
665
- if (this.localDebuggerUrl) {
666
- const openSpan = buildOtlpSpan({
733
+ this.mirrorToLocalDebugger(
734
+ buildOtlpSpan({
667
735
  ids: span.ids,
668
736
  name: span.name,
669
737
  startTimeUnixNano: span.startTimeUnixNano,
@@ -671,16 +739,21 @@ var TraceShipper = class {
671
739
  // placeholder — will be updated on endSpan
672
740
  attributes: span.attributes,
673
741
  status: { code: SpanStatusCode.UNSET }
674
- });
675
- const body = buildExportTraceServiceRequest([openSpan], this.serviceName, this.serviceVersion);
676
- mirrorTraceExportToLocalDebugger(body, {
677
- baseUrl: this.localDebuggerUrl,
678
- debug: false,
679
- sdkName: this.sdkName
680
- });
681
- }
742
+ })
743
+ );
682
744
  return span;
683
745
  }
746
+ mirrorToLocalDebugger(span) {
747
+ if (!this.localDebuggerUrl) return;
748
+ const redacted = this.redactSpan(span);
749
+ if (redacted === null) return;
750
+ const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
751
+ mirrorTraceExportToLocalDebugger(body, {
752
+ baseUrl: this.localDebuggerUrl,
753
+ debug: false,
754
+ sdkName: this.sdkName
755
+ });
756
+ }
684
757
  endSpan(span, extra) {
685
758
  var _a, _b;
686
759
  if (span.endTimeUnixNano) return;
@@ -702,14 +775,7 @@ var TraceShipper = class {
702
775
  status
703
776
  });
704
777
  this.enqueue(otlp);
705
- if (this.localDebuggerUrl) {
706
- const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
707
- mirrorTraceExportToLocalDebugger(body, {
708
- baseUrl: this.localDebuggerUrl,
709
- debug: false,
710
- sdkName: this.sdkName
711
- });
712
- }
778
+ this.mirrorToLocalDebugger(otlp);
713
779
  }
714
780
  createSpan(args) {
715
781
  var _a;
@@ -727,14 +793,7 @@ var TraceShipper = class {
727
793
  status: args.status
728
794
  });
729
795
  this.enqueue(otlp);
730
- if (this.localDebuggerUrl) {
731
- const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
732
- mirrorTraceExportToLocalDebugger(body, {
733
- baseUrl: this.localDebuggerUrl,
734
- debug: false,
735
- sdkName: this.sdkName
736
- });
737
- }
796
+ this.mirrorToLocalDebugger(otlp);
738
797
  }
739
798
  enqueue(span) {
740
799
  if (!this.enabled) return;
@@ -746,10 +805,12 @@ var TraceShipper = class {
746
805
  )}`
747
806
  );
748
807
  }
808
+ const redacted = this.redactSpan(span);
809
+ if (redacted === null) return;
749
810
  if (this.queue.length >= this.maxQueueSize) {
750
811
  this.queue.shift();
751
812
  }
752
- this.queue.push(span);
813
+ this.queue.push(redacted);
753
814
  if (this.queue.length >= this.maxBatchSize) {
754
815
  void this.flush().catch(() => {
755
816
  });
@@ -771,6 +832,7 @@ var TraceShipper = class {
771
832
  }
772
833
  while (this.queue.length > 0) {
773
834
  const batch = this.queue.splice(0, this.maxBatchSize);
835
+ if (!this.writeKey) continue;
774
836
  const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
775
837
  const url = `${this.baseUrl}traces`;
776
838
  if (this.debug) {
@@ -813,6 +875,157 @@ var TraceShipper = class {
813
875
  var import_async_hooks = require("async_hooks");
814
876
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
815
877
 
878
+ // src/internal/config.ts
879
+ var import_node_fs = require("fs");
880
+ var import_node_os = require("os");
881
+ var import_node_path = require("path");
882
+ function getPiAgentDirectory() {
883
+ var _a;
884
+ return (_a = process.env["PI_CODING_AGENT_DIR"]) != null ? _a : (0, import_node_path.join)((0, import_node_os.homedir)(), ".pi", "agent");
885
+ }
886
+ function loadConfig(projectDirectory) {
887
+ var _a, _b, _c, _d, _e, _f, _g;
888
+ let merged = {};
889
+ const configPaths = [
890
+ (0, import_node_path.join)(getPiAgentDirectory(), "raindrop.json"),
891
+ (0, import_node_path.join)(projectDirectory, ".pi", "raindrop.json")
892
+ ];
893
+ for (const configPath of configPaths) {
894
+ try {
895
+ if ((0, import_node_fs.existsSync)(configPath)) {
896
+ const content = (0, import_node_fs.readFileSync)(configPath, "utf-8");
897
+ const parsed = JSON.parse(content);
898
+ merged = { ...merged, ...parsed };
899
+ }
900
+ } catch (e) {
901
+ }
902
+ }
903
+ let eventMetadata;
904
+ const envMeta = process.env["RAINDROP_EVENT_METADATA"];
905
+ if (envMeta) {
906
+ try {
907
+ eventMetadata = JSON.parse(envMeta);
908
+ } catch (e) {
909
+ }
910
+ }
911
+ return {
912
+ writeKey: (_b = (_a = process.env["RAINDROP_WRITE_KEY"]) != null ? _a : merged.write_key) != null ? _b : "",
913
+ endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : merged.api_url) != null ? _d : "https://api.raindrop.ai/v1",
914
+ eventName: (_e = merged.event_name) != null ? _e : "pi_session",
915
+ debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_f = merged.debug) != null ? _f : false,
916
+ captureSystemPrompt: process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] !== void 0 ? process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] === "true" : (_g = merged.capture_system_prompt) != null ? _g : false,
917
+ eventMetadata,
918
+ localWorkshopUrl: resolveLocalWorkshopUrl(merged.local_workshop_url)
919
+ };
920
+ }
921
+ function resolveLocalWorkshopUrl(fileValue) {
922
+ const envValue = process.env["RAINDROP_LOCAL_WORKSHOP_URL"];
923
+ if (envValue !== void 0) {
924
+ if (envValue === "" || envValue.toLowerCase() === "null" || envValue.toLowerCase() === "false") {
925
+ return null;
926
+ }
927
+ return envValue;
928
+ }
929
+ return fileValue;
930
+ }
931
+
932
+ // package.json
933
+ var package_default = {
934
+ name: "@raindrop-ai/pi-agent",
935
+ version: "0.0.4",
936
+ description: "Raindrop observability for Pi Agent \u2014 automatic tracing via subscriber or pi-coding-agent extension",
937
+ type: "module",
938
+ license: "MIT",
939
+ repository: {
940
+ type: "git",
941
+ url: "git+https://github.com/raindrop-ai/raindrop-js.git",
942
+ directory: "packages/pi-agent"
943
+ },
944
+ homepage: "https://github.com/raindrop-ai/raindrop-js/tree/main/packages/pi-agent#readme",
945
+ bugs: {
946
+ url: "https://github.com/raindrop-ai/raindrop-js/issues"
947
+ },
948
+ keywords: [
949
+ "pi-package",
950
+ "raindrop",
951
+ "pi-agent",
952
+ "observability",
953
+ "tracing"
954
+ ],
955
+ exports: {
956
+ ".": {
957
+ types: "./dist/index.d.ts",
958
+ import: "./dist/index.js",
959
+ require: "./dist/index.cjs"
960
+ },
961
+ "./extension": {
962
+ types: "./dist/extension.d.ts",
963
+ import: "./dist/extension.js",
964
+ require: "./dist/extension.cjs"
965
+ }
966
+ },
967
+ pi: {
968
+ extensions: [
969
+ "./dist/extension.js"
970
+ ]
971
+ },
972
+ sideEffects: false,
973
+ files: [
974
+ "dist/**",
975
+ "README.md"
976
+ ],
977
+ scripts: {
978
+ build: "tsup",
979
+ dev: "tsup --watch",
980
+ clean: "rm -rf dist",
981
+ test: "vitest run",
982
+ "test:watch": "vitest"
983
+ },
984
+ peerDependencies: {
985
+ "@earendil-works/pi-agent-core": ">=0.74.0",
986
+ "@earendil-works/pi-coding-agent": ">=0.74.0"
987
+ },
988
+ peerDependenciesMeta: {
989
+ "@earendil-works/pi-coding-agent": {
990
+ optional: true
991
+ }
992
+ },
993
+ devDependencies: {
994
+ "@raindrop-ai/core": "workspace:*",
995
+ "@earendil-works/pi-agent-core": "^0.78.0",
996
+ "@earendil-works/pi-coding-agent": "^0.78.0",
997
+ "@types/node": "^20.11.17",
998
+ msw: "^2.12.7",
999
+ tsup: "^8.5.1",
1000
+ typescript: "^5.7.3",
1001
+ vitest: "^2.1.9"
1002
+ },
1003
+ tsup: {
1004
+ entry: [
1005
+ "src/index.ts",
1006
+ "src/extension.ts"
1007
+ ],
1008
+ format: [
1009
+ "cjs",
1010
+ "esm"
1011
+ ],
1012
+ dts: {
1013
+ resolve: true
1014
+ },
1015
+ clean: true,
1016
+ noExternal: [
1017
+ "@raindrop-ai/core"
1018
+ ]
1019
+ },
1020
+ publishConfig: {
1021
+ access: "public"
1022
+ }
1023
+ };
1024
+
1025
+ // src/version.ts
1026
+ var libraryName = package_default.name;
1027
+ var libraryVersion = package_default.version;
1028
+
816
1029
  // src/internal/shipper.ts
817
1030
  var EventShipper2 = class extends EventShipper {
818
1031
  constructor(opts) {
@@ -1283,25 +1496,33 @@ function extension(pi) {
1283
1496
  console.log(`[raindrop-ai/pi-agent] [${level}] ${message}`);
1284
1497
  }
1285
1498
  appLog("info", `Loading ${PLUGIN_NAME} v${libraryVersion}`);
1286
- if (!config.writeKey) {
1499
+ const resolvedLocalUrl = resolveLocalDebuggerBaseUrl(config.localWorkshopUrl);
1500
+ const hasLocalDestination = resolvedLocalUrl !== null;
1501
+ if (!config.writeKey && !hasLocalDestination) {
1287
1502
  appLog(
1288
1503
  "warn",
1289
- "RAINDROP_WRITE_KEY not set \u2014 Raindrop tracing disabled. Set it in your environment or in ~/.pi/agent/raindrop.json."
1504
+ "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."
1290
1505
  );
1291
1506
  return;
1292
1507
  }
1293
1508
  if (config.debug) {
1294
- appLog("info", `Raindrop tracing enabled (endpoint: ${config.endpoint})`);
1509
+ const destinations = [
1510
+ config.writeKey ? `cloud (${config.endpoint})` : null,
1511
+ resolvedLocalUrl ? `local Workshop (${resolvedLocalUrl})` : null
1512
+ ].filter(Boolean);
1513
+ appLog("info", `Raindrop tracing enabled \u2014 destinations: ${destinations.join(", ")}`);
1295
1514
  }
1296
1515
  const eventShipper = new EventShipper2({
1297
1516
  writeKey: config.writeKey,
1298
1517
  endpoint: config.endpoint,
1299
- debug: config.debug
1518
+ debug: config.debug,
1519
+ localDebuggerUrl: config.localWorkshopUrl
1300
1520
  });
1301
1521
  const traceShipper = new TraceShipper2({
1302
1522
  writeKey: config.writeKey,
1303
1523
  endpoint: config.endpoint,
1304
- debug: config.debug
1524
+ debug: config.debug,
1525
+ localDebuggerUrl: config.localWorkshopUrl
1305
1526
  });
1306
1527
  registerTracing(pi, config, eventShipper, traceShipper);
1307
1528
  }