@raindrop-ai/claude-code 0.0.7 → 0.0.9
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/cli.js +192 -41
- package/dist/index.cjs +190 -41
- package/dist/index.d.cts +39 -2
- package/dist/index.d.ts +39 -2
- package/dist/index.js +190 -41
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -654,7 +654,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
|
|
|
654
654
|
|
|
655
655
|
// src/package-info.ts
|
|
656
656
|
var PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
657
|
-
var PACKAGE_VERSION = "0.0.
|
|
657
|
+
var PACKAGE_VERSION = "0.0.8";
|
|
658
658
|
|
|
659
659
|
// src/shipper.ts
|
|
660
660
|
var EventShipper2 = class extends EventShipper {
|
|
@@ -693,9 +693,47 @@ var TraceShipper2 = class extends TraceShipper {
|
|
|
693
693
|
|
|
694
694
|
// src/transcript.ts
|
|
695
695
|
import { existsSync, readFileSync } from "fs";
|
|
696
|
+
function isToolResultContentBlock(value) {
|
|
697
|
+
return Boolean(
|
|
698
|
+
value && typeof value === "object" && "type" in value && value["type"] === "tool_result"
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
function isTopLevelUserPrompt(entry) {
|
|
702
|
+
var _a;
|
|
703
|
+
if (entry.type !== "user") return false;
|
|
704
|
+
const content = (_a = entry.message) == null ? void 0 : _a.content;
|
|
705
|
+
if (typeof content === "string") return true;
|
|
706
|
+
if (!Array.isArray(content)) return false;
|
|
707
|
+
return content.some((block) => !isToolResultContentBlock(block));
|
|
708
|
+
}
|
|
696
709
|
function parseTranscript(transcriptPath) {
|
|
697
|
-
var _a, _b, _c, _d, _e;
|
|
710
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
698
711
|
try {
|
|
712
|
+
let finalizePhase2 = function() {
|
|
713
|
+
if (activePhase && (activePhase.text || activePhase.toolCalls.length > 0)) {
|
|
714
|
+
currentPhases.push(activePhase);
|
|
715
|
+
}
|
|
716
|
+
activePhase = void 0;
|
|
717
|
+
}, ensurePhase2 = function() {
|
|
718
|
+
if (!activePhase) {
|
|
719
|
+
activePhase = {
|
|
720
|
+
text: "",
|
|
721
|
+
toolCalls: [],
|
|
722
|
+
inputTokens: 0,
|
|
723
|
+
outputTokens: 0,
|
|
724
|
+
cacheReadTokens: 0,
|
|
725
|
+
hasThinking: false
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
return activePhase;
|
|
729
|
+
}, isToolResultUserEntry2 = function(entry) {
|
|
730
|
+
var _a2;
|
|
731
|
+
if (entry.type !== "user") return false;
|
|
732
|
+
const content2 = (_a2 = entry.message) == null ? void 0 : _a2.content;
|
|
733
|
+
if (!Array.isArray(content2)) return false;
|
|
734
|
+
return content2.length > 0 && content2.every((b) => isToolResultContentBlock(b));
|
|
735
|
+
};
|
|
736
|
+
var finalizePhase = finalizePhase2, ensurePhase = ensurePhase2, isToolResultUserEntry = isToolResultUserEntry2;
|
|
699
737
|
if (!existsSync(transcriptPath)) return void 0;
|
|
700
738
|
const content = readFileSync(transcriptPath, "utf-8");
|
|
701
739
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
@@ -706,10 +744,15 @@ function parseTranscript(transcriptPath) {
|
|
|
706
744
|
totalCacheCreationTokens: 0,
|
|
707
745
|
turnCount: 0,
|
|
708
746
|
toolsUsed: [],
|
|
709
|
-
hasThinking: false
|
|
747
|
+
hasThinking: false,
|
|
748
|
+
lastTurnTextBlocks: [],
|
|
749
|
+
llmCallPhases: []
|
|
710
750
|
};
|
|
711
751
|
const toolNames = /* @__PURE__ */ new Set();
|
|
712
752
|
let lastUsage;
|
|
753
|
+
let currentTurnTextBlocks = [];
|
|
754
|
+
let currentPhases = [];
|
|
755
|
+
let activePhase;
|
|
713
756
|
for (const line of lines) {
|
|
714
757
|
let entry;
|
|
715
758
|
try {
|
|
@@ -732,6 +775,15 @@ function parseTranscript(transcriptPath) {
|
|
|
732
775
|
}
|
|
733
776
|
continue;
|
|
734
777
|
}
|
|
778
|
+
if (isTopLevelUserPrompt(entry)) {
|
|
779
|
+
currentTurnTextBlocks = [];
|
|
780
|
+
finalizePhase2();
|
|
781
|
+
currentPhases = [];
|
|
782
|
+
}
|
|
783
|
+
if (isToolResultUserEntry2(entry)) {
|
|
784
|
+
finalizePhase2();
|
|
785
|
+
continue;
|
|
786
|
+
}
|
|
735
787
|
if (entry.type !== "assistant") continue;
|
|
736
788
|
const msg = entry.message;
|
|
737
789
|
if (!msg) continue;
|
|
@@ -761,23 +813,55 @@ function parseTranscript(transcriptPath) {
|
|
|
761
813
|
}
|
|
762
814
|
lastUsage = u;
|
|
763
815
|
}
|
|
816
|
+
const phase = ensurePhase2();
|
|
817
|
+
const entryTimestamp = typeof entry["timestamp"] === "string" ? entry["timestamp"] : void 0;
|
|
818
|
+
if (entryTimestamp) {
|
|
819
|
+
if (!phase.startTimestamp) phase.startTimestamp = entryTimestamp;
|
|
820
|
+
phase.endTimestamp = entryTimestamp;
|
|
821
|
+
}
|
|
822
|
+
if (msg.model) phase.model = msg.model;
|
|
823
|
+
if (msg.usage) {
|
|
824
|
+
phase.inputTokens += (_f = msg.usage.input_tokens) != null ? _f : 0;
|
|
825
|
+
phase.outputTokens += (_g = msg.usage.output_tokens) != null ? _g : 0;
|
|
826
|
+
phase.cacheReadTokens += (_h = msg.usage.cache_read_input_tokens) != null ? _h : 0;
|
|
827
|
+
}
|
|
764
828
|
if (Array.isArray(msg.content)) {
|
|
765
829
|
for (const block of msg.content) {
|
|
766
830
|
if (block.type === "tool_use" && block.name) {
|
|
767
831
|
toolNames.add(block.name);
|
|
832
|
+
phase.toolCalls.push({
|
|
833
|
+
id: block.id,
|
|
834
|
+
name: block.name,
|
|
835
|
+
input: block.input
|
|
836
|
+
});
|
|
768
837
|
}
|
|
769
838
|
if (block.type === "thinking") {
|
|
770
839
|
summary.hasThinking = true;
|
|
840
|
+
phase.hasThinking = true;
|
|
841
|
+
}
|
|
842
|
+
if (block.type === "text" && typeof block.text === "string" && block.text.trim()) {
|
|
843
|
+
currentTurnTextBlocks.push(block.text);
|
|
844
|
+
if (phase.text) {
|
|
845
|
+
phase.text += "\n" + block.text;
|
|
846
|
+
} else {
|
|
847
|
+
phase.text = block.text;
|
|
848
|
+
}
|
|
771
849
|
}
|
|
772
850
|
}
|
|
851
|
+
summary.lastTurnTextBlocks = [...currentTurnTextBlocks];
|
|
773
852
|
}
|
|
774
853
|
}
|
|
854
|
+
finalizePhase2();
|
|
775
855
|
if (lastUsage) {
|
|
776
856
|
summary.lastTurnInputTokens = lastUsage.input_tokens;
|
|
777
857
|
summary.lastTurnOutputTokens = lastUsage.output_tokens;
|
|
778
858
|
summary.lastTurnCacheReadTokens = lastUsage.cache_read_input_tokens;
|
|
779
859
|
}
|
|
780
860
|
summary.toolsUsed = [...toolNames].sort();
|
|
861
|
+
if (summary.lastTurnTextBlocks.length > 0) {
|
|
862
|
+
summary.lastTurnFullOutput = summary.lastTurnTextBlocks.join("\n\n");
|
|
863
|
+
}
|
|
864
|
+
summary.llmCallPhases = currentPhases;
|
|
781
865
|
return summary;
|
|
782
866
|
} catch (e) {
|
|
783
867
|
return void 0;
|
|
@@ -926,12 +1010,13 @@ function readTimestamp(key) {
|
|
|
926
1010
|
return Number.isFinite(ts) ? ts : void 0;
|
|
927
1011
|
}
|
|
928
1012
|
async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
929
|
-
var _a;
|
|
930
|
-
const convoId = payload.session_id;
|
|
1013
|
+
var _a, _b;
|
|
1014
|
+
const convoId = (_a = config.convoId) != null ? _a : payload.session_id;
|
|
931
1015
|
const baseProperties = {
|
|
932
1016
|
...config.customProperties,
|
|
933
1017
|
cwd: payload.cwd,
|
|
934
1018
|
permission_mode: payload.permission_mode,
|
|
1019
|
+
claude_code_session_id: payload.session_id,
|
|
935
1020
|
plugin_version: PACKAGE_VERSION,
|
|
936
1021
|
sdk: PACKAGE_NAME,
|
|
937
1022
|
sdk_version: PACKAGE_VERSION,
|
|
@@ -940,7 +1025,7 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
940
1025
|
};
|
|
941
1026
|
switch (payload.hook_event_name) {
|
|
942
1027
|
case "SessionStart":
|
|
943
|
-
writeState(`model_${payload.session_id}`, (
|
|
1028
|
+
writeState(`model_${payload.session_id}`, (_b = payload.model) != null ? _b : "");
|
|
944
1029
|
tryCaptureAppendSystemPrompt(payload.session_id);
|
|
945
1030
|
break;
|
|
946
1031
|
case "UserPromptSubmit":
|
|
@@ -956,10 +1041,10 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
956
1041
|
handlePostToolUseFailure(payload, getCurrentEventId(payload.session_id), traceShipper);
|
|
957
1042
|
break;
|
|
958
1043
|
case "Stop":
|
|
959
|
-
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper);
|
|
1044
|
+
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), convoId, config, baseProperties, eventShipper, traceShipper);
|
|
960
1045
|
break;
|
|
961
1046
|
case "StopFailure":
|
|
962
|
-
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper, {
|
|
1047
|
+
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), convoId, config, baseProperties, eventShipper, traceShipper, {
|
|
963
1048
|
error: payload.error,
|
|
964
1049
|
error_details: payload.error_details
|
|
965
1050
|
});
|
|
@@ -977,10 +1062,10 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
977
1062
|
handleInstructionsLoaded(payload);
|
|
978
1063
|
break;
|
|
979
1064
|
case "PostCompact":
|
|
980
|
-
await handlePostCompact(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
|
|
1065
|
+
await handlePostCompact(payload, getCurrentEventId(payload.session_id), convoId, config, baseProperties, eventShipper);
|
|
981
1066
|
break;
|
|
982
1067
|
case "SessionEnd":
|
|
983
|
-
await handleSessionEnd(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
|
|
1068
|
+
await handleSessionEnd(payload, getCurrentEventId(payload.session_id), convoId, config, baseProperties, eventShipper);
|
|
984
1069
|
deleteState(turnEventKey(payload.session_id));
|
|
985
1070
|
deleteState(rootSpanKey(payload.session_id));
|
|
986
1071
|
deleteState(`model_${payload.session_id}`);
|
|
@@ -1130,11 +1215,11 @@ function handlePermissionDenied(payload, eventId, traceShipper) {
|
|
|
1130
1215
|
}
|
|
1131
1216
|
});
|
|
1132
1217
|
}
|
|
1133
|
-
async function handlePostCompact(payload, eventId, config, properties, eventShipper) {
|
|
1218
|
+
async function handlePostCompact(payload, eventId, convoId, config, properties, eventShipper) {
|
|
1134
1219
|
await eventShipper.patch(eventId, {
|
|
1135
1220
|
isPending: true,
|
|
1136
1221
|
userId: config.userId,
|
|
1137
|
-
convoId
|
|
1222
|
+
convoId,
|
|
1138
1223
|
eventName: config.eventName,
|
|
1139
1224
|
properties: {
|
|
1140
1225
|
...properties,
|
|
@@ -1328,8 +1413,18 @@ function readAppendSystemPrompt(sessionId) {
|
|
|
1328
1413
|
return void 0;
|
|
1329
1414
|
}
|
|
1330
1415
|
}
|
|
1416
|
+
function isoToNanoString(iso, fallback) {
|
|
1417
|
+
if (!iso) return fallback;
|
|
1418
|
+
try {
|
|
1419
|
+
const ms = new Date(iso).getTime();
|
|
1420
|
+
if (!Number.isFinite(ms)) return fallback;
|
|
1421
|
+
return String(ms) + "000000";
|
|
1422
|
+
} catch (e) {
|
|
1423
|
+
return fallback;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1331
1426
|
function enrichFromTranscript(payload, eventId, traceShipper) {
|
|
1332
|
-
var _a, _b;
|
|
1427
|
+
var _a, _b, _c, _d;
|
|
1333
1428
|
if (!payload.transcript_path) {
|
|
1334
1429
|
return { summary: void 0, props: {} };
|
|
1335
1430
|
}
|
|
@@ -1337,42 +1432,94 @@ function enrichFromTranscript(payload, eventId, traceShipper) {
|
|
|
1337
1432
|
const summary = parseTranscript(payload.transcript_path);
|
|
1338
1433
|
if (!summary) return { summary: void 0, props: {} };
|
|
1339
1434
|
const props = transcriptToProperties(summary);
|
|
1340
|
-
const
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1435
|
+
const parent = getParentContext(payload);
|
|
1436
|
+
if (summary.llmCallPhases.length > 0) {
|
|
1437
|
+
let emittedPhaseIndex = 0;
|
|
1438
|
+
for (let i = 0; i < summary.llmCallPhases.length; i++) {
|
|
1439
|
+
const phase = summary.llmCallPhases[i];
|
|
1440
|
+
const outputText = phase.text.trim();
|
|
1441
|
+
if (!outputText) {
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
const model = (_b = (_a = phase.model) != null ? _a : summary.model) != null ? _b : "claude";
|
|
1445
|
+
const now = nowUnixNanoString();
|
|
1446
|
+
const startNano = isoToNanoString(phase.startTimestamp, now);
|
|
1447
|
+
const endNano = isoToNanoString(phase.endTimestamp, startNano);
|
|
1448
|
+
const toolCallNames = phase.toolCalls.map((tc) => tc.name).join(", ");
|
|
1449
|
+
traceShipper.createSpan({
|
|
1450
|
+
name: model,
|
|
1451
|
+
eventId,
|
|
1452
|
+
parent,
|
|
1453
|
+
startTimeUnixNano: startNano,
|
|
1454
|
+
endTimeUnixNano: endNano,
|
|
1455
|
+
attributes: [
|
|
1456
|
+
attrString("ai.operationId", "generateText"),
|
|
1457
|
+
attrString("gen_ai.response.model", model),
|
|
1458
|
+
attrString("gen_ai.system", "anthropic"),
|
|
1459
|
+
attrInt("gen_ai.usage.prompt_tokens", phase.inputTokens),
|
|
1460
|
+
attrInt("gen_ai.usage.completion_tokens", phase.outputTokens),
|
|
1461
|
+
...phase.cacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", phase.cacheReadTokens)] : [],
|
|
1462
|
+
attrString("ai.response.text", outputText),
|
|
1463
|
+
...toolCallNames ? [attrString("ai.toolCall.names", toolCallNames)] : [],
|
|
1464
|
+
...phase.hasThinking ? [attrString("ai.has_thinking", "true")] : [],
|
|
1465
|
+
attrInt("ai.llm_call.index", emittedPhaseIndex)
|
|
1466
|
+
]
|
|
1467
|
+
});
|
|
1468
|
+
emittedPhaseIndex++;
|
|
1469
|
+
}
|
|
1470
|
+
} else {
|
|
1471
|
+
const spanInputTokens = (_c = summary.lastTurnInputTokens) != null ? _c : summary.totalInputTokens;
|
|
1472
|
+
const spanOutputTokens = (_d = summary.lastTurnOutputTokens) != null ? _d : summary.totalOutputTokens;
|
|
1473
|
+
if (summary.model && (spanInputTokens > 0 || spanOutputTokens > 0)) {
|
|
1474
|
+
const now = nowUnixNanoString();
|
|
1475
|
+
traceShipper.createSpan({
|
|
1476
|
+
name: summary.model,
|
|
1477
|
+
eventId,
|
|
1478
|
+
parent,
|
|
1479
|
+
startTimeUnixNano: now,
|
|
1480
|
+
endTimeUnixNano: now,
|
|
1481
|
+
attributes: [
|
|
1482
|
+
attrString("ai.operationId", "generateText"),
|
|
1483
|
+
attrString("gen_ai.response.model", summary.model),
|
|
1484
|
+
attrString("gen_ai.system", "anthropic"),
|
|
1485
|
+
attrInt("gen_ai.usage.prompt_tokens", spanInputTokens),
|
|
1486
|
+
attrInt("gen_ai.usage.completion_tokens", spanOutputTokens),
|
|
1487
|
+
...summary.lastTurnCacheReadTokens != null && summary.lastTurnCacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", summary.lastTurnCacheReadTokens)] : []
|
|
1488
|
+
]
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1360
1491
|
}
|
|
1361
1492
|
return { summary, props };
|
|
1362
1493
|
} catch (e) {
|
|
1363
1494
|
return { summary: void 0, props: {} };
|
|
1364
1495
|
}
|
|
1365
1496
|
}
|
|
1366
|
-
|
|
1497
|
+
function normalizeAssistantText(value) {
|
|
1498
|
+
return value.trim().replace(/\r\n/g, "\n");
|
|
1499
|
+
}
|
|
1500
|
+
function buildStopOutput(summary, lastAssistantMessage) {
|
|
1501
|
+
const finalMessage = typeof lastAssistantMessage === "string" ? normalizeAssistantText(lastAssistantMessage) : void 0;
|
|
1502
|
+
if (!summary) return finalMessage;
|
|
1503
|
+
const transcriptBlocks = summary.lastTurnTextBlocks.map(normalizeAssistantText).filter(Boolean);
|
|
1504
|
+
if (finalMessage && !transcriptBlocks.includes(finalMessage)) {
|
|
1505
|
+
transcriptBlocks.push(finalMessage);
|
|
1506
|
+
}
|
|
1507
|
+
if (transcriptBlocks.length > 0) {
|
|
1508
|
+
return transcriptBlocks.join("\n\n");
|
|
1509
|
+
}
|
|
1510
|
+
return finalMessage;
|
|
1511
|
+
}
|
|
1512
|
+
async function handleStopOrFailure(payload, eventId, convoId, config, properties, eventShipper, traceShipper, extraProperties) {
|
|
1367
1513
|
const { summary, props: transcriptProps } = enrichFromTranscript(payload, eventId, traceShipper);
|
|
1368
1514
|
const instructions = gatherInstructions(payload.session_id);
|
|
1369
1515
|
const appendSysPrompt = readAppendSystemPrompt(payload.session_id);
|
|
1516
|
+
const output = buildStopOutput(summary, payload.last_assistant_message);
|
|
1370
1517
|
await eventShipper.patch(eventId, {
|
|
1371
1518
|
isPending: false,
|
|
1372
1519
|
userId: config.userId,
|
|
1373
|
-
convoId
|
|
1520
|
+
convoId,
|
|
1374
1521
|
eventName: config.eventName,
|
|
1375
|
-
output
|
|
1522
|
+
output,
|
|
1376
1523
|
...(summary == null ? void 0 : summary.model) ? { model: summary.model } : {},
|
|
1377
1524
|
properties: {
|
|
1378
1525
|
...properties,
|
|
@@ -1383,10 +1530,11 @@ async function handleStopOrFailure(payload, eventId, config, properties, eventSh
|
|
|
1383
1530
|
}
|
|
1384
1531
|
});
|
|
1385
1532
|
}
|
|
1386
|
-
async function handleSessionEnd(payload, eventId, config, properties, eventShipper) {
|
|
1533
|
+
async function handleSessionEnd(payload, eventId, convoId, config, properties, eventShipper) {
|
|
1387
1534
|
await eventShipper.patch(eventId, {
|
|
1388
1535
|
isPending: false,
|
|
1389
1536
|
userId: config.userId,
|
|
1537
|
+
convoId,
|
|
1390
1538
|
eventName: config.eventName,
|
|
1391
1539
|
properties: {
|
|
1392
1540
|
...properties,
|
|
@@ -1401,7 +1549,7 @@ import { homedir, userInfo } from "os";
|
|
|
1401
1549
|
import { dirname, join as join2 } from "path";
|
|
1402
1550
|
var CONFIG_PATH = join2(homedir(), ".config", "raindrop", "config.json");
|
|
1403
1551
|
function loadConfig() {
|
|
1404
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
1552
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
1405
1553
|
let file = {};
|
|
1406
1554
|
try {
|
|
1407
1555
|
if (existsSync3(CONFIG_PATH)) {
|
|
@@ -1442,9 +1590,10 @@ function loadConfig() {
|
|
|
1442
1590
|
writeKey: (_c = (_b = process.env["RAINDROP_WRITE_KEY"]) != null ? _b : file.write_key) != null ? _c : "",
|
|
1443
1591
|
endpoint: (_e = (_d = process.env["RAINDROP_API_URL"]) != null ? _d : file.api_url) != null ? _e : "https://api.raindrop.ai/v1",
|
|
1444
1592
|
userId: (_g = (_f = process.env["RAINDROP_USER_ID"]) != null ? _f : file.user_id) != null ? _g : systemUser,
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1593
|
+
convoId: ((_h = process.env["RAINDROP_CONVO_ID"]) == null ? void 0 : _h.trim()) || void 0,
|
|
1594
|
+
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_i = file.debug) != null ? _i : false,
|
|
1595
|
+
enabled: (_j = file.enabled) != null ? _j : true,
|
|
1596
|
+
eventName: (_l = (_k = process.env["RAINDROP_EVENT_NAME"]) != null ? _k : file.event_name) != null ? _l : "claude_code_session",
|
|
1448
1597
|
customProperties,
|
|
1449
1598
|
selfDiagnostics
|
|
1450
1599
|
};
|
|
@@ -1617,6 +1766,7 @@ async function handleHook() {
|
|
|
1617
1766
|
}
|
|
1618
1767
|
const mapperConfig = {
|
|
1619
1768
|
userId: config.userId,
|
|
1769
|
+
convoId: config.convoId,
|
|
1620
1770
|
debug: config.debug,
|
|
1621
1771
|
eventName: config.eventName,
|
|
1622
1772
|
customProperties: config.customProperties
|
|
@@ -2348,6 +2498,7 @@ async function main() {
|
|
|
2348
2498
|
Environment:
|
|
2349
2499
|
RAINDROP_WRITE_KEY API write key (alternative to --write-key or config file)
|
|
2350
2500
|
RAINDROP_USER_ID User ID override
|
|
2501
|
+
RAINDROP_CONVO_ID Conversation/thread ID override
|
|
2351
2502
|
RAINDROP_EVENT_NAME Custom event name (default: "claude_code_session")
|
|
2352
2503
|
RAINDROP_PROPERTIES JSON object merged into every event's properties
|
|
2353
2504
|
RAINDROP_API_URL Custom API endpoint
|
package/dist/index.cjs
CHANGED
|
@@ -690,7 +690,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
|
|
|
690
690
|
|
|
691
691
|
// src/package-info.ts
|
|
692
692
|
var PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
693
|
-
var PACKAGE_VERSION = "0.0.
|
|
693
|
+
var PACKAGE_VERSION = "0.0.8";
|
|
694
694
|
|
|
695
695
|
// src/shipper.ts
|
|
696
696
|
var EventShipper2 = class extends EventShipper {
|
|
@@ -733,7 +733,7 @@ var import_node_os = require("os");
|
|
|
733
733
|
var import_node_path = require("path");
|
|
734
734
|
var CONFIG_PATH = (0, import_node_path.join)((0, import_node_os.homedir)(), ".config", "raindrop", "config.json");
|
|
735
735
|
function loadConfig() {
|
|
736
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
736
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
737
737
|
let file = {};
|
|
738
738
|
try {
|
|
739
739
|
if ((0, import_node_fs.existsSync)(CONFIG_PATH)) {
|
|
@@ -774,9 +774,10 @@ function loadConfig() {
|
|
|
774
774
|
writeKey: (_c = (_b = process.env["RAINDROP_WRITE_KEY"]) != null ? _b : file.write_key) != null ? _c : "",
|
|
775
775
|
endpoint: (_e = (_d = process.env["RAINDROP_API_URL"]) != null ? _d : file.api_url) != null ? _e : "https://api.raindrop.ai/v1",
|
|
776
776
|
userId: (_g = (_f = process.env["RAINDROP_USER_ID"]) != null ? _f : file.user_id) != null ? _g : systemUser,
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
777
|
+
convoId: ((_h = process.env["RAINDROP_CONVO_ID"]) == null ? void 0 : _h.trim()) || void 0,
|
|
778
|
+
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_i = file.debug) != null ? _i : false,
|
|
779
|
+
enabled: (_j = file.enabled) != null ? _j : true,
|
|
780
|
+
eventName: (_l = (_k = process.env["RAINDROP_EVENT_NAME"]) != null ? _k : file.event_name) != null ? _l : "claude_code_session",
|
|
780
781
|
customProperties,
|
|
781
782
|
selfDiagnostics
|
|
782
783
|
};
|
|
@@ -806,9 +807,47 @@ var import_node_path2 = require("path");
|
|
|
806
807
|
|
|
807
808
|
// src/transcript.ts
|
|
808
809
|
var import_node_fs2 = require("fs");
|
|
810
|
+
function isToolResultContentBlock(value) {
|
|
811
|
+
return Boolean(
|
|
812
|
+
value && typeof value === "object" && "type" in value && value["type"] === "tool_result"
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
function isTopLevelUserPrompt(entry) {
|
|
816
|
+
var _a;
|
|
817
|
+
if (entry.type !== "user") return false;
|
|
818
|
+
const content = (_a = entry.message) == null ? void 0 : _a.content;
|
|
819
|
+
if (typeof content === "string") return true;
|
|
820
|
+
if (!Array.isArray(content)) return false;
|
|
821
|
+
return content.some((block) => !isToolResultContentBlock(block));
|
|
822
|
+
}
|
|
809
823
|
function parseTranscript(transcriptPath) {
|
|
810
|
-
var _a, _b, _c, _d, _e;
|
|
824
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
811
825
|
try {
|
|
826
|
+
let finalizePhase2 = function() {
|
|
827
|
+
if (activePhase && (activePhase.text || activePhase.toolCalls.length > 0)) {
|
|
828
|
+
currentPhases.push(activePhase);
|
|
829
|
+
}
|
|
830
|
+
activePhase = void 0;
|
|
831
|
+
}, ensurePhase2 = function() {
|
|
832
|
+
if (!activePhase) {
|
|
833
|
+
activePhase = {
|
|
834
|
+
text: "",
|
|
835
|
+
toolCalls: [],
|
|
836
|
+
inputTokens: 0,
|
|
837
|
+
outputTokens: 0,
|
|
838
|
+
cacheReadTokens: 0,
|
|
839
|
+
hasThinking: false
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
return activePhase;
|
|
843
|
+
}, isToolResultUserEntry2 = function(entry) {
|
|
844
|
+
var _a2;
|
|
845
|
+
if (entry.type !== "user") return false;
|
|
846
|
+
const content2 = (_a2 = entry.message) == null ? void 0 : _a2.content;
|
|
847
|
+
if (!Array.isArray(content2)) return false;
|
|
848
|
+
return content2.length > 0 && content2.every((b) => isToolResultContentBlock(b));
|
|
849
|
+
};
|
|
850
|
+
var finalizePhase = finalizePhase2, ensurePhase = ensurePhase2, isToolResultUserEntry = isToolResultUserEntry2;
|
|
812
851
|
if (!(0, import_node_fs2.existsSync)(transcriptPath)) return void 0;
|
|
813
852
|
const content = (0, import_node_fs2.readFileSync)(transcriptPath, "utf-8");
|
|
814
853
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
@@ -819,10 +858,15 @@ function parseTranscript(transcriptPath) {
|
|
|
819
858
|
totalCacheCreationTokens: 0,
|
|
820
859
|
turnCount: 0,
|
|
821
860
|
toolsUsed: [],
|
|
822
|
-
hasThinking: false
|
|
861
|
+
hasThinking: false,
|
|
862
|
+
lastTurnTextBlocks: [],
|
|
863
|
+
llmCallPhases: []
|
|
823
864
|
};
|
|
824
865
|
const toolNames = /* @__PURE__ */ new Set();
|
|
825
866
|
let lastUsage;
|
|
867
|
+
let currentTurnTextBlocks = [];
|
|
868
|
+
let currentPhases = [];
|
|
869
|
+
let activePhase;
|
|
826
870
|
for (const line of lines) {
|
|
827
871
|
let entry;
|
|
828
872
|
try {
|
|
@@ -845,6 +889,15 @@ function parseTranscript(transcriptPath) {
|
|
|
845
889
|
}
|
|
846
890
|
continue;
|
|
847
891
|
}
|
|
892
|
+
if (isTopLevelUserPrompt(entry)) {
|
|
893
|
+
currentTurnTextBlocks = [];
|
|
894
|
+
finalizePhase2();
|
|
895
|
+
currentPhases = [];
|
|
896
|
+
}
|
|
897
|
+
if (isToolResultUserEntry2(entry)) {
|
|
898
|
+
finalizePhase2();
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
848
901
|
if (entry.type !== "assistant") continue;
|
|
849
902
|
const msg = entry.message;
|
|
850
903
|
if (!msg) continue;
|
|
@@ -874,23 +927,55 @@ function parseTranscript(transcriptPath) {
|
|
|
874
927
|
}
|
|
875
928
|
lastUsage = u;
|
|
876
929
|
}
|
|
930
|
+
const phase = ensurePhase2();
|
|
931
|
+
const entryTimestamp = typeof entry["timestamp"] === "string" ? entry["timestamp"] : void 0;
|
|
932
|
+
if (entryTimestamp) {
|
|
933
|
+
if (!phase.startTimestamp) phase.startTimestamp = entryTimestamp;
|
|
934
|
+
phase.endTimestamp = entryTimestamp;
|
|
935
|
+
}
|
|
936
|
+
if (msg.model) phase.model = msg.model;
|
|
937
|
+
if (msg.usage) {
|
|
938
|
+
phase.inputTokens += (_f = msg.usage.input_tokens) != null ? _f : 0;
|
|
939
|
+
phase.outputTokens += (_g = msg.usage.output_tokens) != null ? _g : 0;
|
|
940
|
+
phase.cacheReadTokens += (_h = msg.usage.cache_read_input_tokens) != null ? _h : 0;
|
|
941
|
+
}
|
|
877
942
|
if (Array.isArray(msg.content)) {
|
|
878
943
|
for (const block of msg.content) {
|
|
879
944
|
if (block.type === "tool_use" && block.name) {
|
|
880
945
|
toolNames.add(block.name);
|
|
946
|
+
phase.toolCalls.push({
|
|
947
|
+
id: block.id,
|
|
948
|
+
name: block.name,
|
|
949
|
+
input: block.input
|
|
950
|
+
});
|
|
881
951
|
}
|
|
882
952
|
if (block.type === "thinking") {
|
|
883
953
|
summary.hasThinking = true;
|
|
954
|
+
phase.hasThinking = true;
|
|
955
|
+
}
|
|
956
|
+
if (block.type === "text" && typeof block.text === "string" && block.text.trim()) {
|
|
957
|
+
currentTurnTextBlocks.push(block.text);
|
|
958
|
+
if (phase.text) {
|
|
959
|
+
phase.text += "\n" + block.text;
|
|
960
|
+
} else {
|
|
961
|
+
phase.text = block.text;
|
|
962
|
+
}
|
|
884
963
|
}
|
|
885
964
|
}
|
|
965
|
+
summary.lastTurnTextBlocks = [...currentTurnTextBlocks];
|
|
886
966
|
}
|
|
887
967
|
}
|
|
968
|
+
finalizePhase2();
|
|
888
969
|
if (lastUsage) {
|
|
889
970
|
summary.lastTurnInputTokens = lastUsage.input_tokens;
|
|
890
971
|
summary.lastTurnOutputTokens = lastUsage.output_tokens;
|
|
891
972
|
summary.lastTurnCacheReadTokens = lastUsage.cache_read_input_tokens;
|
|
892
973
|
}
|
|
893
974
|
summary.toolsUsed = [...toolNames].sort();
|
|
975
|
+
if (summary.lastTurnTextBlocks.length > 0) {
|
|
976
|
+
summary.lastTurnFullOutput = summary.lastTurnTextBlocks.join("\n\n");
|
|
977
|
+
}
|
|
978
|
+
summary.llmCallPhases = currentPhases;
|
|
894
979
|
return summary;
|
|
895
980
|
} catch (e) {
|
|
896
981
|
return void 0;
|
|
@@ -1039,12 +1124,13 @@ function readTimestamp(key) {
|
|
|
1039
1124
|
return Number.isFinite(ts) ? ts : void 0;
|
|
1040
1125
|
}
|
|
1041
1126
|
async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
1042
|
-
var _a;
|
|
1043
|
-
const convoId = payload.session_id;
|
|
1127
|
+
var _a, _b;
|
|
1128
|
+
const convoId = (_a = config.convoId) != null ? _a : payload.session_id;
|
|
1044
1129
|
const baseProperties = {
|
|
1045
1130
|
...config.customProperties,
|
|
1046
1131
|
cwd: payload.cwd,
|
|
1047
1132
|
permission_mode: payload.permission_mode,
|
|
1133
|
+
claude_code_session_id: payload.session_id,
|
|
1048
1134
|
plugin_version: PACKAGE_VERSION,
|
|
1049
1135
|
sdk: PACKAGE_NAME,
|
|
1050
1136
|
sdk_version: PACKAGE_VERSION,
|
|
@@ -1053,7 +1139,7 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
1053
1139
|
};
|
|
1054
1140
|
switch (payload.hook_event_name) {
|
|
1055
1141
|
case "SessionStart":
|
|
1056
|
-
writeState(`model_${payload.session_id}`, (
|
|
1142
|
+
writeState(`model_${payload.session_id}`, (_b = payload.model) != null ? _b : "");
|
|
1057
1143
|
tryCaptureAppendSystemPrompt(payload.session_id);
|
|
1058
1144
|
break;
|
|
1059
1145
|
case "UserPromptSubmit":
|
|
@@ -1069,10 +1155,10 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
1069
1155
|
handlePostToolUseFailure(payload, getCurrentEventId(payload.session_id), traceShipper);
|
|
1070
1156
|
break;
|
|
1071
1157
|
case "Stop":
|
|
1072
|
-
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper);
|
|
1158
|
+
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), convoId, config, baseProperties, eventShipper, traceShipper);
|
|
1073
1159
|
break;
|
|
1074
1160
|
case "StopFailure":
|
|
1075
|
-
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper, {
|
|
1161
|
+
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), convoId, config, baseProperties, eventShipper, traceShipper, {
|
|
1076
1162
|
error: payload.error,
|
|
1077
1163
|
error_details: payload.error_details
|
|
1078
1164
|
});
|
|
@@ -1090,10 +1176,10 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
1090
1176
|
handleInstructionsLoaded(payload);
|
|
1091
1177
|
break;
|
|
1092
1178
|
case "PostCompact":
|
|
1093
|
-
await handlePostCompact(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
|
|
1179
|
+
await handlePostCompact(payload, getCurrentEventId(payload.session_id), convoId, config, baseProperties, eventShipper);
|
|
1094
1180
|
break;
|
|
1095
1181
|
case "SessionEnd":
|
|
1096
|
-
await handleSessionEnd(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
|
|
1182
|
+
await handleSessionEnd(payload, getCurrentEventId(payload.session_id), convoId, config, baseProperties, eventShipper);
|
|
1097
1183
|
deleteState(turnEventKey(payload.session_id));
|
|
1098
1184
|
deleteState(rootSpanKey(payload.session_id));
|
|
1099
1185
|
deleteState(`model_${payload.session_id}`);
|
|
@@ -1243,11 +1329,11 @@ function handlePermissionDenied(payload, eventId, traceShipper) {
|
|
|
1243
1329
|
}
|
|
1244
1330
|
});
|
|
1245
1331
|
}
|
|
1246
|
-
async function handlePostCompact(payload, eventId, config, properties, eventShipper) {
|
|
1332
|
+
async function handlePostCompact(payload, eventId, convoId, config, properties, eventShipper) {
|
|
1247
1333
|
await eventShipper.patch(eventId, {
|
|
1248
1334
|
isPending: true,
|
|
1249
1335
|
userId: config.userId,
|
|
1250
|
-
convoId
|
|
1336
|
+
convoId,
|
|
1251
1337
|
eventName: config.eventName,
|
|
1252
1338
|
properties: {
|
|
1253
1339
|
...properties,
|
|
@@ -1441,8 +1527,18 @@ function readAppendSystemPrompt(sessionId) {
|
|
|
1441
1527
|
return void 0;
|
|
1442
1528
|
}
|
|
1443
1529
|
}
|
|
1530
|
+
function isoToNanoString(iso, fallback) {
|
|
1531
|
+
if (!iso) return fallback;
|
|
1532
|
+
try {
|
|
1533
|
+
const ms = new Date(iso).getTime();
|
|
1534
|
+
if (!Number.isFinite(ms)) return fallback;
|
|
1535
|
+
return String(ms) + "000000";
|
|
1536
|
+
} catch (e) {
|
|
1537
|
+
return fallback;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1444
1540
|
function enrichFromTranscript(payload, eventId, traceShipper) {
|
|
1445
|
-
var _a, _b;
|
|
1541
|
+
var _a, _b, _c, _d;
|
|
1446
1542
|
if (!payload.transcript_path) {
|
|
1447
1543
|
return { summary: void 0, props: {} };
|
|
1448
1544
|
}
|
|
@@ -1450,42 +1546,94 @@ function enrichFromTranscript(payload, eventId, traceShipper) {
|
|
|
1450
1546
|
const summary = parseTranscript(payload.transcript_path);
|
|
1451
1547
|
if (!summary) return { summary: void 0, props: {} };
|
|
1452
1548
|
const props = transcriptToProperties(summary);
|
|
1453
|
-
const
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1549
|
+
const parent = getParentContext(payload);
|
|
1550
|
+
if (summary.llmCallPhases.length > 0) {
|
|
1551
|
+
let emittedPhaseIndex = 0;
|
|
1552
|
+
for (let i = 0; i < summary.llmCallPhases.length; i++) {
|
|
1553
|
+
const phase = summary.llmCallPhases[i];
|
|
1554
|
+
const outputText = phase.text.trim();
|
|
1555
|
+
if (!outputText) {
|
|
1556
|
+
continue;
|
|
1557
|
+
}
|
|
1558
|
+
const model = (_b = (_a = phase.model) != null ? _a : summary.model) != null ? _b : "claude";
|
|
1559
|
+
const now = nowUnixNanoString();
|
|
1560
|
+
const startNano = isoToNanoString(phase.startTimestamp, now);
|
|
1561
|
+
const endNano = isoToNanoString(phase.endTimestamp, startNano);
|
|
1562
|
+
const toolCallNames = phase.toolCalls.map((tc) => tc.name).join(", ");
|
|
1563
|
+
traceShipper.createSpan({
|
|
1564
|
+
name: model,
|
|
1565
|
+
eventId,
|
|
1566
|
+
parent,
|
|
1567
|
+
startTimeUnixNano: startNano,
|
|
1568
|
+
endTimeUnixNano: endNano,
|
|
1569
|
+
attributes: [
|
|
1570
|
+
attrString("ai.operationId", "generateText"),
|
|
1571
|
+
attrString("gen_ai.response.model", model),
|
|
1572
|
+
attrString("gen_ai.system", "anthropic"),
|
|
1573
|
+
attrInt("gen_ai.usage.prompt_tokens", phase.inputTokens),
|
|
1574
|
+
attrInt("gen_ai.usage.completion_tokens", phase.outputTokens),
|
|
1575
|
+
...phase.cacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", phase.cacheReadTokens)] : [],
|
|
1576
|
+
attrString("ai.response.text", outputText),
|
|
1577
|
+
...toolCallNames ? [attrString("ai.toolCall.names", toolCallNames)] : [],
|
|
1578
|
+
...phase.hasThinking ? [attrString("ai.has_thinking", "true")] : [],
|
|
1579
|
+
attrInt("ai.llm_call.index", emittedPhaseIndex)
|
|
1580
|
+
]
|
|
1581
|
+
});
|
|
1582
|
+
emittedPhaseIndex++;
|
|
1583
|
+
}
|
|
1584
|
+
} else {
|
|
1585
|
+
const spanInputTokens = (_c = summary.lastTurnInputTokens) != null ? _c : summary.totalInputTokens;
|
|
1586
|
+
const spanOutputTokens = (_d = summary.lastTurnOutputTokens) != null ? _d : summary.totalOutputTokens;
|
|
1587
|
+
if (summary.model && (spanInputTokens > 0 || spanOutputTokens > 0)) {
|
|
1588
|
+
const now = nowUnixNanoString();
|
|
1589
|
+
traceShipper.createSpan({
|
|
1590
|
+
name: summary.model,
|
|
1591
|
+
eventId,
|
|
1592
|
+
parent,
|
|
1593
|
+
startTimeUnixNano: now,
|
|
1594
|
+
endTimeUnixNano: now,
|
|
1595
|
+
attributes: [
|
|
1596
|
+
attrString("ai.operationId", "generateText"),
|
|
1597
|
+
attrString("gen_ai.response.model", summary.model),
|
|
1598
|
+
attrString("gen_ai.system", "anthropic"),
|
|
1599
|
+
attrInt("gen_ai.usage.prompt_tokens", spanInputTokens),
|
|
1600
|
+
attrInt("gen_ai.usage.completion_tokens", spanOutputTokens),
|
|
1601
|
+
...summary.lastTurnCacheReadTokens != null && summary.lastTurnCacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", summary.lastTurnCacheReadTokens)] : []
|
|
1602
|
+
]
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1473
1605
|
}
|
|
1474
1606
|
return { summary, props };
|
|
1475
1607
|
} catch (e) {
|
|
1476
1608
|
return { summary: void 0, props: {} };
|
|
1477
1609
|
}
|
|
1478
1610
|
}
|
|
1479
|
-
|
|
1611
|
+
function normalizeAssistantText(value) {
|
|
1612
|
+
return value.trim().replace(/\r\n/g, "\n");
|
|
1613
|
+
}
|
|
1614
|
+
function buildStopOutput(summary, lastAssistantMessage) {
|
|
1615
|
+
const finalMessage = typeof lastAssistantMessage === "string" ? normalizeAssistantText(lastAssistantMessage) : void 0;
|
|
1616
|
+
if (!summary) return finalMessage;
|
|
1617
|
+
const transcriptBlocks = summary.lastTurnTextBlocks.map(normalizeAssistantText).filter(Boolean);
|
|
1618
|
+
if (finalMessage && !transcriptBlocks.includes(finalMessage)) {
|
|
1619
|
+
transcriptBlocks.push(finalMessage);
|
|
1620
|
+
}
|
|
1621
|
+
if (transcriptBlocks.length > 0) {
|
|
1622
|
+
return transcriptBlocks.join("\n\n");
|
|
1623
|
+
}
|
|
1624
|
+
return finalMessage;
|
|
1625
|
+
}
|
|
1626
|
+
async function handleStopOrFailure(payload, eventId, convoId, config, properties, eventShipper, traceShipper, extraProperties) {
|
|
1480
1627
|
const { summary, props: transcriptProps } = enrichFromTranscript(payload, eventId, traceShipper);
|
|
1481
1628
|
const instructions = gatherInstructions(payload.session_id);
|
|
1482
1629
|
const appendSysPrompt = readAppendSystemPrompt(payload.session_id);
|
|
1630
|
+
const output = buildStopOutput(summary, payload.last_assistant_message);
|
|
1483
1631
|
await eventShipper.patch(eventId, {
|
|
1484
1632
|
isPending: false,
|
|
1485
1633
|
userId: config.userId,
|
|
1486
|
-
convoId
|
|
1634
|
+
convoId,
|
|
1487
1635
|
eventName: config.eventName,
|
|
1488
|
-
output
|
|
1636
|
+
output,
|
|
1489
1637
|
...(summary == null ? void 0 : summary.model) ? { model: summary.model } : {},
|
|
1490
1638
|
properties: {
|
|
1491
1639
|
...properties,
|
|
@@ -1496,10 +1644,11 @@ async function handleStopOrFailure(payload, eventId, config, properties, eventSh
|
|
|
1496
1644
|
}
|
|
1497
1645
|
});
|
|
1498
1646
|
}
|
|
1499
|
-
async function handleSessionEnd(payload, eventId, config, properties, eventShipper) {
|
|
1647
|
+
async function handleSessionEnd(payload, eventId, convoId, config, properties, eventShipper) {
|
|
1500
1648
|
await eventShipper.patch(eventId, {
|
|
1501
1649
|
isPending: false,
|
|
1502
1650
|
userId: config.userId,
|
|
1651
|
+
convoId,
|
|
1503
1652
|
eventName: config.eventName,
|
|
1504
1653
|
properties: {
|
|
1505
1654
|
...properties,
|
package/dist/index.d.cts
CHANGED
|
@@ -244,6 +244,7 @@ interface RaindropConfig {
|
|
|
244
244
|
writeKey: string;
|
|
245
245
|
endpoint: string;
|
|
246
246
|
userId: string;
|
|
247
|
+
convoId?: string;
|
|
247
248
|
debug: boolean;
|
|
248
249
|
enabled: boolean;
|
|
249
250
|
eventName: string;
|
|
@@ -292,6 +293,7 @@ interface HookPayload {
|
|
|
292
293
|
}
|
|
293
294
|
interface MapperConfig {
|
|
294
295
|
userId: string;
|
|
296
|
+
convoId?: string;
|
|
295
297
|
debug: boolean;
|
|
296
298
|
eventName: string;
|
|
297
299
|
customProperties: Record<string, unknown>;
|
|
@@ -308,8 +310,37 @@ declare function mapHookToRaindrop(payload: HookPayload, config: MapperConfig, e
|
|
|
308
310
|
declare function extractAppendSystemPrompt(args: string[]): string | undefined;
|
|
309
311
|
|
|
310
312
|
declare const PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
311
|
-
declare const PACKAGE_VERSION = "0.0.
|
|
313
|
+
declare const PACKAGE_VERSION = "0.0.8";
|
|
312
314
|
|
|
315
|
+
/** A tool call extracted from an assistant message content block. */
|
|
316
|
+
interface LLMToolCall {
|
|
317
|
+
id?: string;
|
|
318
|
+
name: string;
|
|
319
|
+
input?: Record<string, unknown>;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* One LLM-call phase within a turn.
|
|
323
|
+
* A phase = one or more contiguous assistant messages before the next
|
|
324
|
+
* tool-result user entry resets the context.
|
|
325
|
+
*/
|
|
326
|
+
interface LLMCallPhase {
|
|
327
|
+
/** Concatenated assistant text blocks in this phase */
|
|
328
|
+
text: string;
|
|
329
|
+
/** Tool calls the model decided to make in this phase */
|
|
330
|
+
toolCalls: LLMToolCall[];
|
|
331
|
+
/** Model that produced this phase */
|
|
332
|
+
model?: string;
|
|
333
|
+
/** Token usage accumulated across assistant messages in this phase */
|
|
334
|
+
inputTokens: number;
|
|
335
|
+
outputTokens: number;
|
|
336
|
+
cacheReadTokens: number;
|
|
337
|
+
/** Whether a thinking block appeared in this phase */
|
|
338
|
+
hasThinking: boolean;
|
|
339
|
+
/** ISO timestamp of the first assistant message in the phase */
|
|
340
|
+
startTimestamp?: string;
|
|
341
|
+
/** ISO timestamp of the last assistant message in the phase */
|
|
342
|
+
endTimestamp?: string;
|
|
343
|
+
}
|
|
313
344
|
interface TranscriptSummary {
|
|
314
345
|
/** Aggregated token usage across all turns */
|
|
315
346
|
totalInputTokens: number;
|
|
@@ -338,6 +369,12 @@ interface TranscriptSummary {
|
|
|
338
369
|
gitBranch?: string;
|
|
339
370
|
/** Whether thinking/reasoning content was used */
|
|
340
371
|
hasThinking: boolean;
|
|
372
|
+
/** Ordered assistant text blocks for the latest top-level user turn */
|
|
373
|
+
lastTurnTextBlocks: string[];
|
|
374
|
+
/** Combined assistant text for the latest top-level user turn */
|
|
375
|
+
lastTurnFullOutput?: string;
|
|
376
|
+
/** Ordered LLM-call phases for the latest top-level user turn */
|
|
377
|
+
llmCallPhases: LLMCallPhase[];
|
|
341
378
|
}
|
|
342
379
|
/**
|
|
343
380
|
* Parse a Claude Code transcript JSONL file and extract a summary.
|
|
@@ -425,4 +462,4 @@ declare const TOOL_SCHEMA: {
|
|
|
425
462
|
};
|
|
426
463
|
declare function startMcpServer(): Promise<void>;
|
|
427
464
|
|
|
428
|
-
export { DEFAULT_CATEGORY_KEYS, EventShipper, type HookPayload, type LocalDebuggerResult, type MapperConfig, PACKAGE_NAME, PACKAGE_VERSION, type RaindropConfig, type SelfDiagnosticsConfig, type SelfDiagnosticsSignalDef, type SetupScope, TOOL_SCHEMA, TraceShipper, type TranscriptSummary, detectLocalDebugger, executeTool, extractAppendSystemPrompt, getConfigPath, loadConfig, mapHookToRaindrop, mirrorEventToLocalDebugger, normalizeSignals, parseTranscript, resolveCurrentEventId, resolveToolConfig, startMcpServer, transcriptToProperties, updateConfig };
|
|
465
|
+
export { DEFAULT_CATEGORY_KEYS, EventShipper, type HookPayload, type LLMCallPhase, type LLMToolCall, type LocalDebuggerResult, type MapperConfig, PACKAGE_NAME, PACKAGE_VERSION, type RaindropConfig, type SelfDiagnosticsConfig, type SelfDiagnosticsSignalDef, type SetupScope, TOOL_SCHEMA, TraceShipper, type TranscriptSummary, detectLocalDebugger, executeTool, extractAppendSystemPrompt, getConfigPath, loadConfig, mapHookToRaindrop, mirrorEventToLocalDebugger, normalizeSignals, parseTranscript, resolveCurrentEventId, resolveToolConfig, startMcpServer, transcriptToProperties, updateConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -244,6 +244,7 @@ interface RaindropConfig {
|
|
|
244
244
|
writeKey: string;
|
|
245
245
|
endpoint: string;
|
|
246
246
|
userId: string;
|
|
247
|
+
convoId?: string;
|
|
247
248
|
debug: boolean;
|
|
248
249
|
enabled: boolean;
|
|
249
250
|
eventName: string;
|
|
@@ -292,6 +293,7 @@ interface HookPayload {
|
|
|
292
293
|
}
|
|
293
294
|
interface MapperConfig {
|
|
294
295
|
userId: string;
|
|
296
|
+
convoId?: string;
|
|
295
297
|
debug: boolean;
|
|
296
298
|
eventName: string;
|
|
297
299
|
customProperties: Record<string, unknown>;
|
|
@@ -308,8 +310,37 @@ declare function mapHookToRaindrop(payload: HookPayload, config: MapperConfig, e
|
|
|
308
310
|
declare function extractAppendSystemPrompt(args: string[]): string | undefined;
|
|
309
311
|
|
|
310
312
|
declare const PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
311
|
-
declare const PACKAGE_VERSION = "0.0.
|
|
313
|
+
declare const PACKAGE_VERSION = "0.0.8";
|
|
312
314
|
|
|
315
|
+
/** A tool call extracted from an assistant message content block. */
|
|
316
|
+
interface LLMToolCall {
|
|
317
|
+
id?: string;
|
|
318
|
+
name: string;
|
|
319
|
+
input?: Record<string, unknown>;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* One LLM-call phase within a turn.
|
|
323
|
+
* A phase = one or more contiguous assistant messages before the next
|
|
324
|
+
* tool-result user entry resets the context.
|
|
325
|
+
*/
|
|
326
|
+
interface LLMCallPhase {
|
|
327
|
+
/** Concatenated assistant text blocks in this phase */
|
|
328
|
+
text: string;
|
|
329
|
+
/** Tool calls the model decided to make in this phase */
|
|
330
|
+
toolCalls: LLMToolCall[];
|
|
331
|
+
/** Model that produced this phase */
|
|
332
|
+
model?: string;
|
|
333
|
+
/** Token usage accumulated across assistant messages in this phase */
|
|
334
|
+
inputTokens: number;
|
|
335
|
+
outputTokens: number;
|
|
336
|
+
cacheReadTokens: number;
|
|
337
|
+
/** Whether a thinking block appeared in this phase */
|
|
338
|
+
hasThinking: boolean;
|
|
339
|
+
/** ISO timestamp of the first assistant message in the phase */
|
|
340
|
+
startTimestamp?: string;
|
|
341
|
+
/** ISO timestamp of the last assistant message in the phase */
|
|
342
|
+
endTimestamp?: string;
|
|
343
|
+
}
|
|
313
344
|
interface TranscriptSummary {
|
|
314
345
|
/** Aggregated token usage across all turns */
|
|
315
346
|
totalInputTokens: number;
|
|
@@ -338,6 +369,12 @@ interface TranscriptSummary {
|
|
|
338
369
|
gitBranch?: string;
|
|
339
370
|
/** Whether thinking/reasoning content was used */
|
|
340
371
|
hasThinking: boolean;
|
|
372
|
+
/** Ordered assistant text blocks for the latest top-level user turn */
|
|
373
|
+
lastTurnTextBlocks: string[];
|
|
374
|
+
/** Combined assistant text for the latest top-level user turn */
|
|
375
|
+
lastTurnFullOutput?: string;
|
|
376
|
+
/** Ordered LLM-call phases for the latest top-level user turn */
|
|
377
|
+
llmCallPhases: LLMCallPhase[];
|
|
341
378
|
}
|
|
342
379
|
/**
|
|
343
380
|
* Parse a Claude Code transcript JSONL file and extract a summary.
|
|
@@ -425,4 +462,4 @@ declare const TOOL_SCHEMA: {
|
|
|
425
462
|
};
|
|
426
463
|
declare function startMcpServer(): Promise<void>;
|
|
427
464
|
|
|
428
|
-
export { DEFAULT_CATEGORY_KEYS, EventShipper, type HookPayload, type LocalDebuggerResult, type MapperConfig, PACKAGE_NAME, PACKAGE_VERSION, type RaindropConfig, type SelfDiagnosticsConfig, type SelfDiagnosticsSignalDef, type SetupScope, TOOL_SCHEMA, TraceShipper, type TranscriptSummary, detectLocalDebugger, executeTool, extractAppendSystemPrompt, getConfigPath, loadConfig, mapHookToRaindrop, mirrorEventToLocalDebugger, normalizeSignals, parseTranscript, resolveCurrentEventId, resolveToolConfig, startMcpServer, transcriptToProperties, updateConfig };
|
|
465
|
+
export { DEFAULT_CATEGORY_KEYS, EventShipper, type HookPayload, type LLMCallPhase, type LLMToolCall, type LocalDebuggerResult, type MapperConfig, PACKAGE_NAME, PACKAGE_VERSION, type RaindropConfig, type SelfDiagnosticsConfig, type SelfDiagnosticsSignalDef, type SetupScope, TOOL_SCHEMA, TraceShipper, type TranscriptSummary, detectLocalDebugger, executeTool, extractAppendSystemPrompt, getConfigPath, loadConfig, mapHookToRaindrop, mirrorEventToLocalDebugger, normalizeSignals, parseTranscript, resolveCurrentEventId, resolveToolConfig, startMcpServer, transcriptToProperties, updateConfig };
|
package/dist/index.js
CHANGED
|
@@ -645,7 +645,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
|
|
|
645
645
|
|
|
646
646
|
// src/package-info.ts
|
|
647
647
|
var PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
648
|
-
var PACKAGE_VERSION = "0.0.
|
|
648
|
+
var PACKAGE_VERSION = "0.0.8";
|
|
649
649
|
|
|
650
650
|
// src/shipper.ts
|
|
651
651
|
var EventShipper2 = class extends EventShipper {
|
|
@@ -688,7 +688,7 @@ import { homedir, userInfo } from "os";
|
|
|
688
688
|
import { dirname, join } from "path";
|
|
689
689
|
var CONFIG_PATH = join(homedir(), ".config", "raindrop", "config.json");
|
|
690
690
|
function loadConfig() {
|
|
691
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
691
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
692
692
|
let file = {};
|
|
693
693
|
try {
|
|
694
694
|
if (existsSync(CONFIG_PATH)) {
|
|
@@ -729,9 +729,10 @@ function loadConfig() {
|
|
|
729
729
|
writeKey: (_c = (_b = process.env["RAINDROP_WRITE_KEY"]) != null ? _b : file.write_key) != null ? _c : "",
|
|
730
730
|
endpoint: (_e = (_d = process.env["RAINDROP_API_URL"]) != null ? _d : file.api_url) != null ? _e : "https://api.raindrop.ai/v1",
|
|
731
731
|
userId: (_g = (_f = process.env["RAINDROP_USER_ID"]) != null ? _f : file.user_id) != null ? _g : systemUser,
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
732
|
+
convoId: ((_h = process.env["RAINDROP_CONVO_ID"]) == null ? void 0 : _h.trim()) || void 0,
|
|
733
|
+
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_i = file.debug) != null ? _i : false,
|
|
734
|
+
enabled: (_j = file.enabled) != null ? _j : true,
|
|
735
|
+
eventName: (_l = (_k = process.env["RAINDROP_EVENT_NAME"]) != null ? _k : file.event_name) != null ? _l : "claude_code_session",
|
|
735
736
|
customProperties,
|
|
736
737
|
selfDiagnostics
|
|
737
738
|
};
|
|
@@ -761,9 +762,47 @@ import { join as join2 } from "path";
|
|
|
761
762
|
|
|
762
763
|
// src/transcript.ts
|
|
763
764
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
765
|
+
function isToolResultContentBlock(value) {
|
|
766
|
+
return Boolean(
|
|
767
|
+
value && typeof value === "object" && "type" in value && value["type"] === "tool_result"
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
function isTopLevelUserPrompt(entry) {
|
|
771
|
+
var _a;
|
|
772
|
+
if (entry.type !== "user") return false;
|
|
773
|
+
const content = (_a = entry.message) == null ? void 0 : _a.content;
|
|
774
|
+
if (typeof content === "string") return true;
|
|
775
|
+
if (!Array.isArray(content)) return false;
|
|
776
|
+
return content.some((block) => !isToolResultContentBlock(block));
|
|
777
|
+
}
|
|
764
778
|
function parseTranscript(transcriptPath) {
|
|
765
|
-
var _a, _b, _c, _d, _e;
|
|
779
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
766
780
|
try {
|
|
781
|
+
let finalizePhase2 = function() {
|
|
782
|
+
if (activePhase && (activePhase.text || activePhase.toolCalls.length > 0)) {
|
|
783
|
+
currentPhases.push(activePhase);
|
|
784
|
+
}
|
|
785
|
+
activePhase = void 0;
|
|
786
|
+
}, ensurePhase2 = function() {
|
|
787
|
+
if (!activePhase) {
|
|
788
|
+
activePhase = {
|
|
789
|
+
text: "",
|
|
790
|
+
toolCalls: [],
|
|
791
|
+
inputTokens: 0,
|
|
792
|
+
outputTokens: 0,
|
|
793
|
+
cacheReadTokens: 0,
|
|
794
|
+
hasThinking: false
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
return activePhase;
|
|
798
|
+
}, isToolResultUserEntry2 = function(entry) {
|
|
799
|
+
var _a2;
|
|
800
|
+
if (entry.type !== "user") return false;
|
|
801
|
+
const content2 = (_a2 = entry.message) == null ? void 0 : _a2.content;
|
|
802
|
+
if (!Array.isArray(content2)) return false;
|
|
803
|
+
return content2.length > 0 && content2.every((b) => isToolResultContentBlock(b));
|
|
804
|
+
};
|
|
805
|
+
var finalizePhase = finalizePhase2, ensurePhase = ensurePhase2, isToolResultUserEntry = isToolResultUserEntry2;
|
|
767
806
|
if (!existsSync2(transcriptPath)) return void 0;
|
|
768
807
|
const content = readFileSync2(transcriptPath, "utf-8");
|
|
769
808
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
@@ -774,10 +813,15 @@ function parseTranscript(transcriptPath) {
|
|
|
774
813
|
totalCacheCreationTokens: 0,
|
|
775
814
|
turnCount: 0,
|
|
776
815
|
toolsUsed: [],
|
|
777
|
-
hasThinking: false
|
|
816
|
+
hasThinking: false,
|
|
817
|
+
lastTurnTextBlocks: [],
|
|
818
|
+
llmCallPhases: []
|
|
778
819
|
};
|
|
779
820
|
const toolNames = /* @__PURE__ */ new Set();
|
|
780
821
|
let lastUsage;
|
|
822
|
+
let currentTurnTextBlocks = [];
|
|
823
|
+
let currentPhases = [];
|
|
824
|
+
let activePhase;
|
|
781
825
|
for (const line of lines) {
|
|
782
826
|
let entry;
|
|
783
827
|
try {
|
|
@@ -800,6 +844,15 @@ function parseTranscript(transcriptPath) {
|
|
|
800
844
|
}
|
|
801
845
|
continue;
|
|
802
846
|
}
|
|
847
|
+
if (isTopLevelUserPrompt(entry)) {
|
|
848
|
+
currentTurnTextBlocks = [];
|
|
849
|
+
finalizePhase2();
|
|
850
|
+
currentPhases = [];
|
|
851
|
+
}
|
|
852
|
+
if (isToolResultUserEntry2(entry)) {
|
|
853
|
+
finalizePhase2();
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
803
856
|
if (entry.type !== "assistant") continue;
|
|
804
857
|
const msg = entry.message;
|
|
805
858
|
if (!msg) continue;
|
|
@@ -829,23 +882,55 @@ function parseTranscript(transcriptPath) {
|
|
|
829
882
|
}
|
|
830
883
|
lastUsage = u;
|
|
831
884
|
}
|
|
885
|
+
const phase = ensurePhase2();
|
|
886
|
+
const entryTimestamp = typeof entry["timestamp"] === "string" ? entry["timestamp"] : void 0;
|
|
887
|
+
if (entryTimestamp) {
|
|
888
|
+
if (!phase.startTimestamp) phase.startTimestamp = entryTimestamp;
|
|
889
|
+
phase.endTimestamp = entryTimestamp;
|
|
890
|
+
}
|
|
891
|
+
if (msg.model) phase.model = msg.model;
|
|
892
|
+
if (msg.usage) {
|
|
893
|
+
phase.inputTokens += (_f = msg.usage.input_tokens) != null ? _f : 0;
|
|
894
|
+
phase.outputTokens += (_g = msg.usage.output_tokens) != null ? _g : 0;
|
|
895
|
+
phase.cacheReadTokens += (_h = msg.usage.cache_read_input_tokens) != null ? _h : 0;
|
|
896
|
+
}
|
|
832
897
|
if (Array.isArray(msg.content)) {
|
|
833
898
|
for (const block of msg.content) {
|
|
834
899
|
if (block.type === "tool_use" && block.name) {
|
|
835
900
|
toolNames.add(block.name);
|
|
901
|
+
phase.toolCalls.push({
|
|
902
|
+
id: block.id,
|
|
903
|
+
name: block.name,
|
|
904
|
+
input: block.input
|
|
905
|
+
});
|
|
836
906
|
}
|
|
837
907
|
if (block.type === "thinking") {
|
|
838
908
|
summary.hasThinking = true;
|
|
909
|
+
phase.hasThinking = true;
|
|
910
|
+
}
|
|
911
|
+
if (block.type === "text" && typeof block.text === "string" && block.text.trim()) {
|
|
912
|
+
currentTurnTextBlocks.push(block.text);
|
|
913
|
+
if (phase.text) {
|
|
914
|
+
phase.text += "\n" + block.text;
|
|
915
|
+
} else {
|
|
916
|
+
phase.text = block.text;
|
|
917
|
+
}
|
|
839
918
|
}
|
|
840
919
|
}
|
|
920
|
+
summary.lastTurnTextBlocks = [...currentTurnTextBlocks];
|
|
841
921
|
}
|
|
842
922
|
}
|
|
923
|
+
finalizePhase2();
|
|
843
924
|
if (lastUsage) {
|
|
844
925
|
summary.lastTurnInputTokens = lastUsage.input_tokens;
|
|
845
926
|
summary.lastTurnOutputTokens = lastUsage.output_tokens;
|
|
846
927
|
summary.lastTurnCacheReadTokens = lastUsage.cache_read_input_tokens;
|
|
847
928
|
}
|
|
848
929
|
summary.toolsUsed = [...toolNames].sort();
|
|
930
|
+
if (summary.lastTurnTextBlocks.length > 0) {
|
|
931
|
+
summary.lastTurnFullOutput = summary.lastTurnTextBlocks.join("\n\n");
|
|
932
|
+
}
|
|
933
|
+
summary.llmCallPhases = currentPhases;
|
|
849
934
|
return summary;
|
|
850
935
|
} catch (e) {
|
|
851
936
|
return void 0;
|
|
@@ -994,12 +1079,13 @@ function readTimestamp(key) {
|
|
|
994
1079
|
return Number.isFinite(ts) ? ts : void 0;
|
|
995
1080
|
}
|
|
996
1081
|
async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
997
|
-
var _a;
|
|
998
|
-
const convoId = payload.session_id;
|
|
1082
|
+
var _a, _b;
|
|
1083
|
+
const convoId = (_a = config.convoId) != null ? _a : payload.session_id;
|
|
999
1084
|
const baseProperties = {
|
|
1000
1085
|
...config.customProperties,
|
|
1001
1086
|
cwd: payload.cwd,
|
|
1002
1087
|
permission_mode: payload.permission_mode,
|
|
1088
|
+
claude_code_session_id: payload.session_id,
|
|
1003
1089
|
plugin_version: PACKAGE_VERSION,
|
|
1004
1090
|
sdk: PACKAGE_NAME,
|
|
1005
1091
|
sdk_version: PACKAGE_VERSION,
|
|
@@ -1008,7 +1094,7 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
1008
1094
|
};
|
|
1009
1095
|
switch (payload.hook_event_name) {
|
|
1010
1096
|
case "SessionStart":
|
|
1011
|
-
writeState(`model_${payload.session_id}`, (
|
|
1097
|
+
writeState(`model_${payload.session_id}`, (_b = payload.model) != null ? _b : "");
|
|
1012
1098
|
tryCaptureAppendSystemPrompt(payload.session_id);
|
|
1013
1099
|
break;
|
|
1014
1100
|
case "UserPromptSubmit":
|
|
@@ -1024,10 +1110,10 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
1024
1110
|
handlePostToolUseFailure(payload, getCurrentEventId(payload.session_id), traceShipper);
|
|
1025
1111
|
break;
|
|
1026
1112
|
case "Stop":
|
|
1027
|
-
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper);
|
|
1113
|
+
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), convoId, config, baseProperties, eventShipper, traceShipper);
|
|
1028
1114
|
break;
|
|
1029
1115
|
case "StopFailure":
|
|
1030
|
-
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper, traceShipper, {
|
|
1116
|
+
await handleStopOrFailure(payload, getCurrentEventId(payload.session_id), convoId, config, baseProperties, eventShipper, traceShipper, {
|
|
1031
1117
|
error: payload.error,
|
|
1032
1118
|
error_details: payload.error_details
|
|
1033
1119
|
});
|
|
@@ -1045,10 +1131,10 @@ async function mapHookToRaindrop(payload, config, eventShipper, traceShipper) {
|
|
|
1045
1131
|
handleInstructionsLoaded(payload);
|
|
1046
1132
|
break;
|
|
1047
1133
|
case "PostCompact":
|
|
1048
|
-
await handlePostCompact(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
|
|
1134
|
+
await handlePostCompact(payload, getCurrentEventId(payload.session_id), convoId, config, baseProperties, eventShipper);
|
|
1049
1135
|
break;
|
|
1050
1136
|
case "SessionEnd":
|
|
1051
|
-
await handleSessionEnd(payload, getCurrentEventId(payload.session_id), config, baseProperties, eventShipper);
|
|
1137
|
+
await handleSessionEnd(payload, getCurrentEventId(payload.session_id), convoId, config, baseProperties, eventShipper);
|
|
1052
1138
|
deleteState(turnEventKey(payload.session_id));
|
|
1053
1139
|
deleteState(rootSpanKey(payload.session_id));
|
|
1054
1140
|
deleteState(`model_${payload.session_id}`);
|
|
@@ -1198,11 +1284,11 @@ function handlePermissionDenied(payload, eventId, traceShipper) {
|
|
|
1198
1284
|
}
|
|
1199
1285
|
});
|
|
1200
1286
|
}
|
|
1201
|
-
async function handlePostCompact(payload, eventId, config, properties, eventShipper) {
|
|
1287
|
+
async function handlePostCompact(payload, eventId, convoId, config, properties, eventShipper) {
|
|
1202
1288
|
await eventShipper.patch(eventId, {
|
|
1203
1289
|
isPending: true,
|
|
1204
1290
|
userId: config.userId,
|
|
1205
|
-
convoId
|
|
1291
|
+
convoId,
|
|
1206
1292
|
eventName: config.eventName,
|
|
1207
1293
|
properties: {
|
|
1208
1294
|
...properties,
|
|
@@ -1396,8 +1482,18 @@ function readAppendSystemPrompt(sessionId) {
|
|
|
1396
1482
|
return void 0;
|
|
1397
1483
|
}
|
|
1398
1484
|
}
|
|
1485
|
+
function isoToNanoString(iso, fallback) {
|
|
1486
|
+
if (!iso) return fallback;
|
|
1487
|
+
try {
|
|
1488
|
+
const ms = new Date(iso).getTime();
|
|
1489
|
+
if (!Number.isFinite(ms)) return fallback;
|
|
1490
|
+
return String(ms) + "000000";
|
|
1491
|
+
} catch (e) {
|
|
1492
|
+
return fallback;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1399
1495
|
function enrichFromTranscript(payload, eventId, traceShipper) {
|
|
1400
|
-
var _a, _b;
|
|
1496
|
+
var _a, _b, _c, _d;
|
|
1401
1497
|
if (!payload.transcript_path) {
|
|
1402
1498
|
return { summary: void 0, props: {} };
|
|
1403
1499
|
}
|
|
@@ -1405,42 +1501,94 @@ function enrichFromTranscript(payload, eventId, traceShipper) {
|
|
|
1405
1501
|
const summary = parseTranscript(payload.transcript_path);
|
|
1406
1502
|
if (!summary) return { summary: void 0, props: {} };
|
|
1407
1503
|
const props = transcriptToProperties(summary);
|
|
1408
|
-
const
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1504
|
+
const parent = getParentContext(payload);
|
|
1505
|
+
if (summary.llmCallPhases.length > 0) {
|
|
1506
|
+
let emittedPhaseIndex = 0;
|
|
1507
|
+
for (let i = 0; i < summary.llmCallPhases.length; i++) {
|
|
1508
|
+
const phase = summary.llmCallPhases[i];
|
|
1509
|
+
const outputText = phase.text.trim();
|
|
1510
|
+
if (!outputText) {
|
|
1511
|
+
continue;
|
|
1512
|
+
}
|
|
1513
|
+
const model = (_b = (_a = phase.model) != null ? _a : summary.model) != null ? _b : "claude";
|
|
1514
|
+
const now = nowUnixNanoString();
|
|
1515
|
+
const startNano = isoToNanoString(phase.startTimestamp, now);
|
|
1516
|
+
const endNano = isoToNanoString(phase.endTimestamp, startNano);
|
|
1517
|
+
const toolCallNames = phase.toolCalls.map((tc) => tc.name).join(", ");
|
|
1518
|
+
traceShipper.createSpan({
|
|
1519
|
+
name: model,
|
|
1520
|
+
eventId,
|
|
1521
|
+
parent,
|
|
1522
|
+
startTimeUnixNano: startNano,
|
|
1523
|
+
endTimeUnixNano: endNano,
|
|
1524
|
+
attributes: [
|
|
1525
|
+
attrString("ai.operationId", "generateText"),
|
|
1526
|
+
attrString("gen_ai.response.model", model),
|
|
1527
|
+
attrString("gen_ai.system", "anthropic"),
|
|
1528
|
+
attrInt("gen_ai.usage.prompt_tokens", phase.inputTokens),
|
|
1529
|
+
attrInt("gen_ai.usage.completion_tokens", phase.outputTokens),
|
|
1530
|
+
...phase.cacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", phase.cacheReadTokens)] : [],
|
|
1531
|
+
attrString("ai.response.text", outputText),
|
|
1532
|
+
...toolCallNames ? [attrString("ai.toolCall.names", toolCallNames)] : [],
|
|
1533
|
+
...phase.hasThinking ? [attrString("ai.has_thinking", "true")] : [],
|
|
1534
|
+
attrInt("ai.llm_call.index", emittedPhaseIndex)
|
|
1535
|
+
]
|
|
1536
|
+
});
|
|
1537
|
+
emittedPhaseIndex++;
|
|
1538
|
+
}
|
|
1539
|
+
} else {
|
|
1540
|
+
const spanInputTokens = (_c = summary.lastTurnInputTokens) != null ? _c : summary.totalInputTokens;
|
|
1541
|
+
const spanOutputTokens = (_d = summary.lastTurnOutputTokens) != null ? _d : summary.totalOutputTokens;
|
|
1542
|
+
if (summary.model && (spanInputTokens > 0 || spanOutputTokens > 0)) {
|
|
1543
|
+
const now = nowUnixNanoString();
|
|
1544
|
+
traceShipper.createSpan({
|
|
1545
|
+
name: summary.model,
|
|
1546
|
+
eventId,
|
|
1547
|
+
parent,
|
|
1548
|
+
startTimeUnixNano: now,
|
|
1549
|
+
endTimeUnixNano: now,
|
|
1550
|
+
attributes: [
|
|
1551
|
+
attrString("ai.operationId", "generateText"),
|
|
1552
|
+
attrString("gen_ai.response.model", summary.model),
|
|
1553
|
+
attrString("gen_ai.system", "anthropic"),
|
|
1554
|
+
attrInt("gen_ai.usage.prompt_tokens", spanInputTokens),
|
|
1555
|
+
attrInt("gen_ai.usage.completion_tokens", spanOutputTokens),
|
|
1556
|
+
...summary.lastTurnCacheReadTokens != null && summary.lastTurnCacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", summary.lastTurnCacheReadTokens)] : []
|
|
1557
|
+
]
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1428
1560
|
}
|
|
1429
1561
|
return { summary, props };
|
|
1430
1562
|
} catch (e) {
|
|
1431
1563
|
return { summary: void 0, props: {} };
|
|
1432
1564
|
}
|
|
1433
1565
|
}
|
|
1434
|
-
|
|
1566
|
+
function normalizeAssistantText(value) {
|
|
1567
|
+
return value.trim().replace(/\r\n/g, "\n");
|
|
1568
|
+
}
|
|
1569
|
+
function buildStopOutput(summary, lastAssistantMessage) {
|
|
1570
|
+
const finalMessage = typeof lastAssistantMessage === "string" ? normalizeAssistantText(lastAssistantMessage) : void 0;
|
|
1571
|
+
if (!summary) return finalMessage;
|
|
1572
|
+
const transcriptBlocks = summary.lastTurnTextBlocks.map(normalizeAssistantText).filter(Boolean);
|
|
1573
|
+
if (finalMessage && !transcriptBlocks.includes(finalMessage)) {
|
|
1574
|
+
transcriptBlocks.push(finalMessage);
|
|
1575
|
+
}
|
|
1576
|
+
if (transcriptBlocks.length > 0) {
|
|
1577
|
+
return transcriptBlocks.join("\n\n");
|
|
1578
|
+
}
|
|
1579
|
+
return finalMessage;
|
|
1580
|
+
}
|
|
1581
|
+
async function handleStopOrFailure(payload, eventId, convoId, config, properties, eventShipper, traceShipper, extraProperties) {
|
|
1435
1582
|
const { summary, props: transcriptProps } = enrichFromTranscript(payload, eventId, traceShipper);
|
|
1436
1583
|
const instructions = gatherInstructions(payload.session_id);
|
|
1437
1584
|
const appendSysPrompt = readAppendSystemPrompt(payload.session_id);
|
|
1585
|
+
const output = buildStopOutput(summary, payload.last_assistant_message);
|
|
1438
1586
|
await eventShipper.patch(eventId, {
|
|
1439
1587
|
isPending: false,
|
|
1440
1588
|
userId: config.userId,
|
|
1441
|
-
convoId
|
|
1589
|
+
convoId,
|
|
1442
1590
|
eventName: config.eventName,
|
|
1443
|
-
output
|
|
1591
|
+
output,
|
|
1444
1592
|
...(summary == null ? void 0 : summary.model) ? { model: summary.model } : {},
|
|
1445
1593
|
properties: {
|
|
1446
1594
|
...properties,
|
|
@@ -1451,10 +1599,11 @@ async function handleStopOrFailure(payload, eventId, config, properties, eventSh
|
|
|
1451
1599
|
}
|
|
1452
1600
|
});
|
|
1453
1601
|
}
|
|
1454
|
-
async function handleSessionEnd(payload, eventId, config, properties, eventShipper) {
|
|
1602
|
+
async function handleSessionEnd(payload, eventId, convoId, config, properties, eventShipper) {
|
|
1455
1603
|
await eventShipper.patch(eventId, {
|
|
1456
1604
|
isPending: false,
|
|
1457
1605
|
userId: config.userId,
|
|
1606
|
+
convoId,
|
|
1458
1607
|
eventName: config.eventName,
|
|
1459
1608
|
properties: {
|
|
1460
1609
|
...properties,
|
package/package.json
CHANGED