@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 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.7";
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 spanInputTokens = (_a = summary.lastTurnInputTokens) != null ? _a : summary.totalInputTokens;
1341
- const spanOutputTokens = (_b = summary.lastTurnOutputTokens) != null ? _b : summary.totalOutputTokens;
1342
- if (summary.model && (spanInputTokens > 0 || spanOutputTokens > 0)) {
1343
- const parent = getParentContext(payload);
1344
- const now = nowUnixNanoString();
1345
- traceShipper.createSpan({
1346
- name: summary.model,
1347
- eventId,
1348
- parent,
1349
- startTimeUnixNano: now,
1350
- endTimeUnixNano: now,
1351
- attributes: [
1352
- attrString("ai.operationId", "generateText"),
1353
- attrString("gen_ai.response.model", summary.model),
1354
- attrString("gen_ai.system", "anthropic"),
1355
- attrInt("gen_ai.usage.prompt_tokens", spanInputTokens),
1356
- attrInt("gen_ai.usage.completion_tokens", spanOutputTokens),
1357
- ...summary.lastTurnCacheReadTokens != null && summary.lastTurnCacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", summary.lastTurnCacheReadTokens)] : []
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: payload.last_assistant_message,
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.7";
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 spanInputTokens = (_a = summary.lastTurnInputTokens) != null ? _a : summary.totalInputTokens;
1454
- const spanOutputTokens = (_b = summary.lastTurnOutputTokens) != null ? _b : summary.totalOutputTokens;
1455
- if (summary.model && (spanInputTokens > 0 || spanOutputTokens > 0)) {
1456
- const parent = getParentContext(payload);
1457
- const now = nowUnixNanoString();
1458
- traceShipper.createSpan({
1459
- name: summary.model,
1460
- eventId,
1461
- parent,
1462
- startTimeUnixNano: now,
1463
- endTimeUnixNano: now,
1464
- attributes: [
1465
- attrString("ai.operationId", "generateText"),
1466
- attrString("gen_ai.response.model", summary.model),
1467
- attrString("gen_ai.system", "anthropic"),
1468
- attrInt("gen_ai.usage.prompt_tokens", spanInputTokens),
1469
- attrInt("gen_ai.usage.completion_tokens", spanOutputTokens),
1470
- ...summary.lastTurnCacheReadTokens != null && summary.lastTurnCacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", summary.lastTurnCacheReadTokens)] : []
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: payload.last_assistant_message,
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.7";
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.7";
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.7";
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 spanInputTokens = (_a = summary.lastTurnInputTokens) != null ? _a : summary.totalInputTokens;
1409
- const spanOutputTokens = (_b = summary.lastTurnOutputTokens) != null ? _b : summary.totalOutputTokens;
1410
- if (summary.model && (spanInputTokens > 0 || spanOutputTokens > 0)) {
1411
- const parent = getParentContext(payload);
1412
- const now = nowUnixNanoString();
1413
- traceShipper.createSpan({
1414
- name: summary.model,
1415
- eventId,
1416
- parent,
1417
- startTimeUnixNano: now,
1418
- endTimeUnixNano: now,
1419
- attributes: [
1420
- attrString("ai.operationId", "generateText"),
1421
- attrString("gen_ai.response.model", summary.model),
1422
- attrString("gen_ai.system", "anthropic"),
1423
- attrInt("gen_ai.usage.prompt_tokens", spanInputTokens),
1424
- attrInt("gen_ai.usage.completion_tokens", spanOutputTokens),
1425
- ...summary.lastTurnCacheReadTokens != null && summary.lastTurnCacheReadTokens > 0 ? [attrInt("gen_ai.usage.cache_read_tokens", summary.lastTurnCacheReadTokens)] : []
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: payload.last_assistant_message,
1589
+ output,
1444
1590
  ...(summary == null ? void 0 : summary.model) ? { model: summary.model } : {},
1445
1591
  properties: {
1446
1592
  ...properties,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raindrop-ai/claude-code",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "Raindrop observability for Claude Code CLI \u2014 automatic session, tool call, and prompt tracing via hooks",
5
5
  "license": "MIT",
6
6
  "type": "module",