@raindrop-ai/claude-code 0.0.7 → 0.0.8
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 +171 -25
- package/dist/index.cjs +171 -25
- package/dist/index.d.cts +37 -2
- package/dist/index.d.ts +37 -2
- package/dist/index.js +171 -25
- 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;
|
|
@@ -1328,8 +1412,18 @@ function readAppendSystemPrompt(sessionId) {
|
|
|
1328
1412
|
return void 0;
|
|
1329
1413
|
}
|
|
1330
1414
|
}
|
|
1415
|
+
function isoToNanoString(iso, fallback) {
|
|
1416
|
+
if (!iso) return fallback;
|
|
1417
|
+
try {
|
|
1418
|
+
const ms = new Date(iso).getTime();
|
|
1419
|
+
if (!Number.isFinite(ms)) return fallback;
|
|
1420
|
+
return String(ms) + "000000";
|
|
1421
|
+
} catch (e) {
|
|
1422
|
+
return fallback;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1331
1425
|
function enrichFromTranscript(payload, eventId, traceShipper) {
|
|
1332
|
-
var _a, _b;
|
|
1426
|
+
var _a, _b, _c, _d;
|
|
1333
1427
|
if (!payload.transcript_path) {
|
|
1334
1428
|
return { summary: void 0, props: {} };
|
|
1335
1429
|
}
|
|
@@ -1337,42 +1431,94 @@ function enrichFromTranscript(payload, eventId, traceShipper) {
|
|
|
1337
1431
|
const summary = parseTranscript(payload.transcript_path);
|
|
1338
1432
|
if (!summary) return { summary: void 0, props: {} };
|
|
1339
1433
|
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
|
-
|
|
1434
|
+
const parent = getParentContext(payload);
|
|
1435
|
+
if (summary.llmCallPhases.length > 0) {
|
|
1436
|
+
let emittedPhaseIndex = 0;
|
|
1437
|
+
for (let i = 0; i < summary.llmCallPhases.length; i++) {
|
|
1438
|
+
const phase = summary.llmCallPhases[i];
|
|
1439
|
+
const outputText = phase.text.trim();
|
|
1440
|
+
if (!outputText) {
|
|
1441
|
+
continue;
|
|
1442
|
+
}
|
|
1443
|
+
const model = (_b = (_a = phase.model) != null ? _a : summary.model) != null ? _b : "claude";
|
|
1444
|
+
const now = nowUnixNanoString();
|
|
1445
|
+
const startNano = isoToNanoString(phase.startTimestamp, now);
|
|
1446
|
+
const endNano = isoToNanoString(phase.endTimestamp, startNano);
|
|
1447
|
+
const toolCallNames = phase.toolCalls.map((tc) => tc.name).join(", ");
|
|
1448
|
+
traceShipper.createSpan({
|
|
1449
|
+
name: model,
|
|
1450
|
+
eventId,
|
|
1451
|
+
parent,
|
|
1452
|
+
startTimeUnixNano: startNano,
|
|
1453
|
+
endTimeUnixNano: endNano,
|
|
1454
|
+
attributes: [
|
|
1455
|
+
attrString("ai.operationId", "generateText"),
|
|
1456
|
+
attrString("gen_ai.response.model", model),
|
|
1457
|
+
attrString("gen_ai.system", "anthropic"),
|
|
1458
|
+
attrInt("gen_ai.usage.prompt_tokens", phase.inputTokens),
|
|
1459
|
+
attrInt("gen_ai.usage.completion_tokens", phase.outputTokens),
|
|
1460
|
+
...phase.cacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", phase.cacheReadTokens)] : [],
|
|
1461
|
+
attrString("ai.response.text", outputText),
|
|
1462
|
+
...toolCallNames ? [attrString("ai.toolCall.names", toolCallNames)] : [],
|
|
1463
|
+
...phase.hasThinking ? [attrString("ai.has_thinking", "true")] : [],
|
|
1464
|
+
attrInt("ai.llm_call.index", emittedPhaseIndex)
|
|
1465
|
+
]
|
|
1466
|
+
});
|
|
1467
|
+
emittedPhaseIndex++;
|
|
1468
|
+
}
|
|
1469
|
+
} else {
|
|
1470
|
+
const spanInputTokens = (_c = summary.lastTurnInputTokens) != null ? _c : summary.totalInputTokens;
|
|
1471
|
+
const spanOutputTokens = (_d = summary.lastTurnOutputTokens) != null ? _d : summary.totalOutputTokens;
|
|
1472
|
+
if (summary.model && (spanInputTokens > 0 || spanOutputTokens > 0)) {
|
|
1473
|
+
const now = nowUnixNanoString();
|
|
1474
|
+
traceShipper.createSpan({
|
|
1475
|
+
name: summary.model,
|
|
1476
|
+
eventId,
|
|
1477
|
+
parent,
|
|
1478
|
+
startTimeUnixNano: now,
|
|
1479
|
+
endTimeUnixNano: now,
|
|
1480
|
+
attributes: [
|
|
1481
|
+
attrString("ai.operationId", "generateText"),
|
|
1482
|
+
attrString("gen_ai.response.model", summary.model),
|
|
1483
|
+
attrString("gen_ai.system", "anthropic"),
|
|
1484
|
+
attrInt("gen_ai.usage.prompt_tokens", spanInputTokens),
|
|
1485
|
+
attrInt("gen_ai.usage.completion_tokens", spanOutputTokens),
|
|
1486
|
+
...summary.lastTurnCacheReadTokens != null && summary.lastTurnCacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", summary.lastTurnCacheReadTokens)] : []
|
|
1487
|
+
]
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1360
1490
|
}
|
|
1361
1491
|
return { summary, props };
|
|
1362
1492
|
} catch (e) {
|
|
1363
1493
|
return { summary: void 0, props: {} };
|
|
1364
1494
|
}
|
|
1365
1495
|
}
|
|
1496
|
+
function normalizeAssistantText(value) {
|
|
1497
|
+
return value.trim().replace(/\r\n/g, "\n");
|
|
1498
|
+
}
|
|
1499
|
+
function buildStopOutput(summary, lastAssistantMessage) {
|
|
1500
|
+
const finalMessage = typeof lastAssistantMessage === "string" ? normalizeAssistantText(lastAssistantMessage) : void 0;
|
|
1501
|
+
if (!summary) return finalMessage;
|
|
1502
|
+
const transcriptBlocks = summary.lastTurnTextBlocks.map(normalizeAssistantText).filter(Boolean);
|
|
1503
|
+
if (finalMessage && !transcriptBlocks.includes(finalMessage)) {
|
|
1504
|
+
transcriptBlocks.push(finalMessage);
|
|
1505
|
+
}
|
|
1506
|
+
if (transcriptBlocks.length > 0) {
|
|
1507
|
+
return transcriptBlocks.join("\n\n");
|
|
1508
|
+
}
|
|
1509
|
+
return finalMessage;
|
|
1510
|
+
}
|
|
1366
1511
|
async function handleStopOrFailure(payload, eventId, config, properties, eventShipper, traceShipper, extraProperties) {
|
|
1367
1512
|
const { summary, props: transcriptProps } = enrichFromTranscript(payload, eventId, traceShipper);
|
|
1368
1513
|
const instructions = gatherInstructions(payload.session_id);
|
|
1369
1514
|
const appendSysPrompt = readAppendSystemPrompt(payload.session_id);
|
|
1515
|
+
const output = buildStopOutput(summary, payload.last_assistant_message);
|
|
1370
1516
|
await eventShipper.patch(eventId, {
|
|
1371
1517
|
isPending: false,
|
|
1372
1518
|
userId: config.userId,
|
|
1373
1519
|
convoId: payload.session_id,
|
|
1374
1520
|
eventName: config.eventName,
|
|
1375
|
-
output
|
|
1521
|
+
output,
|
|
1376
1522
|
...(summary == null ? void 0 : summary.model) ? { model: summary.model } : {},
|
|
1377
1523
|
properties: {
|
|
1378
1524
|
...properties,
|
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 {
|
|
@@ -806,9 +806,47 @@ var import_node_path2 = require("path");
|
|
|
806
806
|
|
|
807
807
|
// src/transcript.ts
|
|
808
808
|
var import_node_fs2 = require("fs");
|
|
809
|
+
function isToolResultContentBlock(value) {
|
|
810
|
+
return Boolean(
|
|
811
|
+
value && typeof value === "object" && "type" in value && value["type"] === "tool_result"
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
function isTopLevelUserPrompt(entry) {
|
|
815
|
+
var _a;
|
|
816
|
+
if (entry.type !== "user") return false;
|
|
817
|
+
const content = (_a = entry.message) == null ? void 0 : _a.content;
|
|
818
|
+
if (typeof content === "string") return true;
|
|
819
|
+
if (!Array.isArray(content)) return false;
|
|
820
|
+
return content.some((block) => !isToolResultContentBlock(block));
|
|
821
|
+
}
|
|
809
822
|
function parseTranscript(transcriptPath) {
|
|
810
|
-
var _a, _b, _c, _d, _e;
|
|
823
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
811
824
|
try {
|
|
825
|
+
let finalizePhase2 = function() {
|
|
826
|
+
if (activePhase && (activePhase.text || activePhase.toolCalls.length > 0)) {
|
|
827
|
+
currentPhases.push(activePhase);
|
|
828
|
+
}
|
|
829
|
+
activePhase = void 0;
|
|
830
|
+
}, ensurePhase2 = function() {
|
|
831
|
+
if (!activePhase) {
|
|
832
|
+
activePhase = {
|
|
833
|
+
text: "",
|
|
834
|
+
toolCalls: [],
|
|
835
|
+
inputTokens: 0,
|
|
836
|
+
outputTokens: 0,
|
|
837
|
+
cacheReadTokens: 0,
|
|
838
|
+
hasThinking: false
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
return activePhase;
|
|
842
|
+
}, isToolResultUserEntry2 = function(entry) {
|
|
843
|
+
var _a2;
|
|
844
|
+
if (entry.type !== "user") return false;
|
|
845
|
+
const content2 = (_a2 = entry.message) == null ? void 0 : _a2.content;
|
|
846
|
+
if (!Array.isArray(content2)) return false;
|
|
847
|
+
return content2.length > 0 && content2.every((b) => isToolResultContentBlock(b));
|
|
848
|
+
};
|
|
849
|
+
var finalizePhase = finalizePhase2, ensurePhase = ensurePhase2, isToolResultUserEntry = isToolResultUserEntry2;
|
|
812
850
|
if (!(0, import_node_fs2.existsSync)(transcriptPath)) return void 0;
|
|
813
851
|
const content = (0, import_node_fs2.readFileSync)(transcriptPath, "utf-8");
|
|
814
852
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
@@ -819,10 +857,15 @@ function parseTranscript(transcriptPath) {
|
|
|
819
857
|
totalCacheCreationTokens: 0,
|
|
820
858
|
turnCount: 0,
|
|
821
859
|
toolsUsed: [],
|
|
822
|
-
hasThinking: false
|
|
860
|
+
hasThinking: false,
|
|
861
|
+
lastTurnTextBlocks: [],
|
|
862
|
+
llmCallPhases: []
|
|
823
863
|
};
|
|
824
864
|
const toolNames = /* @__PURE__ */ new Set();
|
|
825
865
|
let lastUsage;
|
|
866
|
+
let currentTurnTextBlocks = [];
|
|
867
|
+
let currentPhases = [];
|
|
868
|
+
let activePhase;
|
|
826
869
|
for (const line of lines) {
|
|
827
870
|
let entry;
|
|
828
871
|
try {
|
|
@@ -845,6 +888,15 @@ function parseTranscript(transcriptPath) {
|
|
|
845
888
|
}
|
|
846
889
|
continue;
|
|
847
890
|
}
|
|
891
|
+
if (isTopLevelUserPrompt(entry)) {
|
|
892
|
+
currentTurnTextBlocks = [];
|
|
893
|
+
finalizePhase2();
|
|
894
|
+
currentPhases = [];
|
|
895
|
+
}
|
|
896
|
+
if (isToolResultUserEntry2(entry)) {
|
|
897
|
+
finalizePhase2();
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
848
900
|
if (entry.type !== "assistant") continue;
|
|
849
901
|
const msg = entry.message;
|
|
850
902
|
if (!msg) continue;
|
|
@@ -874,23 +926,55 @@ function parseTranscript(transcriptPath) {
|
|
|
874
926
|
}
|
|
875
927
|
lastUsage = u;
|
|
876
928
|
}
|
|
929
|
+
const phase = ensurePhase2();
|
|
930
|
+
const entryTimestamp = typeof entry["timestamp"] === "string" ? entry["timestamp"] : void 0;
|
|
931
|
+
if (entryTimestamp) {
|
|
932
|
+
if (!phase.startTimestamp) phase.startTimestamp = entryTimestamp;
|
|
933
|
+
phase.endTimestamp = entryTimestamp;
|
|
934
|
+
}
|
|
935
|
+
if (msg.model) phase.model = msg.model;
|
|
936
|
+
if (msg.usage) {
|
|
937
|
+
phase.inputTokens += (_f = msg.usage.input_tokens) != null ? _f : 0;
|
|
938
|
+
phase.outputTokens += (_g = msg.usage.output_tokens) != null ? _g : 0;
|
|
939
|
+
phase.cacheReadTokens += (_h = msg.usage.cache_read_input_tokens) != null ? _h : 0;
|
|
940
|
+
}
|
|
877
941
|
if (Array.isArray(msg.content)) {
|
|
878
942
|
for (const block of msg.content) {
|
|
879
943
|
if (block.type === "tool_use" && block.name) {
|
|
880
944
|
toolNames.add(block.name);
|
|
945
|
+
phase.toolCalls.push({
|
|
946
|
+
id: block.id,
|
|
947
|
+
name: block.name,
|
|
948
|
+
input: block.input
|
|
949
|
+
});
|
|
881
950
|
}
|
|
882
951
|
if (block.type === "thinking") {
|
|
883
952
|
summary.hasThinking = true;
|
|
953
|
+
phase.hasThinking = true;
|
|
954
|
+
}
|
|
955
|
+
if (block.type === "text" && typeof block.text === "string" && block.text.trim()) {
|
|
956
|
+
currentTurnTextBlocks.push(block.text);
|
|
957
|
+
if (phase.text) {
|
|
958
|
+
phase.text += "\n" + block.text;
|
|
959
|
+
} else {
|
|
960
|
+
phase.text = block.text;
|
|
961
|
+
}
|
|
884
962
|
}
|
|
885
963
|
}
|
|
964
|
+
summary.lastTurnTextBlocks = [...currentTurnTextBlocks];
|
|
886
965
|
}
|
|
887
966
|
}
|
|
967
|
+
finalizePhase2();
|
|
888
968
|
if (lastUsage) {
|
|
889
969
|
summary.lastTurnInputTokens = lastUsage.input_tokens;
|
|
890
970
|
summary.lastTurnOutputTokens = lastUsage.output_tokens;
|
|
891
971
|
summary.lastTurnCacheReadTokens = lastUsage.cache_read_input_tokens;
|
|
892
972
|
}
|
|
893
973
|
summary.toolsUsed = [...toolNames].sort();
|
|
974
|
+
if (summary.lastTurnTextBlocks.length > 0) {
|
|
975
|
+
summary.lastTurnFullOutput = summary.lastTurnTextBlocks.join("\n\n");
|
|
976
|
+
}
|
|
977
|
+
summary.llmCallPhases = currentPhases;
|
|
894
978
|
return summary;
|
|
895
979
|
} catch (e) {
|
|
896
980
|
return void 0;
|
|
@@ -1441,8 +1525,18 @@ function readAppendSystemPrompt(sessionId) {
|
|
|
1441
1525
|
return void 0;
|
|
1442
1526
|
}
|
|
1443
1527
|
}
|
|
1528
|
+
function isoToNanoString(iso, fallback) {
|
|
1529
|
+
if (!iso) return fallback;
|
|
1530
|
+
try {
|
|
1531
|
+
const ms = new Date(iso).getTime();
|
|
1532
|
+
if (!Number.isFinite(ms)) return fallback;
|
|
1533
|
+
return String(ms) + "000000";
|
|
1534
|
+
} catch (e) {
|
|
1535
|
+
return fallback;
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1444
1538
|
function enrichFromTranscript(payload, eventId, traceShipper) {
|
|
1445
|
-
var _a, _b;
|
|
1539
|
+
var _a, _b, _c, _d;
|
|
1446
1540
|
if (!payload.transcript_path) {
|
|
1447
1541
|
return { summary: void 0, props: {} };
|
|
1448
1542
|
}
|
|
@@ -1450,42 +1544,94 @@ function enrichFromTranscript(payload, eventId, traceShipper) {
|
|
|
1450
1544
|
const summary = parseTranscript(payload.transcript_path);
|
|
1451
1545
|
if (!summary) return { summary: void 0, props: {} };
|
|
1452
1546
|
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
|
-
|
|
1547
|
+
const parent = getParentContext(payload);
|
|
1548
|
+
if (summary.llmCallPhases.length > 0) {
|
|
1549
|
+
let emittedPhaseIndex = 0;
|
|
1550
|
+
for (let i = 0; i < summary.llmCallPhases.length; i++) {
|
|
1551
|
+
const phase = summary.llmCallPhases[i];
|
|
1552
|
+
const outputText = phase.text.trim();
|
|
1553
|
+
if (!outputText) {
|
|
1554
|
+
continue;
|
|
1555
|
+
}
|
|
1556
|
+
const model = (_b = (_a = phase.model) != null ? _a : summary.model) != null ? _b : "claude";
|
|
1557
|
+
const now = nowUnixNanoString();
|
|
1558
|
+
const startNano = isoToNanoString(phase.startTimestamp, now);
|
|
1559
|
+
const endNano = isoToNanoString(phase.endTimestamp, startNano);
|
|
1560
|
+
const toolCallNames = phase.toolCalls.map((tc) => tc.name).join(", ");
|
|
1561
|
+
traceShipper.createSpan({
|
|
1562
|
+
name: model,
|
|
1563
|
+
eventId,
|
|
1564
|
+
parent,
|
|
1565
|
+
startTimeUnixNano: startNano,
|
|
1566
|
+
endTimeUnixNano: endNano,
|
|
1567
|
+
attributes: [
|
|
1568
|
+
attrString("ai.operationId", "generateText"),
|
|
1569
|
+
attrString("gen_ai.response.model", model),
|
|
1570
|
+
attrString("gen_ai.system", "anthropic"),
|
|
1571
|
+
attrInt("gen_ai.usage.prompt_tokens", phase.inputTokens),
|
|
1572
|
+
attrInt("gen_ai.usage.completion_tokens", phase.outputTokens),
|
|
1573
|
+
...phase.cacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", phase.cacheReadTokens)] : [],
|
|
1574
|
+
attrString("ai.response.text", outputText),
|
|
1575
|
+
...toolCallNames ? [attrString("ai.toolCall.names", toolCallNames)] : [],
|
|
1576
|
+
...phase.hasThinking ? [attrString("ai.has_thinking", "true")] : [],
|
|
1577
|
+
attrInt("ai.llm_call.index", emittedPhaseIndex)
|
|
1578
|
+
]
|
|
1579
|
+
});
|
|
1580
|
+
emittedPhaseIndex++;
|
|
1581
|
+
}
|
|
1582
|
+
} else {
|
|
1583
|
+
const spanInputTokens = (_c = summary.lastTurnInputTokens) != null ? _c : summary.totalInputTokens;
|
|
1584
|
+
const spanOutputTokens = (_d = summary.lastTurnOutputTokens) != null ? _d : summary.totalOutputTokens;
|
|
1585
|
+
if (summary.model && (spanInputTokens > 0 || spanOutputTokens > 0)) {
|
|
1586
|
+
const now = nowUnixNanoString();
|
|
1587
|
+
traceShipper.createSpan({
|
|
1588
|
+
name: summary.model,
|
|
1589
|
+
eventId,
|
|
1590
|
+
parent,
|
|
1591
|
+
startTimeUnixNano: now,
|
|
1592
|
+
endTimeUnixNano: now,
|
|
1593
|
+
attributes: [
|
|
1594
|
+
attrString("ai.operationId", "generateText"),
|
|
1595
|
+
attrString("gen_ai.response.model", summary.model),
|
|
1596
|
+
attrString("gen_ai.system", "anthropic"),
|
|
1597
|
+
attrInt("gen_ai.usage.prompt_tokens", spanInputTokens),
|
|
1598
|
+
attrInt("gen_ai.usage.completion_tokens", spanOutputTokens),
|
|
1599
|
+
...summary.lastTurnCacheReadTokens != null && summary.lastTurnCacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", summary.lastTurnCacheReadTokens)] : []
|
|
1600
|
+
]
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1473
1603
|
}
|
|
1474
1604
|
return { summary, props };
|
|
1475
1605
|
} catch (e) {
|
|
1476
1606
|
return { summary: void 0, props: {} };
|
|
1477
1607
|
}
|
|
1478
1608
|
}
|
|
1609
|
+
function normalizeAssistantText(value) {
|
|
1610
|
+
return value.trim().replace(/\r\n/g, "\n");
|
|
1611
|
+
}
|
|
1612
|
+
function buildStopOutput(summary, lastAssistantMessage) {
|
|
1613
|
+
const finalMessage = typeof lastAssistantMessage === "string" ? normalizeAssistantText(lastAssistantMessage) : void 0;
|
|
1614
|
+
if (!summary) return finalMessage;
|
|
1615
|
+
const transcriptBlocks = summary.lastTurnTextBlocks.map(normalizeAssistantText).filter(Boolean);
|
|
1616
|
+
if (finalMessage && !transcriptBlocks.includes(finalMessage)) {
|
|
1617
|
+
transcriptBlocks.push(finalMessage);
|
|
1618
|
+
}
|
|
1619
|
+
if (transcriptBlocks.length > 0) {
|
|
1620
|
+
return transcriptBlocks.join("\n\n");
|
|
1621
|
+
}
|
|
1622
|
+
return finalMessage;
|
|
1623
|
+
}
|
|
1479
1624
|
async function handleStopOrFailure(payload, eventId, config, properties, eventShipper, traceShipper, extraProperties) {
|
|
1480
1625
|
const { summary, props: transcriptProps } = enrichFromTranscript(payload, eventId, traceShipper);
|
|
1481
1626
|
const instructions = gatherInstructions(payload.session_id);
|
|
1482
1627
|
const appendSysPrompt = readAppendSystemPrompt(payload.session_id);
|
|
1628
|
+
const output = buildStopOutput(summary, payload.last_assistant_message);
|
|
1483
1629
|
await eventShipper.patch(eventId, {
|
|
1484
1630
|
isPending: false,
|
|
1485
1631
|
userId: config.userId,
|
|
1486
1632
|
convoId: payload.session_id,
|
|
1487
1633
|
eventName: config.eventName,
|
|
1488
|
-
output
|
|
1634
|
+
output,
|
|
1489
1635
|
...(summary == null ? void 0 : summary.model) ? { model: summary.model } : {},
|
|
1490
1636
|
properties: {
|
|
1491
1637
|
...properties,
|
package/dist/index.d.cts
CHANGED
|
@@ -308,8 +308,37 @@ declare function mapHookToRaindrop(payload: HookPayload, config: MapperConfig, e
|
|
|
308
308
|
declare function extractAppendSystemPrompt(args: string[]): string | undefined;
|
|
309
309
|
|
|
310
310
|
declare const PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
311
|
-
declare const PACKAGE_VERSION = "0.0.
|
|
311
|
+
declare const PACKAGE_VERSION = "0.0.8";
|
|
312
312
|
|
|
313
|
+
/** A tool call extracted from an assistant message content block. */
|
|
314
|
+
interface LLMToolCall {
|
|
315
|
+
id?: string;
|
|
316
|
+
name: string;
|
|
317
|
+
input?: Record<string, unknown>;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* One LLM-call phase within a turn.
|
|
321
|
+
* A phase = one or more contiguous assistant messages before the next
|
|
322
|
+
* tool-result user entry resets the context.
|
|
323
|
+
*/
|
|
324
|
+
interface LLMCallPhase {
|
|
325
|
+
/** Concatenated assistant text blocks in this phase */
|
|
326
|
+
text: string;
|
|
327
|
+
/** Tool calls the model decided to make in this phase */
|
|
328
|
+
toolCalls: LLMToolCall[];
|
|
329
|
+
/** Model that produced this phase */
|
|
330
|
+
model?: string;
|
|
331
|
+
/** Token usage accumulated across assistant messages in this phase */
|
|
332
|
+
inputTokens: number;
|
|
333
|
+
outputTokens: number;
|
|
334
|
+
cacheReadTokens: number;
|
|
335
|
+
/** Whether a thinking block appeared in this phase */
|
|
336
|
+
hasThinking: boolean;
|
|
337
|
+
/** ISO timestamp of the first assistant message in the phase */
|
|
338
|
+
startTimestamp?: string;
|
|
339
|
+
/** ISO timestamp of the last assistant message in the phase */
|
|
340
|
+
endTimestamp?: string;
|
|
341
|
+
}
|
|
313
342
|
interface TranscriptSummary {
|
|
314
343
|
/** Aggregated token usage across all turns */
|
|
315
344
|
totalInputTokens: number;
|
|
@@ -338,6 +367,12 @@ interface TranscriptSummary {
|
|
|
338
367
|
gitBranch?: string;
|
|
339
368
|
/** Whether thinking/reasoning content was used */
|
|
340
369
|
hasThinking: boolean;
|
|
370
|
+
/** Ordered assistant text blocks for the latest top-level user turn */
|
|
371
|
+
lastTurnTextBlocks: string[];
|
|
372
|
+
/** Combined assistant text for the latest top-level user turn */
|
|
373
|
+
lastTurnFullOutput?: string;
|
|
374
|
+
/** Ordered LLM-call phases for the latest top-level user turn */
|
|
375
|
+
llmCallPhases: LLMCallPhase[];
|
|
341
376
|
}
|
|
342
377
|
/**
|
|
343
378
|
* Parse a Claude Code transcript JSONL file and extract a summary.
|
|
@@ -425,4 +460,4 @@ declare const TOOL_SCHEMA: {
|
|
|
425
460
|
};
|
|
426
461
|
declare function startMcpServer(): Promise<void>;
|
|
427
462
|
|
|
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 };
|
|
463
|
+
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
|
@@ -308,8 +308,37 @@ declare function mapHookToRaindrop(payload: HookPayload, config: MapperConfig, e
|
|
|
308
308
|
declare function extractAppendSystemPrompt(args: string[]): string | undefined;
|
|
309
309
|
|
|
310
310
|
declare const PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
311
|
-
declare const PACKAGE_VERSION = "0.0.
|
|
311
|
+
declare const PACKAGE_VERSION = "0.0.8";
|
|
312
312
|
|
|
313
|
+
/** A tool call extracted from an assistant message content block. */
|
|
314
|
+
interface LLMToolCall {
|
|
315
|
+
id?: string;
|
|
316
|
+
name: string;
|
|
317
|
+
input?: Record<string, unknown>;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* One LLM-call phase within a turn.
|
|
321
|
+
* A phase = one or more contiguous assistant messages before the next
|
|
322
|
+
* tool-result user entry resets the context.
|
|
323
|
+
*/
|
|
324
|
+
interface LLMCallPhase {
|
|
325
|
+
/** Concatenated assistant text blocks in this phase */
|
|
326
|
+
text: string;
|
|
327
|
+
/** Tool calls the model decided to make in this phase */
|
|
328
|
+
toolCalls: LLMToolCall[];
|
|
329
|
+
/** Model that produced this phase */
|
|
330
|
+
model?: string;
|
|
331
|
+
/** Token usage accumulated across assistant messages in this phase */
|
|
332
|
+
inputTokens: number;
|
|
333
|
+
outputTokens: number;
|
|
334
|
+
cacheReadTokens: number;
|
|
335
|
+
/** Whether a thinking block appeared in this phase */
|
|
336
|
+
hasThinking: boolean;
|
|
337
|
+
/** ISO timestamp of the first assistant message in the phase */
|
|
338
|
+
startTimestamp?: string;
|
|
339
|
+
/** ISO timestamp of the last assistant message in the phase */
|
|
340
|
+
endTimestamp?: string;
|
|
341
|
+
}
|
|
313
342
|
interface TranscriptSummary {
|
|
314
343
|
/** Aggregated token usage across all turns */
|
|
315
344
|
totalInputTokens: number;
|
|
@@ -338,6 +367,12 @@ interface TranscriptSummary {
|
|
|
338
367
|
gitBranch?: string;
|
|
339
368
|
/** Whether thinking/reasoning content was used */
|
|
340
369
|
hasThinking: boolean;
|
|
370
|
+
/** Ordered assistant text blocks for the latest top-level user turn */
|
|
371
|
+
lastTurnTextBlocks: string[];
|
|
372
|
+
/** Combined assistant text for the latest top-level user turn */
|
|
373
|
+
lastTurnFullOutput?: string;
|
|
374
|
+
/** Ordered LLM-call phases for the latest top-level user turn */
|
|
375
|
+
llmCallPhases: LLMCallPhase[];
|
|
341
376
|
}
|
|
342
377
|
/**
|
|
343
378
|
* Parse a Claude Code transcript JSONL file and extract a summary.
|
|
@@ -425,4 +460,4 @@ declare const TOOL_SCHEMA: {
|
|
|
425
460
|
};
|
|
426
461
|
declare function startMcpServer(): Promise<void>;
|
|
427
462
|
|
|
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 };
|
|
463
|
+
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 {
|
|
@@ -761,9 +761,47 @@ import { join as join2 } from "path";
|
|
|
761
761
|
|
|
762
762
|
// src/transcript.ts
|
|
763
763
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
764
|
+
function isToolResultContentBlock(value) {
|
|
765
|
+
return Boolean(
|
|
766
|
+
value && typeof value === "object" && "type" in value && value["type"] === "tool_result"
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
function isTopLevelUserPrompt(entry) {
|
|
770
|
+
var _a;
|
|
771
|
+
if (entry.type !== "user") return false;
|
|
772
|
+
const content = (_a = entry.message) == null ? void 0 : _a.content;
|
|
773
|
+
if (typeof content === "string") return true;
|
|
774
|
+
if (!Array.isArray(content)) return false;
|
|
775
|
+
return content.some((block) => !isToolResultContentBlock(block));
|
|
776
|
+
}
|
|
764
777
|
function parseTranscript(transcriptPath) {
|
|
765
|
-
var _a, _b, _c, _d, _e;
|
|
778
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
766
779
|
try {
|
|
780
|
+
let finalizePhase2 = function() {
|
|
781
|
+
if (activePhase && (activePhase.text || activePhase.toolCalls.length > 0)) {
|
|
782
|
+
currentPhases.push(activePhase);
|
|
783
|
+
}
|
|
784
|
+
activePhase = void 0;
|
|
785
|
+
}, ensurePhase2 = function() {
|
|
786
|
+
if (!activePhase) {
|
|
787
|
+
activePhase = {
|
|
788
|
+
text: "",
|
|
789
|
+
toolCalls: [],
|
|
790
|
+
inputTokens: 0,
|
|
791
|
+
outputTokens: 0,
|
|
792
|
+
cacheReadTokens: 0,
|
|
793
|
+
hasThinking: false
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
return activePhase;
|
|
797
|
+
}, isToolResultUserEntry2 = function(entry) {
|
|
798
|
+
var _a2;
|
|
799
|
+
if (entry.type !== "user") return false;
|
|
800
|
+
const content2 = (_a2 = entry.message) == null ? void 0 : _a2.content;
|
|
801
|
+
if (!Array.isArray(content2)) return false;
|
|
802
|
+
return content2.length > 0 && content2.every((b) => isToolResultContentBlock(b));
|
|
803
|
+
};
|
|
804
|
+
var finalizePhase = finalizePhase2, ensurePhase = ensurePhase2, isToolResultUserEntry = isToolResultUserEntry2;
|
|
767
805
|
if (!existsSync2(transcriptPath)) return void 0;
|
|
768
806
|
const content = readFileSync2(transcriptPath, "utf-8");
|
|
769
807
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
@@ -774,10 +812,15 @@ function parseTranscript(transcriptPath) {
|
|
|
774
812
|
totalCacheCreationTokens: 0,
|
|
775
813
|
turnCount: 0,
|
|
776
814
|
toolsUsed: [],
|
|
777
|
-
hasThinking: false
|
|
815
|
+
hasThinking: false,
|
|
816
|
+
lastTurnTextBlocks: [],
|
|
817
|
+
llmCallPhases: []
|
|
778
818
|
};
|
|
779
819
|
const toolNames = /* @__PURE__ */ new Set();
|
|
780
820
|
let lastUsage;
|
|
821
|
+
let currentTurnTextBlocks = [];
|
|
822
|
+
let currentPhases = [];
|
|
823
|
+
let activePhase;
|
|
781
824
|
for (const line of lines) {
|
|
782
825
|
let entry;
|
|
783
826
|
try {
|
|
@@ -800,6 +843,15 @@ function parseTranscript(transcriptPath) {
|
|
|
800
843
|
}
|
|
801
844
|
continue;
|
|
802
845
|
}
|
|
846
|
+
if (isTopLevelUserPrompt(entry)) {
|
|
847
|
+
currentTurnTextBlocks = [];
|
|
848
|
+
finalizePhase2();
|
|
849
|
+
currentPhases = [];
|
|
850
|
+
}
|
|
851
|
+
if (isToolResultUserEntry2(entry)) {
|
|
852
|
+
finalizePhase2();
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
803
855
|
if (entry.type !== "assistant") continue;
|
|
804
856
|
const msg = entry.message;
|
|
805
857
|
if (!msg) continue;
|
|
@@ -829,23 +881,55 @@ function parseTranscript(transcriptPath) {
|
|
|
829
881
|
}
|
|
830
882
|
lastUsage = u;
|
|
831
883
|
}
|
|
884
|
+
const phase = ensurePhase2();
|
|
885
|
+
const entryTimestamp = typeof entry["timestamp"] === "string" ? entry["timestamp"] : void 0;
|
|
886
|
+
if (entryTimestamp) {
|
|
887
|
+
if (!phase.startTimestamp) phase.startTimestamp = entryTimestamp;
|
|
888
|
+
phase.endTimestamp = entryTimestamp;
|
|
889
|
+
}
|
|
890
|
+
if (msg.model) phase.model = msg.model;
|
|
891
|
+
if (msg.usage) {
|
|
892
|
+
phase.inputTokens += (_f = msg.usage.input_tokens) != null ? _f : 0;
|
|
893
|
+
phase.outputTokens += (_g = msg.usage.output_tokens) != null ? _g : 0;
|
|
894
|
+
phase.cacheReadTokens += (_h = msg.usage.cache_read_input_tokens) != null ? _h : 0;
|
|
895
|
+
}
|
|
832
896
|
if (Array.isArray(msg.content)) {
|
|
833
897
|
for (const block of msg.content) {
|
|
834
898
|
if (block.type === "tool_use" && block.name) {
|
|
835
899
|
toolNames.add(block.name);
|
|
900
|
+
phase.toolCalls.push({
|
|
901
|
+
id: block.id,
|
|
902
|
+
name: block.name,
|
|
903
|
+
input: block.input
|
|
904
|
+
});
|
|
836
905
|
}
|
|
837
906
|
if (block.type === "thinking") {
|
|
838
907
|
summary.hasThinking = true;
|
|
908
|
+
phase.hasThinking = true;
|
|
909
|
+
}
|
|
910
|
+
if (block.type === "text" && typeof block.text === "string" && block.text.trim()) {
|
|
911
|
+
currentTurnTextBlocks.push(block.text);
|
|
912
|
+
if (phase.text) {
|
|
913
|
+
phase.text += "\n" + block.text;
|
|
914
|
+
} else {
|
|
915
|
+
phase.text = block.text;
|
|
916
|
+
}
|
|
839
917
|
}
|
|
840
918
|
}
|
|
919
|
+
summary.lastTurnTextBlocks = [...currentTurnTextBlocks];
|
|
841
920
|
}
|
|
842
921
|
}
|
|
922
|
+
finalizePhase2();
|
|
843
923
|
if (lastUsage) {
|
|
844
924
|
summary.lastTurnInputTokens = lastUsage.input_tokens;
|
|
845
925
|
summary.lastTurnOutputTokens = lastUsage.output_tokens;
|
|
846
926
|
summary.lastTurnCacheReadTokens = lastUsage.cache_read_input_tokens;
|
|
847
927
|
}
|
|
848
928
|
summary.toolsUsed = [...toolNames].sort();
|
|
929
|
+
if (summary.lastTurnTextBlocks.length > 0) {
|
|
930
|
+
summary.lastTurnFullOutput = summary.lastTurnTextBlocks.join("\n\n");
|
|
931
|
+
}
|
|
932
|
+
summary.llmCallPhases = currentPhases;
|
|
849
933
|
return summary;
|
|
850
934
|
} catch (e) {
|
|
851
935
|
return void 0;
|
|
@@ -1396,8 +1480,18 @@ function readAppendSystemPrompt(sessionId) {
|
|
|
1396
1480
|
return void 0;
|
|
1397
1481
|
}
|
|
1398
1482
|
}
|
|
1483
|
+
function isoToNanoString(iso, fallback) {
|
|
1484
|
+
if (!iso) return fallback;
|
|
1485
|
+
try {
|
|
1486
|
+
const ms = new Date(iso).getTime();
|
|
1487
|
+
if (!Number.isFinite(ms)) return fallback;
|
|
1488
|
+
return String(ms) + "000000";
|
|
1489
|
+
} catch (e) {
|
|
1490
|
+
return fallback;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1399
1493
|
function enrichFromTranscript(payload, eventId, traceShipper) {
|
|
1400
|
-
var _a, _b;
|
|
1494
|
+
var _a, _b, _c, _d;
|
|
1401
1495
|
if (!payload.transcript_path) {
|
|
1402
1496
|
return { summary: void 0, props: {} };
|
|
1403
1497
|
}
|
|
@@ -1405,42 +1499,94 @@ function enrichFromTranscript(payload, eventId, traceShipper) {
|
|
|
1405
1499
|
const summary = parseTranscript(payload.transcript_path);
|
|
1406
1500
|
if (!summary) return { summary: void 0, props: {} };
|
|
1407
1501
|
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
|
-
|
|
1502
|
+
const parent = getParentContext(payload);
|
|
1503
|
+
if (summary.llmCallPhases.length > 0) {
|
|
1504
|
+
let emittedPhaseIndex = 0;
|
|
1505
|
+
for (let i = 0; i < summary.llmCallPhases.length; i++) {
|
|
1506
|
+
const phase = summary.llmCallPhases[i];
|
|
1507
|
+
const outputText = phase.text.trim();
|
|
1508
|
+
if (!outputText) {
|
|
1509
|
+
continue;
|
|
1510
|
+
}
|
|
1511
|
+
const model = (_b = (_a = phase.model) != null ? _a : summary.model) != null ? _b : "claude";
|
|
1512
|
+
const now = nowUnixNanoString();
|
|
1513
|
+
const startNano = isoToNanoString(phase.startTimestamp, now);
|
|
1514
|
+
const endNano = isoToNanoString(phase.endTimestamp, startNano);
|
|
1515
|
+
const toolCallNames = phase.toolCalls.map((tc) => tc.name).join(", ");
|
|
1516
|
+
traceShipper.createSpan({
|
|
1517
|
+
name: model,
|
|
1518
|
+
eventId,
|
|
1519
|
+
parent,
|
|
1520
|
+
startTimeUnixNano: startNano,
|
|
1521
|
+
endTimeUnixNano: endNano,
|
|
1522
|
+
attributes: [
|
|
1523
|
+
attrString("ai.operationId", "generateText"),
|
|
1524
|
+
attrString("gen_ai.response.model", model),
|
|
1525
|
+
attrString("gen_ai.system", "anthropic"),
|
|
1526
|
+
attrInt("gen_ai.usage.prompt_tokens", phase.inputTokens),
|
|
1527
|
+
attrInt("gen_ai.usage.completion_tokens", phase.outputTokens),
|
|
1528
|
+
...phase.cacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", phase.cacheReadTokens)] : [],
|
|
1529
|
+
attrString("ai.response.text", outputText),
|
|
1530
|
+
...toolCallNames ? [attrString("ai.toolCall.names", toolCallNames)] : [],
|
|
1531
|
+
...phase.hasThinking ? [attrString("ai.has_thinking", "true")] : [],
|
|
1532
|
+
attrInt("ai.llm_call.index", emittedPhaseIndex)
|
|
1533
|
+
]
|
|
1534
|
+
});
|
|
1535
|
+
emittedPhaseIndex++;
|
|
1536
|
+
}
|
|
1537
|
+
} else {
|
|
1538
|
+
const spanInputTokens = (_c = summary.lastTurnInputTokens) != null ? _c : summary.totalInputTokens;
|
|
1539
|
+
const spanOutputTokens = (_d = summary.lastTurnOutputTokens) != null ? _d : summary.totalOutputTokens;
|
|
1540
|
+
if (summary.model && (spanInputTokens > 0 || spanOutputTokens > 0)) {
|
|
1541
|
+
const now = nowUnixNanoString();
|
|
1542
|
+
traceShipper.createSpan({
|
|
1543
|
+
name: summary.model,
|
|
1544
|
+
eventId,
|
|
1545
|
+
parent,
|
|
1546
|
+
startTimeUnixNano: now,
|
|
1547
|
+
endTimeUnixNano: now,
|
|
1548
|
+
attributes: [
|
|
1549
|
+
attrString("ai.operationId", "generateText"),
|
|
1550
|
+
attrString("gen_ai.response.model", summary.model),
|
|
1551
|
+
attrString("gen_ai.system", "anthropic"),
|
|
1552
|
+
attrInt("gen_ai.usage.prompt_tokens", spanInputTokens),
|
|
1553
|
+
attrInt("gen_ai.usage.completion_tokens", spanOutputTokens),
|
|
1554
|
+
...summary.lastTurnCacheReadTokens != null && summary.lastTurnCacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", summary.lastTurnCacheReadTokens)] : []
|
|
1555
|
+
]
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1428
1558
|
}
|
|
1429
1559
|
return { summary, props };
|
|
1430
1560
|
} catch (e) {
|
|
1431
1561
|
return { summary: void 0, props: {} };
|
|
1432
1562
|
}
|
|
1433
1563
|
}
|
|
1564
|
+
function normalizeAssistantText(value) {
|
|
1565
|
+
return value.trim().replace(/\r\n/g, "\n");
|
|
1566
|
+
}
|
|
1567
|
+
function buildStopOutput(summary, lastAssistantMessage) {
|
|
1568
|
+
const finalMessage = typeof lastAssistantMessage === "string" ? normalizeAssistantText(lastAssistantMessage) : void 0;
|
|
1569
|
+
if (!summary) return finalMessage;
|
|
1570
|
+
const transcriptBlocks = summary.lastTurnTextBlocks.map(normalizeAssistantText).filter(Boolean);
|
|
1571
|
+
if (finalMessage && !transcriptBlocks.includes(finalMessage)) {
|
|
1572
|
+
transcriptBlocks.push(finalMessage);
|
|
1573
|
+
}
|
|
1574
|
+
if (transcriptBlocks.length > 0) {
|
|
1575
|
+
return transcriptBlocks.join("\n\n");
|
|
1576
|
+
}
|
|
1577
|
+
return finalMessage;
|
|
1578
|
+
}
|
|
1434
1579
|
async function handleStopOrFailure(payload, eventId, config, properties, eventShipper, traceShipper, extraProperties) {
|
|
1435
1580
|
const { summary, props: transcriptProps } = enrichFromTranscript(payload, eventId, traceShipper);
|
|
1436
1581
|
const instructions = gatherInstructions(payload.session_id);
|
|
1437
1582
|
const appendSysPrompt = readAppendSystemPrompt(payload.session_id);
|
|
1583
|
+
const output = buildStopOutput(summary, payload.last_assistant_message);
|
|
1438
1584
|
await eventShipper.patch(eventId, {
|
|
1439
1585
|
isPending: false,
|
|
1440
1586
|
userId: config.userId,
|
|
1441
1587
|
convoId: payload.session_id,
|
|
1442
1588
|
eventName: config.eventName,
|
|
1443
|
-
output
|
|
1589
|
+
output,
|
|
1444
1590
|
...(summary == null ? void 0 : summary.model) ? { model: summary.model } : {},
|
|
1445
1591
|
properties: {
|
|
1446
1592
|
...properties,
|
package/package.json
CHANGED