@raindrop-ai/pi-agent 0.0.3 → 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.
- package/README.md +4 -4
- package/dist/{chunk-MHIMKMK7.js → chunk-EWIO36KH.js} +152 -34
- package/dist/extension.cjs +152 -34
- package/dist/extension.d.cts +2 -2
- package/dist/extension.d.ts +2 -2
- package/dist/extension.js +1 -1
- package/dist/index.cjs +152 -34
- package/dist/{index.d-CtwaEReY.d.cts → index.d-Cxs_NTx0.d.cts} +69 -0
- package/dist/{index.d-CtwaEReY.d.ts → index.d-Cxs_NTx0.d.ts} +69 -0
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @raindrop-ai/pi-agent
|
|
2
2
|
|
|
3
|
-
Automatic observability for [Pi Agent](https://github.com/
|
|
3
|
+
Automatic observability for [Pi Agent](https://github.com/earendil-works/pi) with [Raindrop](https://raindrop.ai). Captures agent runs, LLM generations, tool calls, and token usage.
|
|
4
4
|
|
|
5
5
|
Two entry points:
|
|
6
6
|
|
|
@@ -10,8 +10,8 @@ Two entry points:
|
|
|
10
10
|
## Quick Start — Programmatic
|
|
11
11
|
|
|
12
12
|
```typescript
|
|
13
|
-
import { Agent } from "@
|
|
14
|
-
import { getModel } from "@
|
|
13
|
+
import { Agent } from "@earendil-works/pi-agent-core";
|
|
14
|
+
import { getModel } from "@earendil-works/pi-ai";
|
|
15
15
|
import { createRaindropPiAgent } from "@raindrop-ai/pi-agent";
|
|
16
16
|
|
|
17
17
|
const raindrop = createRaindropPiAgent({
|
|
@@ -41,7 +41,7 @@ Set `RAINDROP_WRITE_KEY` in your environment. Traces appear automatically.
|
|
|
41
41
|
|
|
42
42
|
## Documentation
|
|
43
43
|
|
|
44
|
-
See the full [Pi Agent docs](https://
|
|
44
|
+
See the full [Pi Agent docs](https://www.raindrop.ai/docs/integrations/pi-agent/) for configuration, per-subscribe overrides, and extension settings.
|
|
45
45
|
|
|
46
46
|
## License
|
|
47
47
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ../core/dist/chunk-
|
|
1
|
+
// ../core/dist/chunk-VUNUOE2X.js
|
|
2
2
|
function getCrypto() {
|
|
3
3
|
const c = globalThis.crypto;
|
|
4
4
|
return c;
|
|
@@ -551,6 +551,95 @@ var EventShipper = class {
|
|
|
551
551
|
}
|
|
552
552
|
}
|
|
553
553
|
};
|
|
554
|
+
var DEFAULT_SECRET_KEY_NAMES = [
|
|
555
|
+
"apikey",
|
|
556
|
+
"apisecret",
|
|
557
|
+
"apitoken",
|
|
558
|
+
"secretaccesskey",
|
|
559
|
+
"sessiontoken",
|
|
560
|
+
"privatekey",
|
|
561
|
+
"privatekeyid",
|
|
562
|
+
"clientsecret",
|
|
563
|
+
"accesstoken",
|
|
564
|
+
"refreshtoken",
|
|
565
|
+
"oauthtoken",
|
|
566
|
+
"bearertoken",
|
|
567
|
+
"authorization",
|
|
568
|
+
"password",
|
|
569
|
+
"passphrase"
|
|
570
|
+
];
|
|
571
|
+
var REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
572
|
+
function normalizeKeyName(name) {
|
|
573
|
+
return name.toLowerCase().replace(/[-_.]/g, "");
|
|
574
|
+
}
|
|
575
|
+
function redactSecretsInObject(value, options) {
|
|
576
|
+
var _a, _b;
|
|
577
|
+
const normalizedSecretSet = buildSecretSet((_a = options == null ? void 0 : options.secretKeyNames) != null ? _a : DEFAULT_SECRET_KEY_NAMES);
|
|
578
|
+
const placeholder = (_b = options == null ? void 0 : options.placeholder) != null ? _b : REDACTED_PLACEHOLDER;
|
|
579
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
580
|
+
const walk = (node) => {
|
|
581
|
+
if (node === null || typeof node !== "object") return node;
|
|
582
|
+
if (seen.has(node)) return "[CIRCULAR]";
|
|
583
|
+
seen.add(node);
|
|
584
|
+
if (Array.isArray(node)) {
|
|
585
|
+
return node.map((item) => walk(item));
|
|
586
|
+
}
|
|
587
|
+
const out = {};
|
|
588
|
+
for (const [k, v] of Object.entries(node)) {
|
|
589
|
+
if (normalizedSecretSet.has(normalizeKeyName(k))) {
|
|
590
|
+
out[k] = placeholder;
|
|
591
|
+
} else {
|
|
592
|
+
out[k] = walk(v);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return out;
|
|
596
|
+
};
|
|
597
|
+
return walk(value);
|
|
598
|
+
}
|
|
599
|
+
function buildSecretSet(names) {
|
|
600
|
+
const set = /* @__PURE__ */ new Set();
|
|
601
|
+
for (const name of names) set.add(normalizeKeyName(name));
|
|
602
|
+
return set;
|
|
603
|
+
}
|
|
604
|
+
var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
|
|
605
|
+
"ai.request.providerOptions",
|
|
606
|
+
"ai.response.providerMetadata"
|
|
607
|
+
];
|
|
608
|
+
function defaultTransformSpan(span) {
|
|
609
|
+
const attrs = span.attributes;
|
|
610
|
+
if (!attrs || attrs.length === 0) return span;
|
|
611
|
+
let nextAttrs;
|
|
612
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
613
|
+
const attr = attrs[i];
|
|
614
|
+
const redacted = redactJsonAttributeValue(attr.key, attr.value);
|
|
615
|
+
if (redacted === void 0) continue;
|
|
616
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
617
|
+
nextAttrs[i] = { key: attr.key, value: redacted };
|
|
618
|
+
}
|
|
619
|
+
if (!nextAttrs) return span;
|
|
620
|
+
return { ...span, attributes: nextAttrs };
|
|
621
|
+
}
|
|
622
|
+
var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
|
|
623
|
+
function redactJsonAttributeValue(key, value) {
|
|
624
|
+
if (!REDACT_JSON_ATTRIBUTE_KEYS.has(key)) return void 0;
|
|
625
|
+
const json = value.stringValue;
|
|
626
|
+
if (typeof json !== "string" || json.length === 0) return void 0;
|
|
627
|
+
let parsed;
|
|
628
|
+
try {
|
|
629
|
+
parsed = JSON.parse(json);
|
|
630
|
+
} catch (e) {
|
|
631
|
+
return void 0;
|
|
632
|
+
}
|
|
633
|
+
const scrubbed = redactSecretsInObject(parsed);
|
|
634
|
+
let scrubbedJson;
|
|
635
|
+
try {
|
|
636
|
+
scrubbedJson = JSON.stringify(scrubbed);
|
|
637
|
+
} catch (e) {
|
|
638
|
+
return void 0;
|
|
639
|
+
}
|
|
640
|
+
if (scrubbedJson === json) return void 0;
|
|
641
|
+
return { stringValue: scrubbedJson };
|
|
642
|
+
}
|
|
554
643
|
var TraceShipper = class {
|
|
555
644
|
constructor(opts) {
|
|
556
645
|
this.queue = [];
|
|
@@ -572,6 +661,42 @@ var TraceShipper = class {
|
|
|
572
661
|
if (this.debug && this.localDebuggerUrl) {
|
|
573
662
|
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
574
663
|
}
|
|
664
|
+
this.transformSpanHook = opts.transformSpan;
|
|
665
|
+
this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Apply the user `transformSpan` hook (if any) followed by the default
|
|
669
|
+
* redactor (unless disabled). Returns either the (possibly new) span to
|
|
670
|
+
* ship, or `null` to drop the span entirely.
|
|
671
|
+
*
|
|
672
|
+
* Ordering: user hook runs first so callers can rewrite the span freely
|
|
673
|
+
* (rename attrs, add new ones, scrub things the default doesn't know
|
|
674
|
+
* about). The default redactor then runs on whatever the user produced,
|
|
675
|
+
* acting as the always-on floor for documented BYOK secrets. If the user
|
|
676
|
+
* sets `disableDefaultRedaction: true`, the floor is skipped.
|
|
677
|
+
*
|
|
678
|
+
* Fail-closed: if the user hook throws, the span is dropped — a buggy
|
|
679
|
+
* hook can never accidentally ship raw, un-redacted spans.
|
|
680
|
+
*/
|
|
681
|
+
redactSpan(span) {
|
|
682
|
+
let current = span;
|
|
683
|
+
if (this.transformSpanHook) {
|
|
684
|
+
try {
|
|
685
|
+
const result = this.transformSpanHook(current);
|
|
686
|
+
if (result === null) return null;
|
|
687
|
+
if (result !== void 0) current = result;
|
|
688
|
+
} catch (err) {
|
|
689
|
+
if (this.debug) {
|
|
690
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
691
|
+
console.warn(`${this.prefix} transformSpan hook threw: ${msg}`);
|
|
692
|
+
}
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (!this.disableDefaultRedaction) {
|
|
697
|
+
current = defaultTransformSpan(current);
|
|
698
|
+
}
|
|
699
|
+
return current;
|
|
575
700
|
}
|
|
576
701
|
isDebugEnabled() {
|
|
577
702
|
return this.debug;
|
|
@@ -589,8 +714,8 @@ var TraceShipper = class {
|
|
|
589
714
|
];
|
|
590
715
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
591
716
|
const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
592
|
-
|
|
593
|
-
|
|
717
|
+
this.mirrorToLocalDebugger(
|
|
718
|
+
buildOtlpSpan({
|
|
594
719
|
ids: span.ids,
|
|
595
720
|
name: span.name,
|
|
596
721
|
startTimeUnixNano: span.startTimeUnixNano,
|
|
@@ -598,16 +723,21 @@ var TraceShipper = class {
|
|
|
598
723
|
// placeholder — will be updated on endSpan
|
|
599
724
|
attributes: span.attributes,
|
|
600
725
|
status: { code: SpanStatusCode.UNSET }
|
|
601
|
-
})
|
|
602
|
-
|
|
603
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
604
|
-
baseUrl: this.localDebuggerUrl,
|
|
605
|
-
debug: false,
|
|
606
|
-
sdkName: this.sdkName
|
|
607
|
-
});
|
|
608
|
-
}
|
|
726
|
+
})
|
|
727
|
+
);
|
|
609
728
|
return span;
|
|
610
729
|
}
|
|
730
|
+
mirrorToLocalDebugger(span) {
|
|
731
|
+
if (!this.localDebuggerUrl) return;
|
|
732
|
+
const redacted = this.redactSpan(span);
|
|
733
|
+
if (redacted === null) return;
|
|
734
|
+
const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
|
|
735
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
736
|
+
baseUrl: this.localDebuggerUrl,
|
|
737
|
+
debug: false,
|
|
738
|
+
sdkName: this.sdkName
|
|
739
|
+
});
|
|
740
|
+
}
|
|
611
741
|
endSpan(span, extra) {
|
|
612
742
|
var _a, _b;
|
|
613
743
|
if (span.endTimeUnixNano) return;
|
|
@@ -629,14 +759,7 @@ var TraceShipper = class {
|
|
|
629
759
|
status
|
|
630
760
|
});
|
|
631
761
|
this.enqueue(otlp);
|
|
632
|
-
|
|
633
|
-
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
634
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
635
|
-
baseUrl: this.localDebuggerUrl,
|
|
636
|
-
debug: false,
|
|
637
|
-
sdkName: this.sdkName
|
|
638
|
-
});
|
|
639
|
-
}
|
|
762
|
+
this.mirrorToLocalDebugger(otlp);
|
|
640
763
|
}
|
|
641
764
|
createSpan(args) {
|
|
642
765
|
var _a;
|
|
@@ -654,14 +777,7 @@ var TraceShipper = class {
|
|
|
654
777
|
status: args.status
|
|
655
778
|
});
|
|
656
779
|
this.enqueue(otlp);
|
|
657
|
-
|
|
658
|
-
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
659
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
660
|
-
baseUrl: this.localDebuggerUrl,
|
|
661
|
-
debug: false,
|
|
662
|
-
sdkName: this.sdkName
|
|
663
|
-
});
|
|
664
|
-
}
|
|
780
|
+
this.mirrorToLocalDebugger(otlp);
|
|
665
781
|
}
|
|
666
782
|
enqueue(span) {
|
|
667
783
|
if (!this.enabled) return;
|
|
@@ -673,10 +789,12 @@ var TraceShipper = class {
|
|
|
673
789
|
)}`
|
|
674
790
|
);
|
|
675
791
|
}
|
|
792
|
+
const redacted = this.redactSpan(span);
|
|
793
|
+
if (redacted === null) return;
|
|
676
794
|
if (this.queue.length >= this.maxQueueSize) {
|
|
677
795
|
this.queue.shift();
|
|
678
796
|
}
|
|
679
|
-
this.queue.push(
|
|
797
|
+
this.queue.push(redacted);
|
|
680
798
|
if (this.queue.length >= this.maxBatchSize) {
|
|
681
799
|
void this.flush().catch(() => {
|
|
682
800
|
});
|
|
@@ -744,7 +862,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
|
|
|
744
862
|
// package.json
|
|
745
863
|
var package_default = {
|
|
746
864
|
name: "@raindrop-ai/pi-agent",
|
|
747
|
-
version: "0.0.
|
|
865
|
+
version: "0.0.4",
|
|
748
866
|
description: "Raindrop observability for Pi Agent \u2014 automatic tracing via subscriber or pi-coding-agent extension",
|
|
749
867
|
type: "module",
|
|
750
868
|
license: "MIT",
|
|
@@ -794,18 +912,18 @@ var package_default = {
|
|
|
794
912
|
"test:watch": "vitest"
|
|
795
913
|
},
|
|
796
914
|
peerDependencies: {
|
|
797
|
-
"@
|
|
798
|
-
"@
|
|
915
|
+
"@earendil-works/pi-agent-core": ">=0.74.0",
|
|
916
|
+
"@earendil-works/pi-coding-agent": ">=0.74.0"
|
|
799
917
|
},
|
|
800
918
|
peerDependenciesMeta: {
|
|
801
|
-
"@
|
|
919
|
+
"@earendil-works/pi-coding-agent": {
|
|
802
920
|
optional: true
|
|
803
921
|
}
|
|
804
922
|
},
|
|
805
923
|
devDependencies: {
|
|
806
924
|
"@raindrop-ai/core": "workspace:*",
|
|
807
|
-
"@
|
|
808
|
-
"@
|
|
925
|
+
"@earendil-works/pi-agent-core": "^0.78.0",
|
|
926
|
+
"@earendil-works/pi-coding-agent": "^0.78.0",
|
|
809
927
|
"@types/node": "^20.11.17",
|
|
810
928
|
msw: "^2.12.7",
|
|
811
929
|
tsup: "^8.5.1",
|
package/dist/extension.cjs
CHANGED
|
@@ -25,7 +25,7 @@ __export(extension_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(extension_exports);
|
|
27
27
|
|
|
28
|
-
// ../core/dist/chunk-
|
|
28
|
+
// ../core/dist/chunk-VUNUOE2X.js
|
|
29
29
|
function getCrypto() {
|
|
30
30
|
const c = globalThis.crypto;
|
|
31
31
|
return c;
|
|
@@ -567,6 +567,95 @@ var EventShipper = class {
|
|
|
567
567
|
}
|
|
568
568
|
}
|
|
569
569
|
};
|
|
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, "");
|
|
590
|
+
}
|
|
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 };
|
|
658
|
+
}
|
|
570
659
|
var TraceShipper = class {
|
|
571
660
|
constructor(opts) {
|
|
572
661
|
this.queue = [];
|
|
@@ -588,6 +677,42 @@ var TraceShipper = class {
|
|
|
588
677
|
if (this.debug && this.localDebuggerUrl) {
|
|
589
678
|
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
590
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;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
if (!this.disableDefaultRedaction) {
|
|
713
|
+
current = defaultTransformSpan(current);
|
|
714
|
+
}
|
|
715
|
+
return current;
|
|
591
716
|
}
|
|
592
717
|
isDebugEnabled() {
|
|
593
718
|
return this.debug;
|
|
@@ -605,8 +730,8 @@ var TraceShipper = class {
|
|
|
605
730
|
];
|
|
606
731
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
607
732
|
const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
608
|
-
|
|
609
|
-
|
|
733
|
+
this.mirrorToLocalDebugger(
|
|
734
|
+
buildOtlpSpan({
|
|
610
735
|
ids: span.ids,
|
|
611
736
|
name: span.name,
|
|
612
737
|
startTimeUnixNano: span.startTimeUnixNano,
|
|
@@ -614,16 +739,21 @@ var TraceShipper = class {
|
|
|
614
739
|
// placeholder — will be updated on endSpan
|
|
615
740
|
attributes: span.attributes,
|
|
616
741
|
status: { code: SpanStatusCode.UNSET }
|
|
617
|
-
})
|
|
618
|
-
|
|
619
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
620
|
-
baseUrl: this.localDebuggerUrl,
|
|
621
|
-
debug: false,
|
|
622
|
-
sdkName: this.sdkName
|
|
623
|
-
});
|
|
624
|
-
}
|
|
742
|
+
})
|
|
743
|
+
);
|
|
625
744
|
return span;
|
|
626
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
|
+
}
|
|
627
757
|
endSpan(span, extra) {
|
|
628
758
|
var _a, _b;
|
|
629
759
|
if (span.endTimeUnixNano) return;
|
|
@@ -645,14 +775,7 @@ var TraceShipper = class {
|
|
|
645
775
|
status
|
|
646
776
|
});
|
|
647
777
|
this.enqueue(otlp);
|
|
648
|
-
|
|
649
|
-
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
650
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
651
|
-
baseUrl: this.localDebuggerUrl,
|
|
652
|
-
debug: false,
|
|
653
|
-
sdkName: this.sdkName
|
|
654
|
-
});
|
|
655
|
-
}
|
|
778
|
+
this.mirrorToLocalDebugger(otlp);
|
|
656
779
|
}
|
|
657
780
|
createSpan(args) {
|
|
658
781
|
var _a;
|
|
@@ -670,14 +793,7 @@ var TraceShipper = class {
|
|
|
670
793
|
status: args.status
|
|
671
794
|
});
|
|
672
795
|
this.enqueue(otlp);
|
|
673
|
-
|
|
674
|
-
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
675
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
676
|
-
baseUrl: this.localDebuggerUrl,
|
|
677
|
-
debug: false,
|
|
678
|
-
sdkName: this.sdkName
|
|
679
|
-
});
|
|
680
|
-
}
|
|
796
|
+
this.mirrorToLocalDebugger(otlp);
|
|
681
797
|
}
|
|
682
798
|
enqueue(span) {
|
|
683
799
|
if (!this.enabled) return;
|
|
@@ -689,10 +805,12 @@ var TraceShipper = class {
|
|
|
689
805
|
)}`
|
|
690
806
|
);
|
|
691
807
|
}
|
|
808
|
+
const redacted = this.redactSpan(span);
|
|
809
|
+
if (redacted === null) return;
|
|
692
810
|
if (this.queue.length >= this.maxQueueSize) {
|
|
693
811
|
this.queue.shift();
|
|
694
812
|
}
|
|
695
|
-
this.queue.push(
|
|
813
|
+
this.queue.push(redacted);
|
|
696
814
|
if (this.queue.length >= this.maxBatchSize) {
|
|
697
815
|
void this.flush().catch(() => {
|
|
698
816
|
});
|
|
@@ -814,7 +932,7 @@ function resolveLocalWorkshopUrl(fileValue) {
|
|
|
814
932
|
// package.json
|
|
815
933
|
var package_default = {
|
|
816
934
|
name: "@raindrop-ai/pi-agent",
|
|
817
|
-
version: "0.0.
|
|
935
|
+
version: "0.0.4",
|
|
818
936
|
description: "Raindrop observability for Pi Agent \u2014 automatic tracing via subscriber or pi-coding-agent extension",
|
|
819
937
|
type: "module",
|
|
820
938
|
license: "MIT",
|
|
@@ -864,18 +982,18 @@ var package_default = {
|
|
|
864
982
|
"test:watch": "vitest"
|
|
865
983
|
},
|
|
866
984
|
peerDependencies: {
|
|
867
|
-
"@
|
|
868
|
-
"@
|
|
985
|
+
"@earendil-works/pi-agent-core": ">=0.74.0",
|
|
986
|
+
"@earendil-works/pi-coding-agent": ">=0.74.0"
|
|
869
987
|
},
|
|
870
988
|
peerDependenciesMeta: {
|
|
871
|
-
"@
|
|
989
|
+
"@earendil-works/pi-coding-agent": {
|
|
872
990
|
optional: true
|
|
873
991
|
}
|
|
874
992
|
},
|
|
875
993
|
devDependencies: {
|
|
876
994
|
"@raindrop-ai/core": "workspace:*",
|
|
877
|
-
"@
|
|
878
|
-
"@
|
|
995
|
+
"@earendil-works/pi-agent-core": "^0.78.0",
|
|
996
|
+
"@earendil-works/pi-coding-agent": "^0.78.0",
|
|
879
997
|
"@types/node": "^20.11.17",
|
|
880
998
|
msw: "^2.12.7",
|
|
881
999
|
tsup: "^8.5.1",
|
package/dist/extension.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ExtensionAPI } from '@
|
|
2
|
-
import { E as EventShipper$1, T as TraceShipper$1, O as OtlpSpan } from './index.d-
|
|
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;
|
package/dist/extension.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ExtensionAPI } from '@
|
|
2
|
-
import { E as EventShipper$1, T as TraceShipper$1, O as OtlpSpan } from './index.d-
|
|
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;
|
package/dist/extension.js
CHANGED
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-
|
|
27
|
+
// ../core/dist/chunk-VUNUOE2X.js
|
|
28
28
|
function getCrypto() {
|
|
29
29
|
const c = globalThis.crypto;
|
|
30
30
|
return c;
|
|
@@ -574,6 +574,95 @@ var EventShipper = class {
|
|
|
574
574
|
}
|
|
575
575
|
}
|
|
576
576
|
};
|
|
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, "");
|
|
597
|
+
}
|
|
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 };
|
|
665
|
+
}
|
|
577
666
|
var TraceShipper = class {
|
|
578
667
|
constructor(opts) {
|
|
579
668
|
this.queue = [];
|
|
@@ -595,6 +684,42 @@ var TraceShipper = class {
|
|
|
595
684
|
if (this.debug && this.localDebuggerUrl) {
|
|
596
685
|
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
597
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;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (!this.disableDefaultRedaction) {
|
|
720
|
+
current = defaultTransformSpan(current);
|
|
721
|
+
}
|
|
722
|
+
return current;
|
|
598
723
|
}
|
|
599
724
|
isDebugEnabled() {
|
|
600
725
|
return this.debug;
|
|
@@ -612,8 +737,8 @@ var TraceShipper = class {
|
|
|
612
737
|
];
|
|
613
738
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
614
739
|
const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
615
|
-
|
|
616
|
-
|
|
740
|
+
this.mirrorToLocalDebugger(
|
|
741
|
+
buildOtlpSpan({
|
|
617
742
|
ids: span.ids,
|
|
618
743
|
name: span.name,
|
|
619
744
|
startTimeUnixNano: span.startTimeUnixNano,
|
|
@@ -621,16 +746,21 @@ var TraceShipper = class {
|
|
|
621
746
|
// placeholder — will be updated on endSpan
|
|
622
747
|
attributes: span.attributes,
|
|
623
748
|
status: { code: SpanStatusCode.UNSET }
|
|
624
|
-
})
|
|
625
|
-
|
|
626
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
627
|
-
baseUrl: this.localDebuggerUrl,
|
|
628
|
-
debug: false,
|
|
629
|
-
sdkName: this.sdkName
|
|
630
|
-
});
|
|
631
|
-
}
|
|
749
|
+
})
|
|
750
|
+
);
|
|
632
751
|
return span;
|
|
633
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
|
+
}
|
|
634
764
|
endSpan(span, extra) {
|
|
635
765
|
var _a, _b;
|
|
636
766
|
if (span.endTimeUnixNano) return;
|
|
@@ -652,14 +782,7 @@ var TraceShipper = class {
|
|
|
652
782
|
status
|
|
653
783
|
});
|
|
654
784
|
this.enqueue(otlp);
|
|
655
|
-
|
|
656
|
-
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
657
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
658
|
-
baseUrl: this.localDebuggerUrl,
|
|
659
|
-
debug: false,
|
|
660
|
-
sdkName: this.sdkName
|
|
661
|
-
});
|
|
662
|
-
}
|
|
785
|
+
this.mirrorToLocalDebugger(otlp);
|
|
663
786
|
}
|
|
664
787
|
createSpan(args) {
|
|
665
788
|
var _a;
|
|
@@ -677,14 +800,7 @@ var TraceShipper = class {
|
|
|
677
800
|
status: args.status
|
|
678
801
|
});
|
|
679
802
|
this.enqueue(otlp);
|
|
680
|
-
|
|
681
|
-
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
682
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
683
|
-
baseUrl: this.localDebuggerUrl,
|
|
684
|
-
debug: false,
|
|
685
|
-
sdkName: this.sdkName
|
|
686
|
-
});
|
|
687
|
-
}
|
|
803
|
+
this.mirrorToLocalDebugger(otlp);
|
|
688
804
|
}
|
|
689
805
|
enqueue(span) {
|
|
690
806
|
if (!this.enabled) return;
|
|
@@ -696,10 +812,12 @@ var TraceShipper = class {
|
|
|
696
812
|
)}`
|
|
697
813
|
);
|
|
698
814
|
}
|
|
815
|
+
const redacted = this.redactSpan(span);
|
|
816
|
+
if (redacted === null) return;
|
|
699
817
|
if (this.queue.length >= this.maxQueueSize) {
|
|
700
818
|
this.queue.shift();
|
|
701
819
|
}
|
|
702
|
-
this.queue.push(
|
|
820
|
+
this.queue.push(redacted);
|
|
703
821
|
if (this.queue.length >= this.maxBatchSize) {
|
|
704
822
|
void this.flush().catch(() => {
|
|
705
823
|
});
|
|
@@ -767,7 +885,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
|
|
|
767
885
|
// package.json
|
|
768
886
|
var package_default = {
|
|
769
887
|
name: "@raindrop-ai/pi-agent",
|
|
770
|
-
version: "0.0.
|
|
888
|
+
version: "0.0.4",
|
|
771
889
|
description: "Raindrop observability for Pi Agent \u2014 automatic tracing via subscriber or pi-coding-agent extension",
|
|
772
890
|
type: "module",
|
|
773
891
|
license: "MIT",
|
|
@@ -817,18 +935,18 @@ var package_default = {
|
|
|
817
935
|
"test:watch": "vitest"
|
|
818
936
|
},
|
|
819
937
|
peerDependencies: {
|
|
820
|
-
"@
|
|
821
|
-
"@
|
|
938
|
+
"@earendil-works/pi-agent-core": ">=0.74.0",
|
|
939
|
+
"@earendil-works/pi-coding-agent": ">=0.74.0"
|
|
822
940
|
},
|
|
823
941
|
peerDependenciesMeta: {
|
|
824
|
-
"@
|
|
942
|
+
"@earendil-works/pi-coding-agent": {
|
|
825
943
|
optional: true
|
|
826
944
|
}
|
|
827
945
|
},
|
|
828
946
|
devDependencies: {
|
|
829
947
|
"@raindrop-ai/core": "workspace:*",
|
|
830
|
-
"@
|
|
831
|
-
"@
|
|
948
|
+
"@earendil-works/pi-agent-core": "^0.78.0",
|
|
949
|
+
"@earendil-works/pi-coding-agent": "^0.78.0",
|
|
832
950
|
"@types/node": "^20.11.17",
|
|
833
951
|
msw: "^2.12.7",
|
|
834
952
|
tsup: "^8.5.1",
|
|
@@ -117,6 +117,23 @@ declare class EventShipper {
|
|
|
117
117
|
identify(users: IdentifyInput | IdentifyInput[]): Promise<void>;
|
|
118
118
|
private flushOne;
|
|
119
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Hook fired per OTLP span right before the span is shipped (to the Raindrop
|
|
122
|
+
* API and to a local debugger). Lets callers inspect, rewrite, or drop the
|
|
123
|
+
* entire span — not just individual attributes — which is more flexible than
|
|
124
|
+
* an attribute-level hook (you can rename attributes, add new ones, drop the
|
|
125
|
+
* span outright, etc.).
|
|
126
|
+
*
|
|
127
|
+
* Return values:
|
|
128
|
+
* - `undefined` or the same span: ship the span unchanged.
|
|
129
|
+
* - a new `OtlpSpan`: ship the returned span in place of the original.
|
|
130
|
+
* - `null`: drop the span entirely from every ship path.
|
|
131
|
+
*
|
|
132
|
+
* The hook runs on the hot path — keep it synchronous and side-effect-free.
|
|
133
|
+
* If the hook throws, the span is dropped (fail-closed) so a buggy hook can
|
|
134
|
+
* never accidentally ship raw, un-redacted spans.
|
|
135
|
+
*/
|
|
136
|
+
type TransformSpanHook = (span: OtlpSpan) => OtlpSpan | null | undefined;
|
|
120
137
|
|
|
121
138
|
type InternalSpan = {
|
|
122
139
|
ids: SpanIds;
|
|
@@ -142,6 +159,40 @@ type TraceShipperOptions = {
|
|
|
142
159
|
* Pass `null` to opt out of all mirroring (including auto-detect).
|
|
143
160
|
*/
|
|
144
161
|
localDebuggerUrl?: string | null;
|
|
162
|
+
/**
|
|
163
|
+
* Per-span hook that fires for every OTLP span right before the span is
|
|
164
|
+
* shipped (both to the Raindrop API and to a local debugger). Lets callers
|
|
165
|
+
* inspect, rewrite, or drop entire spans — rename attributes, add new ones,
|
|
166
|
+
* scrub additional secret-shaped values inside `ai.prompt.messages` /
|
|
167
|
+
* `ai.toolCall.args`, etc.
|
|
168
|
+
*
|
|
169
|
+
* Return values:
|
|
170
|
+
* - `undefined` or the same span reference: ship the span unchanged.
|
|
171
|
+
* - a new `OtlpSpan`: ship the returned span in place of the original.
|
|
172
|
+
* - `null`: drop the span entirely from every ship path.
|
|
173
|
+
*
|
|
174
|
+
* The hook runs BEFORE the default redactor (which is the always-on floor
|
|
175
|
+
* for documented BYOK secrets). The default redactor still runs on the
|
|
176
|
+
* post-transform span unless `disableDefaultRedaction` is set, so even if
|
|
177
|
+
* a custom transform overlooks a secret-shaped attribute, the floor catches
|
|
178
|
+
* it.
|
|
179
|
+
*
|
|
180
|
+
* The hook runs on the hot path — keep it synchronous and side-effect-free.
|
|
181
|
+
* If the hook itself throws, the span is dropped (fail-closed) so a buggy
|
|
182
|
+
* hook can never accidentally ship raw, un-redacted spans.
|
|
183
|
+
*/
|
|
184
|
+
transformSpan?: TransformSpanHook;
|
|
185
|
+
/**
|
|
186
|
+
* Disable the built-in default span transformer (which scrubs documented
|
|
187
|
+
* secret-shaped properties — `apiKey`, `secretAccessKey`, `privateKey`,
|
|
188
|
+
* etc. — inside `ai.request.providerOptions` and
|
|
189
|
+
* `ai.response.providerMetadata`).
|
|
190
|
+
*
|
|
191
|
+
* Default: `false` (i.e. default redaction is on). Setting this to `true`
|
|
192
|
+
* disables the floor entirely; provide a custom `transformSpan` if you
|
|
193
|
+
* still want some redaction in that case.
|
|
194
|
+
*/
|
|
195
|
+
disableDefaultRedaction?: boolean;
|
|
145
196
|
};
|
|
146
197
|
declare class TraceShipper {
|
|
147
198
|
private baseUrl;
|
|
@@ -161,7 +212,24 @@ declare class TraceShipper {
|
|
|
161
212
|
private inFlight;
|
|
162
213
|
/** URL of the local debugger / Workshop daemon, when one is reachable. */
|
|
163
214
|
private localDebuggerUrl;
|
|
215
|
+
private transformSpanHook;
|
|
216
|
+
private disableDefaultRedaction;
|
|
164
217
|
constructor(opts: TraceShipperOptions);
|
|
218
|
+
/**
|
|
219
|
+
* Apply the user `transformSpan` hook (if any) followed by the default
|
|
220
|
+
* redactor (unless disabled). Returns either the (possibly new) span to
|
|
221
|
+
* ship, or `null` to drop the span entirely.
|
|
222
|
+
*
|
|
223
|
+
* Ordering: user hook runs first so callers can rewrite the span freely
|
|
224
|
+
* (rename attrs, add new ones, scrub things the default doesn't know
|
|
225
|
+
* about). The default redactor then runs on whatever the user produced,
|
|
226
|
+
* acting as the always-on floor for documented BYOK secrets. If the user
|
|
227
|
+
* sets `disableDefaultRedaction: true`, the floor is skipped.
|
|
228
|
+
*
|
|
229
|
+
* Fail-closed: if the user hook throws, the span is dropped — a buggy
|
|
230
|
+
* hook can never accidentally ship raw, un-redacted spans.
|
|
231
|
+
*/
|
|
232
|
+
private redactSpan;
|
|
165
233
|
isDebugEnabled(): boolean;
|
|
166
234
|
private authHeaders;
|
|
167
235
|
startSpan(args: {
|
|
@@ -175,6 +243,7 @@ declare class TraceShipper {
|
|
|
175
243
|
attributes?: Array<OtlpKeyValue | undefined>;
|
|
176
244
|
startTimeUnixNano?: string;
|
|
177
245
|
}): InternalSpan;
|
|
246
|
+
private mirrorToLocalDebugger;
|
|
178
247
|
endSpan(span: InternalSpan, extra?: {
|
|
179
248
|
attributes?: InternalSpan["attributes"];
|
|
180
249
|
error?: unknown;
|
|
@@ -117,6 +117,23 @@ declare class EventShipper {
|
|
|
117
117
|
identify(users: IdentifyInput | IdentifyInput[]): Promise<void>;
|
|
118
118
|
private flushOne;
|
|
119
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Hook fired per OTLP span right before the span is shipped (to the Raindrop
|
|
122
|
+
* API and to a local debugger). Lets callers inspect, rewrite, or drop the
|
|
123
|
+
* entire span — not just individual attributes — which is more flexible than
|
|
124
|
+
* an attribute-level hook (you can rename attributes, add new ones, drop the
|
|
125
|
+
* span outright, etc.).
|
|
126
|
+
*
|
|
127
|
+
* Return values:
|
|
128
|
+
* - `undefined` or the same span: ship the span unchanged.
|
|
129
|
+
* - a new `OtlpSpan`: ship the returned span in place of the original.
|
|
130
|
+
* - `null`: drop the span entirely from every ship path.
|
|
131
|
+
*
|
|
132
|
+
* The hook runs on the hot path — keep it synchronous and side-effect-free.
|
|
133
|
+
* If the hook throws, the span is dropped (fail-closed) so a buggy hook can
|
|
134
|
+
* never accidentally ship raw, un-redacted spans.
|
|
135
|
+
*/
|
|
136
|
+
type TransformSpanHook = (span: OtlpSpan) => OtlpSpan | null | undefined;
|
|
120
137
|
|
|
121
138
|
type InternalSpan = {
|
|
122
139
|
ids: SpanIds;
|
|
@@ -142,6 +159,40 @@ type TraceShipperOptions = {
|
|
|
142
159
|
* Pass `null` to opt out of all mirroring (including auto-detect).
|
|
143
160
|
*/
|
|
144
161
|
localDebuggerUrl?: string | null;
|
|
162
|
+
/**
|
|
163
|
+
* Per-span hook that fires for every OTLP span right before the span is
|
|
164
|
+
* shipped (both to the Raindrop API and to a local debugger). Lets callers
|
|
165
|
+
* inspect, rewrite, or drop entire spans — rename attributes, add new ones,
|
|
166
|
+
* scrub additional secret-shaped values inside `ai.prompt.messages` /
|
|
167
|
+
* `ai.toolCall.args`, etc.
|
|
168
|
+
*
|
|
169
|
+
* Return values:
|
|
170
|
+
* - `undefined` or the same span reference: ship the span unchanged.
|
|
171
|
+
* - a new `OtlpSpan`: ship the returned span in place of the original.
|
|
172
|
+
* - `null`: drop the span entirely from every ship path.
|
|
173
|
+
*
|
|
174
|
+
* The hook runs BEFORE the default redactor (which is the always-on floor
|
|
175
|
+
* for documented BYOK secrets). The default redactor still runs on the
|
|
176
|
+
* post-transform span unless `disableDefaultRedaction` is set, so even if
|
|
177
|
+
* a custom transform overlooks a secret-shaped attribute, the floor catches
|
|
178
|
+
* it.
|
|
179
|
+
*
|
|
180
|
+
* The hook runs on the hot path — keep it synchronous and side-effect-free.
|
|
181
|
+
* If the hook itself throws, the span is dropped (fail-closed) so a buggy
|
|
182
|
+
* hook can never accidentally ship raw, un-redacted spans.
|
|
183
|
+
*/
|
|
184
|
+
transformSpan?: TransformSpanHook;
|
|
185
|
+
/**
|
|
186
|
+
* Disable the built-in default span transformer (which scrubs documented
|
|
187
|
+
* secret-shaped properties — `apiKey`, `secretAccessKey`, `privateKey`,
|
|
188
|
+
* etc. — inside `ai.request.providerOptions` and
|
|
189
|
+
* `ai.response.providerMetadata`).
|
|
190
|
+
*
|
|
191
|
+
* Default: `false` (i.e. default redaction is on). Setting this to `true`
|
|
192
|
+
* disables the floor entirely; provide a custom `transformSpan` if you
|
|
193
|
+
* still want some redaction in that case.
|
|
194
|
+
*/
|
|
195
|
+
disableDefaultRedaction?: boolean;
|
|
145
196
|
};
|
|
146
197
|
declare class TraceShipper {
|
|
147
198
|
private baseUrl;
|
|
@@ -161,7 +212,24 @@ declare class TraceShipper {
|
|
|
161
212
|
private inFlight;
|
|
162
213
|
/** URL of the local debugger / Workshop daemon, when one is reachable. */
|
|
163
214
|
private localDebuggerUrl;
|
|
215
|
+
private transformSpanHook;
|
|
216
|
+
private disableDefaultRedaction;
|
|
164
217
|
constructor(opts: TraceShipperOptions);
|
|
218
|
+
/**
|
|
219
|
+
* Apply the user `transformSpan` hook (if any) followed by the default
|
|
220
|
+
* redactor (unless disabled). Returns either the (possibly new) span to
|
|
221
|
+
* ship, or `null` to drop the span entirely.
|
|
222
|
+
*
|
|
223
|
+
* Ordering: user hook runs first so callers can rewrite the span freely
|
|
224
|
+
* (rename attrs, add new ones, scrub things the default doesn't know
|
|
225
|
+
* about). The default redactor then runs on whatever the user produced,
|
|
226
|
+
* acting as the always-on floor for documented BYOK secrets. If the user
|
|
227
|
+
* sets `disableDefaultRedaction: true`, the floor is skipped.
|
|
228
|
+
*
|
|
229
|
+
* Fail-closed: if the user hook throws, the span is dropped — a buggy
|
|
230
|
+
* hook can never accidentally ship raw, un-redacted spans.
|
|
231
|
+
*/
|
|
232
|
+
private redactSpan;
|
|
165
233
|
isDebugEnabled(): boolean;
|
|
166
234
|
private authHeaders;
|
|
167
235
|
startSpan(args: {
|
|
@@ -175,6 +243,7 @@ declare class TraceShipper {
|
|
|
175
243
|
attributes?: Array<OtlpKeyValue | undefined>;
|
|
176
244
|
startTimeUnixNano?: string;
|
|
177
245
|
}): InternalSpan;
|
|
246
|
+
private mirrorToLocalDebugger;
|
|
178
247
|
endSpan(span: InternalSpan, extra?: {
|
|
179
248
|
attributes?: InternalSpan["attributes"];
|
|
180
249
|
error?: unknown;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Agent } from '@
|
|
2
|
-
import { A as Attachment } from './index.d-
|
|
1
|
+
import { Agent } from '@earendil-works/pi-agent-core';
|
|
2
|
+
import { A as Attachment } from './index.d-Cxs_NTx0.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Options for the Raindrop Pi Agent client.
|
|
@@ -125,7 +125,7 @@ interface RaindropPiAgentClient {
|
|
|
125
125
|
*
|
|
126
126
|
* @example
|
|
127
127
|
* ```typescript
|
|
128
|
-
* import { Agent } from "@
|
|
128
|
+
* import { Agent } from "@earendil-works/pi-agent-core";
|
|
129
129
|
* import { createRaindropPiAgent } from "@raindrop-ai/pi-agent";
|
|
130
130
|
*
|
|
131
131
|
* const raindrop = createRaindropPiAgent({
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Agent } from '@
|
|
2
|
-
import { A as Attachment } from './index.d-
|
|
1
|
+
import { Agent } from '@earendil-works/pi-agent-core';
|
|
2
|
+
import { A as Attachment } from './index.d-Cxs_NTx0.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Options for the Raindrop Pi Agent client.
|
|
@@ -125,7 +125,7 @@ interface RaindropPiAgentClient {
|
|
|
125
125
|
*
|
|
126
126
|
* @example
|
|
127
127
|
* ```typescript
|
|
128
|
-
* import { Agent } from "@
|
|
128
|
+
* import { Agent } from "@earendil-works/pi-agent-core";
|
|
129
129
|
* import { createRaindropPiAgent } from "@raindrop-ai/pi-agent";
|
|
130
130
|
*
|
|
131
131
|
* const raindrop = createRaindropPiAgent({
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
resolveLocalDebuggerBaseUrl,
|
|
17
17
|
safeStringify,
|
|
18
18
|
truncate
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-EWIO36KH.js";
|
|
20
20
|
|
|
21
21
|
// src/internal/subscriber.ts
|
|
22
22
|
function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, options, debug) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raindrop-ai/pi-agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
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",
|
|
@@ -43,23 +43,23 @@
|
|
|
43
43
|
"README.md"
|
|
44
44
|
],
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@
|
|
47
|
-
"@
|
|
46
|
+
"@earendil-works/pi-agent-core": ">=0.74.0",
|
|
47
|
+
"@earendil-works/pi-coding-agent": ">=0.74.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependenciesMeta": {
|
|
50
|
-
"@
|
|
50
|
+
"@earendil-works/pi-coding-agent": {
|
|
51
51
|
"optional": true
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@
|
|
56
|
-
"@
|
|
55
|
+
"@earendil-works/pi-agent-core": "^0.78.0",
|
|
56
|
+
"@earendil-works/pi-coding-agent": "^0.78.0",
|
|
57
57
|
"@types/node": "^20.11.17",
|
|
58
58
|
"msw": "^2.12.7",
|
|
59
59
|
"tsup": "^8.5.1",
|
|
60
60
|
"typescript": "^5.7.3",
|
|
61
61
|
"vitest": "^2.1.9",
|
|
62
|
-
"@raindrop-ai/core": "0.0.
|
|
62
|
+
"@raindrop-ai/core": "0.0.2"
|
|
63
63
|
},
|
|
64
64
|
"tsup": {
|
|
65
65
|
"entry": [
|