@raindrop-ai/opencode-plugin 0.1.0 → 0.1.2
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/dist/index.cjs +274 -42
- package/dist/index.js +274 -49
- package/package.json +2 -2
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-YUIXRQWN.js
|
|
28
28
|
function getCrypto() {
|
|
29
29
|
const c = globalThis.crypto;
|
|
30
30
|
return c;
|
|
@@ -82,6 +82,20 @@ function base64Encode(bytes) {
|
|
|
82
82
|
function generateId() {
|
|
83
83
|
return randomUUID();
|
|
84
84
|
}
|
|
85
|
+
var HANDOFF_ATTRIBUTE_SUFFIXES = {
|
|
86
|
+
mode: "raindrop.handoff.mode",
|
|
87
|
+
childEventId: "raindrop.handoff.childEventId",
|
|
88
|
+
parentEventId: "raindrop.handoff.parentEventId",
|
|
89
|
+
parentSpanId: "raindrop.handoff.parentSpanId",
|
|
90
|
+
name: "raindrop.handoff.name",
|
|
91
|
+
terminal: "raindrop.handoff.terminal",
|
|
92
|
+
agentRole: "raindrop.agent.role"
|
|
93
|
+
};
|
|
94
|
+
var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
|
|
95
|
+
var TRACEPARENT_HEADER = "traceparent";
|
|
96
|
+
var BAGGAGE_HEADER = "baggage";
|
|
97
|
+
var HANDOFF_HEADER = "x-raindrop-handoff";
|
|
98
|
+
var UNRESOLVED_CARRIER_WARNING = `[raindrop] resume: headers were supplied but no hand-off carrier was found on them (searched ${HANDOFF_HEADER}, ${TRACEPARENT_HEADER}, ${BAGGAGE_HEADER}). This run reports as an UNLINKED event, and the launcher that dispatched it will stay on "queued" forever because nothing ever references its child. Passing headers means a parent handed off, so no carrier on them is almost certainly a defect: a gateway or proxy stripping ${HANDOFF_HEADER}, a middleware overwriting ${BAGGAGE_HEADER}, a request forwarded without its headers, or a hand-built carrier whose field names do not match. Send every header the launcher's dispatch returned. Logged once per process; pass no headers for a directly-invoked sub-agent to silence it.`;
|
|
85
99
|
function runWithTracingSuppressed(fn) {
|
|
86
100
|
const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
|
|
87
101
|
if (typeof hook !== "function") return fn();
|
|
@@ -800,6 +814,194 @@ var EventShipper = class {
|
|
|
800
814
|
}
|
|
801
815
|
}
|
|
802
816
|
};
|
|
817
|
+
var MODEL_USAGE_ATTRIBUTES = {
|
|
818
|
+
providerName: "gen_ai.provider.name",
|
|
819
|
+
requestModel: "gen_ai.request.model",
|
|
820
|
+
responseModel: "gen_ai.response.model",
|
|
821
|
+
inputTokens: "gen_ai.usage.input_tokens",
|
|
822
|
+
outputTokens: "gen_ai.usage.output_tokens",
|
|
823
|
+
reasoningTokens: "gen_ai.usage.reasoning_tokens",
|
|
824
|
+
cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
|
|
825
|
+
cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
|
|
826
|
+
nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
|
|
827
|
+
nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
|
|
828
|
+
totalOutputTokens: "raindrop.usage.output_tokens.total",
|
|
829
|
+
reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
|
|
830
|
+
cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
|
|
831
|
+
cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
|
|
832
|
+
};
|
|
833
|
+
var MODEL_PROVIDER_NAMES = [
|
|
834
|
+
["google.vertex", "google-vertex"],
|
|
835
|
+
["vertex.anthropic", "google-vertex"],
|
|
836
|
+
["amazon-bedrock", "amazon-bedrock"],
|
|
837
|
+
["bedrock", "amazon-bedrock"],
|
|
838
|
+
["anthropic", "anthropic"],
|
|
839
|
+
["openai", "openai"],
|
|
840
|
+
["google", "google"],
|
|
841
|
+
["azure", "azure"],
|
|
842
|
+
["openrouter", "openrouter"]
|
|
843
|
+
];
|
|
844
|
+
function canonicalModelProvider(provider) {
|
|
845
|
+
var _a;
|
|
846
|
+
const normalizedProvider = provider.trim().toLowerCase();
|
|
847
|
+
if (normalizedProvider === "gateway") return "vercel";
|
|
848
|
+
const match = MODEL_PROVIDER_NAMES.find(
|
|
849
|
+
([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
|
|
850
|
+
);
|
|
851
|
+
return (_a = match == null ? void 0 : match[1]) != null ? _a : provider;
|
|
852
|
+
}
|
|
853
|
+
function tokenCount(value) {
|
|
854
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
855
|
+
}
|
|
856
|
+
function exclusiveTokens({
|
|
857
|
+
explicit,
|
|
858
|
+
total,
|
|
859
|
+
parts,
|
|
860
|
+
semantics
|
|
861
|
+
}) {
|
|
862
|
+
if (explicit !== void 0) return explicit;
|
|
863
|
+
if (total === void 0) return void 0;
|
|
864
|
+
if (semantics === "exclusive") return total;
|
|
865
|
+
const observedParts = parts.filter((part) => part !== void 0 && part > 0);
|
|
866
|
+
if (semantics === void 0 && observedParts.length > 0) return void 0;
|
|
867
|
+
const exclusive = total - observedParts.reduce((sum, part) => sum + part, 0);
|
|
868
|
+
return exclusive >= 0 ? exclusive : void 0;
|
|
869
|
+
}
|
|
870
|
+
function buildModelUsageAttributes(usage) {
|
|
871
|
+
const inputTokens = tokenCount(usage.inputTokens);
|
|
872
|
+
const outputTokens = tokenCount(usage.outputTokens);
|
|
873
|
+
const explicitNonCachedInputTokens = tokenCount(usage.nonCachedInputTokens);
|
|
874
|
+
const explicitNonReasoningOutputTokens = tokenCount(usage.nonReasoningOutputTokens);
|
|
875
|
+
const reasoningTokens = tokenCount(usage.reasoningTokens);
|
|
876
|
+
const cacheReadInputTokens = tokenCount(usage.cacheReadInputTokens);
|
|
877
|
+
const cacheWriteInputTokens = tokenCount(usage.cacheWriteInputTokens);
|
|
878
|
+
const nonCachedInputTokens = exclusiveTokens({
|
|
879
|
+
explicit: explicitNonCachedInputTokens,
|
|
880
|
+
total: inputTokens,
|
|
881
|
+
parts: [cacheReadInputTokens, cacheWriteInputTokens],
|
|
882
|
+
semantics: usage.inputTokenSemantics
|
|
883
|
+
});
|
|
884
|
+
const nonReasoningOutputTokens = exclusiveTokens({
|
|
885
|
+
explicit: explicitNonReasoningOutputTokens,
|
|
886
|
+
total: outputTokens,
|
|
887
|
+
parts: [reasoningTokens],
|
|
888
|
+
semantics: usage.outputTokenSemantics
|
|
889
|
+
});
|
|
890
|
+
const exclusivePoolAttributes = nonCachedInputTokens !== void 0 && nonReasoningOutputTokens !== void 0 ? [
|
|
891
|
+
// backend consumes this complete raindrop.* set as one atomic billing-pool contract.
|
|
892
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.nonCachedInputTokens, nonCachedInputTokens),
|
|
893
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.nonReasoningOutputTokens, nonReasoningOutputTokens),
|
|
894
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.reasoningOutputTokens, reasoningTokens),
|
|
895
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadTokens, cacheReadInputTokens),
|
|
896
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteTokens, cacheWriteInputTokens)
|
|
897
|
+
] : [];
|
|
898
|
+
return [
|
|
899
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, inputTokens),
|
|
900
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, outputTokens),
|
|
901
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, reasoningTokens),
|
|
902
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, cacheReadInputTokens),
|
|
903
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, cacheWriteInputTokens),
|
|
904
|
+
...exclusivePoolAttributes
|
|
905
|
+
].filter((attribute) => attribute !== void 0);
|
|
906
|
+
}
|
|
907
|
+
var STRING_ALIASES = {
|
|
908
|
+
[MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
|
|
909
|
+
};
|
|
910
|
+
var NUMBER_ALIASES = {
|
|
911
|
+
[MODEL_USAGE_ATTRIBUTES.inputTokens]: [
|
|
912
|
+
"gen_ai.usage.prompt_tokens",
|
|
913
|
+
"ai.usage.prompt_tokens",
|
|
914
|
+
"ai.usage.promptTokens",
|
|
915
|
+
"ai.usage.input_tokens",
|
|
916
|
+
"ai.usage.inputTokens"
|
|
917
|
+
],
|
|
918
|
+
[MODEL_USAGE_ATTRIBUTES.outputTokens]: [
|
|
919
|
+
"gen_ai.usage.completion_tokens",
|
|
920
|
+
"ai.usage.completion_tokens",
|
|
921
|
+
"ai.usage.completionTokens",
|
|
922
|
+
"ai.usage.output_tokens",
|
|
923
|
+
"ai.usage.outputTokens"
|
|
924
|
+
],
|
|
925
|
+
[MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
|
|
926
|
+
"gen_ai.usage.reasoning.output_tokens",
|
|
927
|
+
"ai.usage.reasoningTokens",
|
|
928
|
+
"ai.usage.thoughts_tokens"
|
|
929
|
+
],
|
|
930
|
+
[MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
|
|
931
|
+
"gen_ai.usage.cache_read_tokens",
|
|
932
|
+
"gen_ai.usage.cache_read.input_tokens",
|
|
933
|
+
"ai.usage.cachedInputTokens",
|
|
934
|
+
"ai.usage.cached_tokens",
|
|
935
|
+
"ai.usage.cache_read_tokens",
|
|
936
|
+
"ai.usage.cache_read_input_tokens",
|
|
937
|
+
"ai.usage.cacheReadInputTokens"
|
|
938
|
+
],
|
|
939
|
+
[MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
|
|
940
|
+
"gen_ai.usage.cache_creation_input_tokens",
|
|
941
|
+
"gen_ai.usage.cache_creation.input_tokens",
|
|
942
|
+
"ai.usage.cacheWriteInputTokens",
|
|
943
|
+
"ai.usage.cache_creation_input_tokens",
|
|
944
|
+
"ai.usage.cacheCreationInputTokens"
|
|
945
|
+
]
|
|
946
|
+
};
|
|
947
|
+
function hasAttribute(attributes, key) {
|
|
948
|
+
return attributes.some((attribute) => attribute.key === key);
|
|
949
|
+
}
|
|
950
|
+
function findStringAttribute(attributes, keys) {
|
|
951
|
+
return attributes.find(
|
|
952
|
+
(attribute) => keys.includes(attribute.key) && typeof attribute.value.stringValue === "string" && attribute.value.stringValue.length > 0
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
function findNumberAttribute(attributes, keys) {
|
|
956
|
+
return attributes.find((attribute) => {
|
|
957
|
+
if (!keys.includes(attribute.key)) return false;
|
|
958
|
+
if (typeof attribute.value.doubleValue === "number" && Number.isSafeInteger(attribute.value.doubleValue) && attribute.value.doubleValue >= 0) {
|
|
959
|
+
return true;
|
|
960
|
+
}
|
|
961
|
+
const intValue = attribute.value.intValue;
|
|
962
|
+
return integerValue(intValue) !== void 0;
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
function integerValue(value) {
|
|
966
|
+
if (typeof value === "number") {
|
|
967
|
+
return Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
968
|
+
}
|
|
969
|
+
if (typeof value !== "string") return void 0;
|
|
970
|
+
const parsed = Number(value);
|
|
971
|
+
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
|
|
972
|
+
}
|
|
973
|
+
function appendAlias(attributes, target, source) {
|
|
974
|
+
if (!source || hasAttribute(attributes, target)) return attributes;
|
|
975
|
+
return [...attributes, { key: target, value: { ...source.value } }];
|
|
976
|
+
}
|
|
977
|
+
function appendProviderAlias(attributes) {
|
|
978
|
+
if (hasAttribute(attributes, MODEL_USAGE_ATTRIBUTES.providerName)) return attributes;
|
|
979
|
+
const source = findStringAttribute(attributes, ["gen_ai.system", "ai.model.provider"]);
|
|
980
|
+
if (!(source == null ? void 0 : source.value.stringValue)) return attributes;
|
|
981
|
+
const attribute = attrString(
|
|
982
|
+
MODEL_USAGE_ATTRIBUTES.providerName,
|
|
983
|
+
canonicalModelProvider(source.value.stringValue)
|
|
984
|
+
);
|
|
985
|
+
return attribute ? [...attributes, attribute] : attributes;
|
|
986
|
+
}
|
|
987
|
+
function normalizeModelUsageSpan(span) {
|
|
988
|
+
const original = span.attributes;
|
|
989
|
+
if (!original || original.length === 0) return span;
|
|
990
|
+
const responseModel = findStringAttribute(original, [
|
|
991
|
+
MODEL_USAGE_ATTRIBUTES.responseModel,
|
|
992
|
+
"ai.response.model"
|
|
993
|
+
]);
|
|
994
|
+
if (!responseModel) return span;
|
|
995
|
+
let attributes = appendAlias(original, MODEL_USAGE_ATTRIBUTES.responseModel, responseModel);
|
|
996
|
+
attributes = appendProviderAlias(attributes);
|
|
997
|
+
for (const [target, aliases] of Object.entries(STRING_ALIASES)) {
|
|
998
|
+
attributes = appendAlias(attributes, target, findStringAttribute(attributes, aliases));
|
|
999
|
+
}
|
|
1000
|
+
for (const [target, aliases] of Object.entries(NUMBER_ALIASES)) {
|
|
1001
|
+
attributes = appendAlias(attributes, target, findNumberAttribute(attributes, aliases));
|
|
1002
|
+
}
|
|
1003
|
+
return attributes === original ? span : { ...span, attributes };
|
|
1004
|
+
}
|
|
803
1005
|
var DEFAULT_SECRET_KEY_NAMES = [
|
|
804
1006
|
"apikey",
|
|
805
1007
|
"apisecret",
|
|
@@ -1000,6 +1202,10 @@ var TraceShipper = class {
|
|
|
1000
1202
|
return null;
|
|
1001
1203
|
}
|
|
1002
1204
|
}
|
|
1205
|
+
try {
|
|
1206
|
+
current = normalizeModelUsageSpan(current);
|
|
1207
|
+
} catch (e) {
|
|
1208
|
+
}
|
|
1003
1209
|
if (!this.disableDefaultRedaction) {
|
|
1004
1210
|
current = defaultTransformSpan(current);
|
|
1005
1211
|
}
|
|
@@ -1296,7 +1502,7 @@ function resolveLocalWorkshopUrl(fileValue) {
|
|
|
1296
1502
|
// package.json
|
|
1297
1503
|
var package_default = {
|
|
1298
1504
|
name: "@raindrop-ai/opencode-plugin",
|
|
1299
|
-
version: "0.1.
|
|
1505
|
+
version: "0.1.2",
|
|
1300
1506
|
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
1301
1507
|
type: "module",
|
|
1302
1508
|
main: "dist/index.js",
|
|
@@ -1401,6 +1607,9 @@ var TraceShipper2 = class extends TraceShipper {
|
|
|
1401
1607
|
}
|
|
1402
1608
|
};
|
|
1403
1609
|
|
|
1610
|
+
// src/tracing.ts
|
|
1611
|
+
var import_node_os2 = require("os");
|
|
1612
|
+
|
|
1404
1613
|
// src/bounded.ts
|
|
1405
1614
|
var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
|
|
1406
1615
|
var MAX_TEXT_FIELD_CHARS = 1e6;
|
|
@@ -1481,6 +1690,49 @@ function boundedStringify(value, limit = MAX_TEXT_FIELD_CHARS) {
|
|
|
1481
1690
|
}
|
|
1482
1691
|
}
|
|
1483
1692
|
|
|
1693
|
+
// src/model-spend.ts
|
|
1694
|
+
function parseOpenCodeAssistantTokens(tokens) {
|
|
1695
|
+
const input = tokens["input"];
|
|
1696
|
+
const output = tokens["output"];
|
|
1697
|
+
if (typeof input !== "number" || typeof output !== "number") return void 0;
|
|
1698
|
+
const reasoning = tokens["reasoning"];
|
|
1699
|
+
const cache = tokens["cache"];
|
|
1700
|
+
const cacheObj = cache && typeof cache === "object" && cache !== null ? cache : void 0;
|
|
1701
|
+
return {
|
|
1702
|
+
input,
|
|
1703
|
+
output,
|
|
1704
|
+
reasoning: typeof reasoning === "number" ? reasoning : 0,
|
|
1705
|
+
cacheRead: typeof (cacheObj == null ? void 0 : cacheObj["read"]) === "number" ? cacheObj["read"] : 0,
|
|
1706
|
+
cacheWrite: typeof (cacheObj == null ? void 0 : cacheObj["write"]) === "number" ? cacheObj["write"] : 0
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
function modelSpendSpanAttributes(input) {
|
|
1710
|
+
const providerAttr = attrString(
|
|
1711
|
+
"gen_ai.provider.name",
|
|
1712
|
+
canonicalModelProvider(input.providerID)
|
|
1713
|
+
);
|
|
1714
|
+
const usageAttrs = buildModelUsageAttributes({
|
|
1715
|
+
inputTokens: input.tokens.input,
|
|
1716
|
+
outputTokens: input.tokens.output,
|
|
1717
|
+
reasoningTokens: input.tokens.reasoning,
|
|
1718
|
+
cacheReadInputTokens: input.tokens.cacheRead,
|
|
1719
|
+
cacheWriteInputTokens: input.tokens.cacheWrite,
|
|
1720
|
+
inputTokenSemantics: "exclusive",
|
|
1721
|
+
outputTokenSemantics: "exclusive"
|
|
1722
|
+
});
|
|
1723
|
+
return providerAttr ? [providerAttr, ...usageAttrs] : usageAttrs;
|
|
1724
|
+
}
|
|
1725
|
+
function buildLlmGenAiAttributes(input) {
|
|
1726
|
+
const parsed = parseOpenCodeAssistantTokens(input.tokens);
|
|
1727
|
+
if (!parsed) return [];
|
|
1728
|
+
return [
|
|
1729
|
+
attrString("gen_ai.system", input.providerID),
|
|
1730
|
+
attrString("gen_ai.request.model", input.modelID),
|
|
1731
|
+
attrString("gen_ai.response.model", input.modelID),
|
|
1732
|
+
...modelSpendSpanAttributes({ providerID: input.providerID, tokens: parsed })
|
|
1733
|
+
].filter((attribute) => attribute !== void 0);
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1484
1736
|
// src/tracing-linkage-helpers.ts
|
|
1485
1737
|
function createSessionParentMapHelpers({
|
|
1486
1738
|
sessions: sessions2,
|
|
@@ -1576,6 +1828,8 @@ function createSessionState(sessionId) {
|
|
|
1576
1828
|
return {
|
|
1577
1829
|
sessionId,
|
|
1578
1830
|
currentInput: "",
|
|
1831
|
+
turnOutputParts: [],
|
|
1832
|
+
turnOutputTokens: 0,
|
|
1579
1833
|
outputParts: /* @__PURE__ */ new Map(),
|
|
1580
1834
|
reasoningParts: /* @__PURE__ */ new Map(),
|
|
1581
1835
|
toolSpanStarts: /* @__PURE__ */ new Map(),
|
|
@@ -1643,7 +1897,7 @@ function getHostname() {
|
|
|
1643
1897
|
try {
|
|
1644
1898
|
const bunGlobal = globalThis;
|
|
1645
1899
|
if ((_a = bunGlobal.Bun) == null ? void 0 : _a.hostname) return bunGlobal.Bun.hostname;
|
|
1646
|
-
return
|
|
1900
|
+
return (0, import_node_os2.hostname)();
|
|
1647
1901
|
} catch (e) {
|
|
1648
1902
|
const h = process.env["HOSTNAME"];
|
|
1649
1903
|
if (h === void 0 || h === "") throw new Error("HOSTNAME env is not set");
|
|
@@ -1792,7 +2046,6 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1792
2046
|
const tokensObj = tokens;
|
|
1793
2047
|
const inputTokens = tokensObj["input"];
|
|
1794
2048
|
const outputTokens = tokensObj["output"];
|
|
1795
|
-
const reasoningTokens = tokensObj["reasoning"];
|
|
1796
2049
|
if (typeof inputTokens !== "number" || typeof outputTokens !== "number")
|
|
1797
2050
|
throw new Error("message.updated: tokens.input and tokens.output must be numbers");
|
|
1798
2051
|
const providerID = info2["providerID"];
|
|
@@ -1806,13 +2059,11 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1806
2059
|
const outputText = state.outputParts.get(messageId);
|
|
1807
2060
|
const reasoningText = state.reasoningParts.get(messageId);
|
|
1808
2061
|
const isToolCallsCompletion = finishReason === "tool-calls";
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
return;
|
|
1812
|
-
if (isToolCallsCompletion && !hasIntermediateReasoning) {
|
|
1813
|
-
await traceShipper.flush();
|
|
1814
|
-
return;
|
|
2062
|
+
if (outputText !== void 0 && outputText.trim().length > 0) {
|
|
2063
|
+
state.turnOutputParts.push(outputText);
|
|
1815
2064
|
}
|
|
2065
|
+
state.turnOutputTokens += outputTokens;
|
|
2066
|
+
if (outputText === void 0 && !isToolCallsCompletion) return;
|
|
1816
2067
|
const msgError = info2["error"];
|
|
1817
2068
|
const errorForSpan = msgError ? (() => {
|
|
1818
2069
|
var _a2, _b2;
|
|
@@ -1825,20 +2076,12 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1825
2076
|
type: ${name != null ? name : "UnknownError"}`;
|
|
1826
2077
|
})() : void 0;
|
|
1827
2078
|
if (isToolCallsCompletion) {
|
|
1828
|
-
|
|
1829
|
-
throw new Error("message.updated: reasoningParts missing for tool-call continuation");
|
|
2079
|
+
const responseText = reasoningText !== void 0 && reasoningText.trim().length > 0 ? reasoningText.trim() : outputText !== void 0 && outputText.trim().length > 0 ? outputText.trim() : "";
|
|
1830
2080
|
const llmAttrs = [
|
|
1831
2081
|
attrString("ai.operationId", "generateText"),
|
|
1832
|
-
attrString("ai.response.text",
|
|
1833
|
-
|
|
1834
|
-
attrString("gen_ai.request.model", modelID),
|
|
1835
|
-
attrString("gen_ai.response.model", modelID),
|
|
1836
|
-
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1837
|
-
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
2082
|
+
attrString("ai.response.text", responseText),
|
|
2083
|
+
...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
|
|
1838
2084
|
];
|
|
1839
|
-
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1840
|
-
llmAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1841
|
-
}
|
|
1842
2085
|
if (state.currentSystemPrompt) {
|
|
1843
2086
|
llmAttrs.push(
|
|
1844
2087
|
attrString("gen_ai.prompt.0.role", "system"),
|
|
@@ -1860,6 +2103,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1860
2103
|
return;
|
|
1861
2104
|
}
|
|
1862
2105
|
if (outputText === void 0) return;
|
|
2106
|
+
const turnOutputText = capText2(state.turnOutputParts.join("\n\n"));
|
|
1863
2107
|
const rootSpan = state.currentRootSpan;
|
|
1864
2108
|
const isCompaction = state.isCompacting === true;
|
|
1865
2109
|
if (state.parentId) {
|
|
@@ -1871,16 +2115,9 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1871
2115
|
"ai.prompt.messages",
|
|
1872
2116
|
buildPromptMessages(state.currentSystemPrompt, state.currentInput)
|
|
1873
2117
|
),
|
|
1874
|
-
attrString("ai.response.text",
|
|
1875
|
-
|
|
1876
|
-
attrString("gen_ai.request.model", modelID),
|
|
1877
|
-
attrString("gen_ai.response.model", modelID),
|
|
1878
|
-
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1879
|
-
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
2118
|
+
attrString("ai.response.text", turnOutputText),
|
|
2119
|
+
...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
|
|
1880
2120
|
];
|
|
1881
|
-
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1882
|
-
llmAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1883
|
-
}
|
|
1884
2121
|
if (state.currentSystemPrompt) {
|
|
1885
2122
|
llmAttrs.push(
|
|
1886
2123
|
attrString("gen_ai.prompt.0.role", "system"),
|
|
@@ -1923,20 +2160,13 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1923
2160
|
"ai.prompt.messages",
|
|
1924
2161
|
buildPromptMessages(state.currentSystemPrompt, state.currentInput)
|
|
1925
2162
|
),
|
|
1926
|
-
attrString("ai.response.text",
|
|
1927
|
-
|
|
1928
|
-
attrString("gen_ai.request.model", modelID),
|
|
1929
|
-
attrString("gen_ai.response.model", modelID),
|
|
1930
|
-
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1931
|
-
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
2163
|
+
attrString("ai.response.text", turnOutputText),
|
|
2164
|
+
...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
|
|
1932
2165
|
];
|
|
1933
2166
|
if (isCompaction) {
|
|
1934
2167
|
rootAttrs.push(attrString("is_compaction", "true"));
|
|
1935
2168
|
}
|
|
1936
2169
|
rootAttrs.push(attrString("message_id", messageId));
|
|
1937
|
-
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1938
|
-
rootAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1939
|
-
}
|
|
1940
2170
|
if (state.currentSystemPrompt) {
|
|
1941
2171
|
rootAttrs.push(
|
|
1942
2172
|
attrString("gen_ai.prompt.0.role", "system"),
|
|
@@ -1952,7 +2182,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1952
2182
|
}
|
|
1953
2183
|
}
|
|
1954
2184
|
state.currentRootSpan = void 0;
|
|
1955
|
-
const hasAssistantResponse =
|
|
2185
|
+
const hasAssistantResponse = turnOutputText.trim().length > 0;
|
|
1956
2186
|
if (!hasAssistantResponse && !msgError) {
|
|
1957
2187
|
await traceShipper.flush();
|
|
1958
2188
|
state.currentEventId = void 0;
|
|
@@ -1962,10 +2192,10 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1962
2192
|
await eventShipper.finish(state.currentEventId, {
|
|
1963
2193
|
userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
|
|
1964
2194
|
model: modelID,
|
|
1965
|
-
output: msgError ? void 0 :
|
|
2195
|
+
output: msgError ? void 0 : turnOutputText,
|
|
1966
2196
|
usage: {
|
|
1967
2197
|
promptTokens: inputTokens,
|
|
1968
|
-
completionTokens:
|
|
2198
|
+
completionTokens: state.turnOutputTokens
|
|
1969
2199
|
},
|
|
1970
2200
|
error: msgError ? {
|
|
1971
2201
|
name: msgError.name,
|
|
@@ -2099,6 +2329,8 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
2099
2329
|
};
|
|
2100
2330
|
const userText = capText2(textParts.join("\n"));
|
|
2101
2331
|
state.currentInput = userText;
|
|
2332
|
+
state.turnOutputParts = [];
|
|
2333
|
+
state.turnOutputTokens = 0;
|
|
2102
2334
|
if (state.parentId) {
|
|
2103
2335
|
if (!state.parentTaskSpanIds || !state.parentEventContext) {
|
|
2104
2336
|
return;
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
// ../core/dist/chunk-QTUH3BHZ.js
|
|
1
|
+
// ../core/dist/chunk-YUIXRQWN.js
|
|
9
2
|
function getCrypto() {
|
|
10
3
|
const c = globalThis.crypto;
|
|
11
4
|
return c;
|
|
@@ -63,6 +56,20 @@ function base64Encode(bytes) {
|
|
|
63
56
|
function generateId() {
|
|
64
57
|
return randomUUID();
|
|
65
58
|
}
|
|
59
|
+
var HANDOFF_ATTRIBUTE_SUFFIXES = {
|
|
60
|
+
mode: "raindrop.handoff.mode",
|
|
61
|
+
childEventId: "raindrop.handoff.childEventId",
|
|
62
|
+
parentEventId: "raindrop.handoff.parentEventId",
|
|
63
|
+
parentSpanId: "raindrop.handoff.parentSpanId",
|
|
64
|
+
name: "raindrop.handoff.name",
|
|
65
|
+
terminal: "raindrop.handoff.terminal",
|
|
66
|
+
agentRole: "raindrop.agent.role"
|
|
67
|
+
};
|
|
68
|
+
var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
|
|
69
|
+
var TRACEPARENT_HEADER = "traceparent";
|
|
70
|
+
var BAGGAGE_HEADER = "baggage";
|
|
71
|
+
var HANDOFF_HEADER = "x-raindrop-handoff";
|
|
72
|
+
var UNRESOLVED_CARRIER_WARNING = `[raindrop] resume: headers were supplied but no hand-off carrier was found on them (searched ${HANDOFF_HEADER}, ${TRACEPARENT_HEADER}, ${BAGGAGE_HEADER}). This run reports as an UNLINKED event, and the launcher that dispatched it will stay on "queued" forever because nothing ever references its child. Passing headers means a parent handed off, so no carrier on them is almost certainly a defect: a gateway or proxy stripping ${HANDOFF_HEADER}, a middleware overwriting ${BAGGAGE_HEADER}, a request forwarded without its headers, or a hand-built carrier whose field names do not match. Send every header the launcher's dispatch returned. Logged once per process; pass no headers for a directly-invoked sub-agent to silence it.`;
|
|
66
73
|
function runWithTracingSuppressed(fn) {
|
|
67
74
|
const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
|
|
68
75
|
if (typeof hook !== "function") return fn();
|
|
@@ -781,6 +788,194 @@ var EventShipper = class {
|
|
|
781
788
|
}
|
|
782
789
|
}
|
|
783
790
|
};
|
|
791
|
+
var MODEL_USAGE_ATTRIBUTES = {
|
|
792
|
+
providerName: "gen_ai.provider.name",
|
|
793
|
+
requestModel: "gen_ai.request.model",
|
|
794
|
+
responseModel: "gen_ai.response.model",
|
|
795
|
+
inputTokens: "gen_ai.usage.input_tokens",
|
|
796
|
+
outputTokens: "gen_ai.usage.output_tokens",
|
|
797
|
+
reasoningTokens: "gen_ai.usage.reasoning_tokens",
|
|
798
|
+
cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
|
|
799
|
+
cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
|
|
800
|
+
nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
|
|
801
|
+
nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
|
|
802
|
+
totalOutputTokens: "raindrop.usage.output_tokens.total",
|
|
803
|
+
reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
|
|
804
|
+
cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
|
|
805
|
+
cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
|
|
806
|
+
};
|
|
807
|
+
var MODEL_PROVIDER_NAMES = [
|
|
808
|
+
["google.vertex", "google-vertex"],
|
|
809
|
+
["vertex.anthropic", "google-vertex"],
|
|
810
|
+
["amazon-bedrock", "amazon-bedrock"],
|
|
811
|
+
["bedrock", "amazon-bedrock"],
|
|
812
|
+
["anthropic", "anthropic"],
|
|
813
|
+
["openai", "openai"],
|
|
814
|
+
["google", "google"],
|
|
815
|
+
["azure", "azure"],
|
|
816
|
+
["openrouter", "openrouter"]
|
|
817
|
+
];
|
|
818
|
+
function canonicalModelProvider(provider) {
|
|
819
|
+
var _a;
|
|
820
|
+
const normalizedProvider = provider.trim().toLowerCase();
|
|
821
|
+
if (normalizedProvider === "gateway") return "vercel";
|
|
822
|
+
const match = MODEL_PROVIDER_NAMES.find(
|
|
823
|
+
([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
|
|
824
|
+
);
|
|
825
|
+
return (_a = match == null ? void 0 : match[1]) != null ? _a : provider;
|
|
826
|
+
}
|
|
827
|
+
function tokenCount(value) {
|
|
828
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
829
|
+
}
|
|
830
|
+
function exclusiveTokens({
|
|
831
|
+
explicit,
|
|
832
|
+
total,
|
|
833
|
+
parts,
|
|
834
|
+
semantics
|
|
835
|
+
}) {
|
|
836
|
+
if (explicit !== void 0) return explicit;
|
|
837
|
+
if (total === void 0) return void 0;
|
|
838
|
+
if (semantics === "exclusive") return total;
|
|
839
|
+
const observedParts = parts.filter((part) => part !== void 0 && part > 0);
|
|
840
|
+
if (semantics === void 0 && observedParts.length > 0) return void 0;
|
|
841
|
+
const exclusive = total - observedParts.reduce((sum, part) => sum + part, 0);
|
|
842
|
+
return exclusive >= 0 ? exclusive : void 0;
|
|
843
|
+
}
|
|
844
|
+
function buildModelUsageAttributes(usage) {
|
|
845
|
+
const inputTokens = tokenCount(usage.inputTokens);
|
|
846
|
+
const outputTokens = tokenCount(usage.outputTokens);
|
|
847
|
+
const explicitNonCachedInputTokens = tokenCount(usage.nonCachedInputTokens);
|
|
848
|
+
const explicitNonReasoningOutputTokens = tokenCount(usage.nonReasoningOutputTokens);
|
|
849
|
+
const reasoningTokens = tokenCount(usage.reasoningTokens);
|
|
850
|
+
const cacheReadInputTokens = tokenCount(usage.cacheReadInputTokens);
|
|
851
|
+
const cacheWriteInputTokens = tokenCount(usage.cacheWriteInputTokens);
|
|
852
|
+
const nonCachedInputTokens = exclusiveTokens({
|
|
853
|
+
explicit: explicitNonCachedInputTokens,
|
|
854
|
+
total: inputTokens,
|
|
855
|
+
parts: [cacheReadInputTokens, cacheWriteInputTokens],
|
|
856
|
+
semantics: usage.inputTokenSemantics
|
|
857
|
+
});
|
|
858
|
+
const nonReasoningOutputTokens = exclusiveTokens({
|
|
859
|
+
explicit: explicitNonReasoningOutputTokens,
|
|
860
|
+
total: outputTokens,
|
|
861
|
+
parts: [reasoningTokens],
|
|
862
|
+
semantics: usage.outputTokenSemantics
|
|
863
|
+
});
|
|
864
|
+
const exclusivePoolAttributes = nonCachedInputTokens !== void 0 && nonReasoningOutputTokens !== void 0 ? [
|
|
865
|
+
// backend consumes this complete raindrop.* set as one atomic billing-pool contract.
|
|
866
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.nonCachedInputTokens, nonCachedInputTokens),
|
|
867
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.nonReasoningOutputTokens, nonReasoningOutputTokens),
|
|
868
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.reasoningOutputTokens, reasoningTokens),
|
|
869
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadTokens, cacheReadInputTokens),
|
|
870
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteTokens, cacheWriteInputTokens)
|
|
871
|
+
] : [];
|
|
872
|
+
return [
|
|
873
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, inputTokens),
|
|
874
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, outputTokens),
|
|
875
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, reasoningTokens),
|
|
876
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, cacheReadInputTokens),
|
|
877
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, cacheWriteInputTokens),
|
|
878
|
+
...exclusivePoolAttributes
|
|
879
|
+
].filter((attribute) => attribute !== void 0);
|
|
880
|
+
}
|
|
881
|
+
var STRING_ALIASES = {
|
|
882
|
+
[MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
|
|
883
|
+
};
|
|
884
|
+
var NUMBER_ALIASES = {
|
|
885
|
+
[MODEL_USAGE_ATTRIBUTES.inputTokens]: [
|
|
886
|
+
"gen_ai.usage.prompt_tokens",
|
|
887
|
+
"ai.usage.prompt_tokens",
|
|
888
|
+
"ai.usage.promptTokens",
|
|
889
|
+
"ai.usage.input_tokens",
|
|
890
|
+
"ai.usage.inputTokens"
|
|
891
|
+
],
|
|
892
|
+
[MODEL_USAGE_ATTRIBUTES.outputTokens]: [
|
|
893
|
+
"gen_ai.usage.completion_tokens",
|
|
894
|
+
"ai.usage.completion_tokens",
|
|
895
|
+
"ai.usage.completionTokens",
|
|
896
|
+
"ai.usage.output_tokens",
|
|
897
|
+
"ai.usage.outputTokens"
|
|
898
|
+
],
|
|
899
|
+
[MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
|
|
900
|
+
"gen_ai.usage.reasoning.output_tokens",
|
|
901
|
+
"ai.usage.reasoningTokens",
|
|
902
|
+
"ai.usage.thoughts_tokens"
|
|
903
|
+
],
|
|
904
|
+
[MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
|
|
905
|
+
"gen_ai.usage.cache_read_tokens",
|
|
906
|
+
"gen_ai.usage.cache_read.input_tokens",
|
|
907
|
+
"ai.usage.cachedInputTokens",
|
|
908
|
+
"ai.usage.cached_tokens",
|
|
909
|
+
"ai.usage.cache_read_tokens",
|
|
910
|
+
"ai.usage.cache_read_input_tokens",
|
|
911
|
+
"ai.usage.cacheReadInputTokens"
|
|
912
|
+
],
|
|
913
|
+
[MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
|
|
914
|
+
"gen_ai.usage.cache_creation_input_tokens",
|
|
915
|
+
"gen_ai.usage.cache_creation.input_tokens",
|
|
916
|
+
"ai.usage.cacheWriteInputTokens",
|
|
917
|
+
"ai.usage.cache_creation_input_tokens",
|
|
918
|
+
"ai.usage.cacheCreationInputTokens"
|
|
919
|
+
]
|
|
920
|
+
};
|
|
921
|
+
function hasAttribute(attributes, key) {
|
|
922
|
+
return attributes.some((attribute) => attribute.key === key);
|
|
923
|
+
}
|
|
924
|
+
function findStringAttribute(attributes, keys) {
|
|
925
|
+
return attributes.find(
|
|
926
|
+
(attribute) => keys.includes(attribute.key) && typeof attribute.value.stringValue === "string" && attribute.value.stringValue.length > 0
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
function findNumberAttribute(attributes, keys) {
|
|
930
|
+
return attributes.find((attribute) => {
|
|
931
|
+
if (!keys.includes(attribute.key)) return false;
|
|
932
|
+
if (typeof attribute.value.doubleValue === "number" && Number.isSafeInteger(attribute.value.doubleValue) && attribute.value.doubleValue >= 0) {
|
|
933
|
+
return true;
|
|
934
|
+
}
|
|
935
|
+
const intValue = attribute.value.intValue;
|
|
936
|
+
return integerValue(intValue) !== void 0;
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
function integerValue(value) {
|
|
940
|
+
if (typeof value === "number") {
|
|
941
|
+
return Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
942
|
+
}
|
|
943
|
+
if (typeof value !== "string") return void 0;
|
|
944
|
+
const parsed = Number(value);
|
|
945
|
+
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
|
|
946
|
+
}
|
|
947
|
+
function appendAlias(attributes, target, source) {
|
|
948
|
+
if (!source || hasAttribute(attributes, target)) return attributes;
|
|
949
|
+
return [...attributes, { key: target, value: { ...source.value } }];
|
|
950
|
+
}
|
|
951
|
+
function appendProviderAlias(attributes) {
|
|
952
|
+
if (hasAttribute(attributes, MODEL_USAGE_ATTRIBUTES.providerName)) return attributes;
|
|
953
|
+
const source = findStringAttribute(attributes, ["gen_ai.system", "ai.model.provider"]);
|
|
954
|
+
if (!(source == null ? void 0 : source.value.stringValue)) return attributes;
|
|
955
|
+
const attribute = attrString(
|
|
956
|
+
MODEL_USAGE_ATTRIBUTES.providerName,
|
|
957
|
+
canonicalModelProvider(source.value.stringValue)
|
|
958
|
+
);
|
|
959
|
+
return attribute ? [...attributes, attribute] : attributes;
|
|
960
|
+
}
|
|
961
|
+
function normalizeModelUsageSpan(span) {
|
|
962
|
+
const original = span.attributes;
|
|
963
|
+
if (!original || original.length === 0) return span;
|
|
964
|
+
const responseModel = findStringAttribute(original, [
|
|
965
|
+
MODEL_USAGE_ATTRIBUTES.responseModel,
|
|
966
|
+
"ai.response.model"
|
|
967
|
+
]);
|
|
968
|
+
if (!responseModel) return span;
|
|
969
|
+
let attributes = appendAlias(original, MODEL_USAGE_ATTRIBUTES.responseModel, responseModel);
|
|
970
|
+
attributes = appendProviderAlias(attributes);
|
|
971
|
+
for (const [target, aliases] of Object.entries(STRING_ALIASES)) {
|
|
972
|
+
attributes = appendAlias(attributes, target, findStringAttribute(attributes, aliases));
|
|
973
|
+
}
|
|
974
|
+
for (const [target, aliases] of Object.entries(NUMBER_ALIASES)) {
|
|
975
|
+
attributes = appendAlias(attributes, target, findNumberAttribute(attributes, aliases));
|
|
976
|
+
}
|
|
977
|
+
return attributes === original ? span : { ...span, attributes };
|
|
978
|
+
}
|
|
784
979
|
var DEFAULT_SECRET_KEY_NAMES = [
|
|
785
980
|
"apikey",
|
|
786
981
|
"apisecret",
|
|
@@ -981,6 +1176,10 @@ var TraceShipper = class {
|
|
|
981
1176
|
return null;
|
|
982
1177
|
}
|
|
983
1178
|
}
|
|
1179
|
+
try {
|
|
1180
|
+
current = normalizeModelUsageSpan(current);
|
|
1181
|
+
} catch (e) {
|
|
1182
|
+
}
|
|
984
1183
|
if (!this.disableDefaultRedaction) {
|
|
985
1184
|
current = defaultTransformSpan(current);
|
|
986
1185
|
}
|
|
@@ -1277,7 +1476,7 @@ function resolveLocalWorkshopUrl(fileValue) {
|
|
|
1277
1476
|
// package.json
|
|
1278
1477
|
var package_default = {
|
|
1279
1478
|
name: "@raindrop-ai/opencode-plugin",
|
|
1280
|
-
version: "0.1.
|
|
1479
|
+
version: "0.1.2",
|
|
1281
1480
|
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
1282
1481
|
type: "module",
|
|
1283
1482
|
main: "dist/index.js",
|
|
@@ -1382,6 +1581,9 @@ var TraceShipper2 = class extends TraceShipper {
|
|
|
1382
1581
|
}
|
|
1383
1582
|
};
|
|
1384
1583
|
|
|
1584
|
+
// src/tracing.ts
|
|
1585
|
+
import { hostname as osHostname } from "os";
|
|
1586
|
+
|
|
1385
1587
|
// src/bounded.ts
|
|
1386
1588
|
var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
|
|
1387
1589
|
var MAX_TEXT_FIELD_CHARS = 1e6;
|
|
@@ -1462,6 +1664,49 @@ function boundedStringify(value, limit = MAX_TEXT_FIELD_CHARS) {
|
|
|
1462
1664
|
}
|
|
1463
1665
|
}
|
|
1464
1666
|
|
|
1667
|
+
// src/model-spend.ts
|
|
1668
|
+
function parseOpenCodeAssistantTokens(tokens) {
|
|
1669
|
+
const input = tokens["input"];
|
|
1670
|
+
const output = tokens["output"];
|
|
1671
|
+
if (typeof input !== "number" || typeof output !== "number") return void 0;
|
|
1672
|
+
const reasoning = tokens["reasoning"];
|
|
1673
|
+
const cache = tokens["cache"];
|
|
1674
|
+
const cacheObj = cache && typeof cache === "object" && cache !== null ? cache : void 0;
|
|
1675
|
+
return {
|
|
1676
|
+
input,
|
|
1677
|
+
output,
|
|
1678
|
+
reasoning: typeof reasoning === "number" ? reasoning : 0,
|
|
1679
|
+
cacheRead: typeof (cacheObj == null ? void 0 : cacheObj["read"]) === "number" ? cacheObj["read"] : 0,
|
|
1680
|
+
cacheWrite: typeof (cacheObj == null ? void 0 : cacheObj["write"]) === "number" ? cacheObj["write"] : 0
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
function modelSpendSpanAttributes(input) {
|
|
1684
|
+
const providerAttr = attrString(
|
|
1685
|
+
"gen_ai.provider.name",
|
|
1686
|
+
canonicalModelProvider(input.providerID)
|
|
1687
|
+
);
|
|
1688
|
+
const usageAttrs = buildModelUsageAttributes({
|
|
1689
|
+
inputTokens: input.tokens.input,
|
|
1690
|
+
outputTokens: input.tokens.output,
|
|
1691
|
+
reasoningTokens: input.tokens.reasoning,
|
|
1692
|
+
cacheReadInputTokens: input.tokens.cacheRead,
|
|
1693
|
+
cacheWriteInputTokens: input.tokens.cacheWrite,
|
|
1694
|
+
inputTokenSemantics: "exclusive",
|
|
1695
|
+
outputTokenSemantics: "exclusive"
|
|
1696
|
+
});
|
|
1697
|
+
return providerAttr ? [providerAttr, ...usageAttrs] : usageAttrs;
|
|
1698
|
+
}
|
|
1699
|
+
function buildLlmGenAiAttributes(input) {
|
|
1700
|
+
const parsed = parseOpenCodeAssistantTokens(input.tokens);
|
|
1701
|
+
if (!parsed) return [];
|
|
1702
|
+
return [
|
|
1703
|
+
attrString("gen_ai.system", input.providerID),
|
|
1704
|
+
attrString("gen_ai.request.model", input.modelID),
|
|
1705
|
+
attrString("gen_ai.response.model", input.modelID),
|
|
1706
|
+
...modelSpendSpanAttributes({ providerID: input.providerID, tokens: parsed })
|
|
1707
|
+
].filter((attribute) => attribute !== void 0);
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1465
1710
|
// src/tracing-linkage-helpers.ts
|
|
1466
1711
|
function createSessionParentMapHelpers({
|
|
1467
1712
|
sessions: sessions2,
|
|
@@ -1557,6 +1802,8 @@ function createSessionState(sessionId) {
|
|
|
1557
1802
|
return {
|
|
1558
1803
|
sessionId,
|
|
1559
1804
|
currentInput: "",
|
|
1805
|
+
turnOutputParts: [],
|
|
1806
|
+
turnOutputTokens: 0,
|
|
1560
1807
|
outputParts: /* @__PURE__ */ new Map(),
|
|
1561
1808
|
reasoningParts: /* @__PURE__ */ new Map(),
|
|
1562
1809
|
toolSpanStarts: /* @__PURE__ */ new Map(),
|
|
@@ -1624,7 +1871,7 @@ function getHostname() {
|
|
|
1624
1871
|
try {
|
|
1625
1872
|
const bunGlobal = globalThis;
|
|
1626
1873
|
if ((_a = bunGlobal.Bun) == null ? void 0 : _a.hostname) return bunGlobal.Bun.hostname;
|
|
1627
|
-
return
|
|
1874
|
+
return osHostname();
|
|
1628
1875
|
} catch (e) {
|
|
1629
1876
|
const h = process.env["HOSTNAME"];
|
|
1630
1877
|
if (h === void 0 || h === "") throw new Error("HOSTNAME env is not set");
|
|
@@ -1773,7 +2020,6 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1773
2020
|
const tokensObj = tokens;
|
|
1774
2021
|
const inputTokens = tokensObj["input"];
|
|
1775
2022
|
const outputTokens = tokensObj["output"];
|
|
1776
|
-
const reasoningTokens = tokensObj["reasoning"];
|
|
1777
2023
|
if (typeof inputTokens !== "number" || typeof outputTokens !== "number")
|
|
1778
2024
|
throw new Error("message.updated: tokens.input and tokens.output must be numbers");
|
|
1779
2025
|
const providerID = info2["providerID"];
|
|
@@ -1787,13 +2033,11 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1787
2033
|
const outputText = state.outputParts.get(messageId);
|
|
1788
2034
|
const reasoningText = state.reasoningParts.get(messageId);
|
|
1789
2035
|
const isToolCallsCompletion = finishReason === "tool-calls";
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
return;
|
|
1793
|
-
if (isToolCallsCompletion && !hasIntermediateReasoning) {
|
|
1794
|
-
await traceShipper.flush();
|
|
1795
|
-
return;
|
|
2036
|
+
if (outputText !== void 0 && outputText.trim().length > 0) {
|
|
2037
|
+
state.turnOutputParts.push(outputText);
|
|
1796
2038
|
}
|
|
2039
|
+
state.turnOutputTokens += outputTokens;
|
|
2040
|
+
if (outputText === void 0 && !isToolCallsCompletion) return;
|
|
1797
2041
|
const msgError = info2["error"];
|
|
1798
2042
|
const errorForSpan = msgError ? (() => {
|
|
1799
2043
|
var _a2, _b2;
|
|
@@ -1806,20 +2050,12 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1806
2050
|
type: ${name != null ? name : "UnknownError"}`;
|
|
1807
2051
|
})() : void 0;
|
|
1808
2052
|
if (isToolCallsCompletion) {
|
|
1809
|
-
|
|
1810
|
-
throw new Error("message.updated: reasoningParts missing for tool-call continuation");
|
|
2053
|
+
const responseText = reasoningText !== void 0 && reasoningText.trim().length > 0 ? reasoningText.trim() : outputText !== void 0 && outputText.trim().length > 0 ? outputText.trim() : "";
|
|
1811
2054
|
const llmAttrs = [
|
|
1812
2055
|
attrString("ai.operationId", "generateText"),
|
|
1813
|
-
attrString("ai.response.text",
|
|
1814
|
-
|
|
1815
|
-
attrString("gen_ai.request.model", modelID),
|
|
1816
|
-
attrString("gen_ai.response.model", modelID),
|
|
1817
|
-
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1818
|
-
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
2056
|
+
attrString("ai.response.text", responseText),
|
|
2057
|
+
...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
|
|
1819
2058
|
];
|
|
1820
|
-
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1821
|
-
llmAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1822
|
-
}
|
|
1823
2059
|
if (state.currentSystemPrompt) {
|
|
1824
2060
|
llmAttrs.push(
|
|
1825
2061
|
attrString("gen_ai.prompt.0.role", "system"),
|
|
@@ -1841,6 +2077,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1841
2077
|
return;
|
|
1842
2078
|
}
|
|
1843
2079
|
if (outputText === void 0) return;
|
|
2080
|
+
const turnOutputText = capText2(state.turnOutputParts.join("\n\n"));
|
|
1844
2081
|
const rootSpan = state.currentRootSpan;
|
|
1845
2082
|
const isCompaction = state.isCompacting === true;
|
|
1846
2083
|
if (state.parentId) {
|
|
@@ -1852,16 +2089,9 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1852
2089
|
"ai.prompt.messages",
|
|
1853
2090
|
buildPromptMessages(state.currentSystemPrompt, state.currentInput)
|
|
1854
2091
|
),
|
|
1855
|
-
attrString("ai.response.text",
|
|
1856
|
-
|
|
1857
|
-
attrString("gen_ai.request.model", modelID),
|
|
1858
|
-
attrString("gen_ai.response.model", modelID),
|
|
1859
|
-
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1860
|
-
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
2092
|
+
attrString("ai.response.text", turnOutputText),
|
|
2093
|
+
...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
|
|
1861
2094
|
];
|
|
1862
|
-
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1863
|
-
llmAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1864
|
-
}
|
|
1865
2095
|
if (state.currentSystemPrompt) {
|
|
1866
2096
|
llmAttrs.push(
|
|
1867
2097
|
attrString("gen_ai.prompt.0.role", "system"),
|
|
@@ -1904,20 +2134,13 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1904
2134
|
"ai.prompt.messages",
|
|
1905
2135
|
buildPromptMessages(state.currentSystemPrompt, state.currentInput)
|
|
1906
2136
|
),
|
|
1907
|
-
attrString("ai.response.text",
|
|
1908
|
-
|
|
1909
|
-
attrString("gen_ai.request.model", modelID),
|
|
1910
|
-
attrString("gen_ai.response.model", modelID),
|
|
1911
|
-
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1912
|
-
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
2137
|
+
attrString("ai.response.text", turnOutputText),
|
|
2138
|
+
...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
|
|
1913
2139
|
];
|
|
1914
2140
|
if (isCompaction) {
|
|
1915
2141
|
rootAttrs.push(attrString("is_compaction", "true"));
|
|
1916
2142
|
}
|
|
1917
2143
|
rootAttrs.push(attrString("message_id", messageId));
|
|
1918
|
-
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1919
|
-
rootAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1920
|
-
}
|
|
1921
2144
|
if (state.currentSystemPrompt) {
|
|
1922
2145
|
rootAttrs.push(
|
|
1923
2146
|
attrString("gen_ai.prompt.0.role", "system"),
|
|
@@ -1933,7 +2156,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1933
2156
|
}
|
|
1934
2157
|
}
|
|
1935
2158
|
state.currentRootSpan = void 0;
|
|
1936
|
-
const hasAssistantResponse =
|
|
2159
|
+
const hasAssistantResponse = turnOutputText.trim().length > 0;
|
|
1937
2160
|
if (!hasAssistantResponse && !msgError) {
|
|
1938
2161
|
await traceShipper.flush();
|
|
1939
2162
|
state.currentEventId = void 0;
|
|
@@ -1943,10 +2166,10 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1943
2166
|
await eventShipper.finish(state.currentEventId, {
|
|
1944
2167
|
userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
|
|
1945
2168
|
model: modelID,
|
|
1946
|
-
output: msgError ? void 0 :
|
|
2169
|
+
output: msgError ? void 0 : turnOutputText,
|
|
1947
2170
|
usage: {
|
|
1948
2171
|
promptTokens: inputTokens,
|
|
1949
|
-
completionTokens:
|
|
2172
|
+
completionTokens: state.turnOutputTokens
|
|
1950
2173
|
},
|
|
1951
2174
|
error: msgError ? {
|
|
1952
2175
|
name: msgError.name,
|
|
@@ -2080,6 +2303,8 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
2080
2303
|
};
|
|
2081
2304
|
const userText = capText2(textParts.join("\n"));
|
|
2082
2305
|
state.currentInput = userText;
|
|
2306
|
+
state.turnOutputParts = [];
|
|
2307
|
+
state.turnOutputTokens = 0;
|
|
2083
2308
|
if (state.parentId) {
|
|
2084
2309
|
if (!state.parentTaskSpanIds || !state.parentEventContext) {
|
|
2085
2310
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raindrop-ai/opencode-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Raindrop observability plugin for OpenCode — automatic session/event/span tracing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@types/node": "^20.11.17",
|
|
38
38
|
"tsup": "^8.4.0",
|
|
39
39
|
"typescript": "^5.3.3",
|
|
40
|
-
"@raindrop-ai/core": "0.
|
|
40
|
+
"@raindrop-ai/core": "0.2.0"
|
|
41
41
|
},
|
|
42
42
|
"tsup": {
|
|
43
43
|
"entry": [
|