@raindrop-ai/claude-code 0.0.6 → 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/README.md +28 -3
- package/dist/cli.js +266 -37
- 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 +2 -2
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ raindrop-claude-code setup
|
|
|
16
16
|
|
|
17
17
|
This saves your write key and configures Claude Code hooks. Every session will now send telemetry to your [Raindrop dashboard](https://app.raindrop.ai).
|
|
18
18
|
|
|
19
|
+
Hooks are **synchronous by default** for headless/one-shot compatibility (`claude -p`). Use `--async` if you prefer non-blocking hooks in interactive mode.
|
|
20
|
+
|
|
19
21
|
## What gets tracked
|
|
20
22
|
|
|
21
23
|
- Every prompt turn as a separate event, grouped by session
|
|
@@ -24,27 +26,50 @@ This saves your write key and configures Claude Code hooks. Every session will n
|
|
|
24
26
|
- Model name, service tier, and stop reason
|
|
25
27
|
- CLAUDE.md and rules file contents
|
|
26
28
|
- `--append-system-prompt` / `--append-system-prompt-file` content (best-effort)
|
|
27
|
-
- Self-diagnostics — agent-reported issues via MCP tool, with customizable signal categories
|
|
28
29
|
- Subagent spawns and completions
|
|
29
30
|
- Permission denials and context compaction
|
|
31
|
+
- Self-diagnostics — agent-reported issues via MCP tool, with customizable signal categories
|
|
30
32
|
- Nested trace view (tools under root, subagent tools under subagent)
|
|
31
33
|
- Claude's responses and errors
|
|
32
34
|
|
|
35
|
+
## Custom Properties
|
|
36
|
+
|
|
37
|
+
Tag events with product names or custom metadata via `.claude/settings.json`:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"env": {
|
|
42
|
+
"RAINDROP_EVENT_NAME": "design-agent",
|
|
43
|
+
"RAINDROP_PROPERTIES": "{\"product\":\"design\",\"team\":\"ai\"}"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
33
48
|
## Custom Self-Diagnostics Signals
|
|
34
49
|
|
|
35
|
-
Replace built-in signal categories with your own via config
|
|
50
|
+
Replace the built-in signal categories with your own via `~/.config/raindrop/config.json`:
|
|
36
51
|
|
|
37
52
|
```json
|
|
38
53
|
{
|
|
39
54
|
"self_diagnostics": {
|
|
40
55
|
"signals": {
|
|
41
|
-
"billing_complaint": { "description": "User billing issue.", "sentiment": "NEGATIVE" }
|
|
56
|
+
"billing_complaint": { "description": "User billing issue.", "sentiment": "NEGATIVE" },
|
|
57
|
+
"feature_request": { "description": "User wants a feature.", "sentiment": "POSITIVE" }
|
|
42
58
|
},
|
|
43
59
|
"guidance": "Only report billing if explicitly mentioned."
|
|
44
60
|
}
|
|
45
61
|
}
|
|
46
62
|
```
|
|
47
63
|
|
|
64
|
+
Or via env var: `RAINDROP_SELF_DIAGNOSTICS='{"signals":{...}}'`
|
|
65
|
+
|
|
66
|
+
## Debugging
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
raindrop-claude-code debug-on # logs hook output to /tmp/raindrop-hooks.log
|
|
70
|
+
raindrop-claude-code debug-off # disables logging
|
|
71
|
+
```
|
|
72
|
+
|
|
48
73
|
## Docs
|
|
49
74
|
|
|
50
75
|
Full documentation: [docs.raindrop.ai/sdk/claude-code](https://docs.raindrop.ai/sdk/claude-code)
|
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,
|
|
@@ -1745,7 +1891,7 @@ function getHookEventsForVersion(version) {
|
|
|
1745
1891
|
}
|
|
1746
1892
|
return events;
|
|
1747
1893
|
}
|
|
1748
|
-
function makeHookConfig(hookEvents) {
|
|
1894
|
+
function makeHookConfig(hookEvents, useAsync) {
|
|
1749
1895
|
const hooks = {};
|
|
1750
1896
|
for (const event of hookEvents) {
|
|
1751
1897
|
hooks[event] = [
|
|
@@ -1754,7 +1900,7 @@ function makeHookConfig(hookEvents) {
|
|
|
1754
1900
|
{
|
|
1755
1901
|
type: "command",
|
|
1756
1902
|
command: "raindrop-claude-code hook",
|
|
1757
|
-
async:
|
|
1903
|
+
async: useAsync,
|
|
1758
1904
|
timeout: 10
|
|
1759
1905
|
}
|
|
1760
1906
|
]
|
|
@@ -1773,7 +1919,7 @@ function prompt(question) {
|
|
|
1773
1919
|
});
|
|
1774
1920
|
}
|
|
1775
1921
|
async function runSetup(args2) {
|
|
1776
|
-
var _a, _b, _c, _d, _e;
|
|
1922
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1777
1923
|
const scope = (_a = args2.scope) != null ? _a : "user";
|
|
1778
1924
|
const scopeLabel = scope === "project" ? "project" : "global";
|
|
1779
1925
|
console.log("\n Raindrop \xD7 Claude Code \u2014 Setup\n");
|
|
@@ -1832,8 +1978,9 @@ async function runSetup(args2) {
|
|
|
1832
1978
|
settings = {};
|
|
1833
1979
|
}
|
|
1834
1980
|
}
|
|
1835
|
-
const
|
|
1836
|
-
const
|
|
1981
|
+
const useAsync = (_e = args2.useAsync) != null ? _e : false;
|
|
1982
|
+
const existingHooks = (_f = settings["hooks"]) != null ? _f : {};
|
|
1983
|
+
const newHooks = makeHookConfig(hookEvents, useAsync);
|
|
1837
1984
|
for (const [event, hookGroups] of Object.entries(newHooks)) {
|
|
1838
1985
|
const existing = existingHooks[event];
|
|
1839
1986
|
if (!existing || !Array.isArray(existing)) {
|
|
@@ -1841,13 +1988,16 @@ async function runSetup(args2) {
|
|
|
1841
1988
|
continue;
|
|
1842
1989
|
}
|
|
1843
1990
|
const typedExisting = existing;
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1991
|
+
let found = false;
|
|
1992
|
+
for (const group of typedExisting) {
|
|
1993
|
+
for (const h of (_g = group.hooks) != null ? _g : []) {
|
|
1994
|
+
if (typeof h["command"] === "string" && h["command"].includes("raindrop-claude-code")) {
|
|
1995
|
+
h["async"] = useAsync;
|
|
1996
|
+
found = true;
|
|
1997
|
+
}
|
|
1848
1998
|
}
|
|
1849
|
-
|
|
1850
|
-
if (!
|
|
1999
|
+
}
|
|
2000
|
+
if (!found) {
|
|
1851
2001
|
typedExisting.push(...hookGroups);
|
|
1852
2002
|
}
|
|
1853
2003
|
}
|
|
@@ -1893,6 +2043,71 @@ async function runSetup(args2) {
|
|
|
1893
2043
|
`);
|
|
1894
2044
|
}
|
|
1895
2045
|
}
|
|
2046
|
+
var DEBUG_LOG_PATH = "/tmp/raindrop-hooks.log";
|
|
2047
|
+
var DEBUG_PREFIX = "RAINDROP_DEBUG=true ";
|
|
2048
|
+
var TEE_SUFFIX = ` 2>&1 | tee -a ${DEBUG_LOG_PATH} || true`;
|
|
2049
|
+
function toggleDebug(enable, scope = "user") {
|
|
2050
|
+
var _a;
|
|
2051
|
+
const settingsPath = getClaudeSettingsPath(scope, process.cwd());
|
|
2052
|
+
if (!existsSync6(settingsPath)) {
|
|
2053
|
+
console.error(` Settings file not found: ${settingsPath}`);
|
|
2054
|
+
console.error(` Run 'raindrop-claude-code setup' first.`);
|
|
2055
|
+
process.exit(1);
|
|
2056
|
+
}
|
|
2057
|
+
let settings;
|
|
2058
|
+
try {
|
|
2059
|
+
settings = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
2060
|
+
} catch (e) {
|
|
2061
|
+
console.error(` Could not parse ${settingsPath}`);
|
|
2062
|
+
process.exit(1);
|
|
2063
|
+
}
|
|
2064
|
+
const hooks = settings["hooks"];
|
|
2065
|
+
if (!hooks) {
|
|
2066
|
+
console.error(` No hooks found in ${settingsPath}`);
|
|
2067
|
+
process.exit(1);
|
|
2068
|
+
}
|
|
2069
|
+
let modified = 0;
|
|
2070
|
+
for (const groups of Object.values(hooks)) {
|
|
2071
|
+
if (!Array.isArray(groups)) continue;
|
|
2072
|
+
for (const group of groups) {
|
|
2073
|
+
for (const h of (_a = group.hooks) != null ? _a : []) {
|
|
2074
|
+
const cmd = h["command"];
|
|
2075
|
+
if (typeof cmd !== "string" || !cmd.includes("raindrop-claude-code")) continue;
|
|
2076
|
+
if (enable) {
|
|
2077
|
+
let updated = cmd;
|
|
2078
|
+
if (!updated.startsWith(DEBUG_PREFIX)) {
|
|
2079
|
+
updated = DEBUG_PREFIX + updated;
|
|
2080
|
+
}
|
|
2081
|
+
if (!updated.includes("tee")) {
|
|
2082
|
+
updated = updated + TEE_SUFFIX;
|
|
2083
|
+
}
|
|
2084
|
+
if (updated !== cmd) {
|
|
2085
|
+
h["command"] = updated;
|
|
2086
|
+
modified++;
|
|
2087
|
+
}
|
|
2088
|
+
} else {
|
|
2089
|
+
let updated = cmd;
|
|
2090
|
+
if (updated.startsWith(DEBUG_PREFIX)) {
|
|
2091
|
+
updated = updated.slice(DEBUG_PREFIX.length);
|
|
2092
|
+
}
|
|
2093
|
+
updated = updated.replace(TEE_SUFFIX, "").replace(/ 2>&1 \| tee -a .*$/, "");
|
|
2094
|
+
if (updated !== cmd) {
|
|
2095
|
+
h["command"] = updated;
|
|
2096
|
+
modified++;
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
writeFileSync4(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
2103
|
+
if (enable) {
|
|
2104
|
+
console.log(` Debug logging enabled (${modified} hooks updated).`);
|
|
2105
|
+
console.log(` Log file: ${DEBUG_LOG_PATH}`);
|
|
2106
|
+
console.log(` Run 'raindrop-claude-code debug-off' to disable.`);
|
|
2107
|
+
} else {
|
|
2108
|
+
console.log(` Debug logging disabled (${modified} hooks updated).`);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
1896
2111
|
|
|
1897
2112
|
// src/mcp-serve.ts
|
|
1898
2113
|
import { createInterface as createInterface2 } from "readline";
|
|
@@ -2194,7 +2409,8 @@ async function main() {
|
|
|
2194
2409
|
writeKey: parseFlag("write-key"),
|
|
2195
2410
|
userId: parseFlag("user-id"),
|
|
2196
2411
|
scope,
|
|
2197
|
-
localOnly: args.includes("--local-only")
|
|
2412
|
+
localOnly: args.includes("--local-only"),
|
|
2413
|
+
useAsync: args.includes("--async")
|
|
2198
2414
|
});
|
|
2199
2415
|
break;
|
|
2200
2416
|
}
|
|
@@ -2232,6 +2448,16 @@ async function main() {
|
|
|
2232
2448
|
console.log(" Raindrop hooks disabled.");
|
|
2233
2449
|
break;
|
|
2234
2450
|
}
|
|
2451
|
+
case "debug-on": {
|
|
2452
|
+
const debugScope = parseFlag("scope") === "project" ? "project" : "user";
|
|
2453
|
+
toggleDebug(true, debugScope);
|
|
2454
|
+
break;
|
|
2455
|
+
}
|
|
2456
|
+
case "debug-off": {
|
|
2457
|
+
const debugScope = parseFlag("scope") === "project" ? "project" : "user";
|
|
2458
|
+
toggleDebug(false, debugScope);
|
|
2459
|
+
break;
|
|
2460
|
+
}
|
|
2235
2461
|
case "version":
|
|
2236
2462
|
case "--version":
|
|
2237
2463
|
case "-v": {
|
|
@@ -2253,12 +2479,15 @@ async function main() {
|
|
|
2253
2479
|
--user-id=ID User identifier (defaults to system username)
|
|
2254
2480
|
--scope=SCOPE "user" (global, default) or "project" (.claude/ in cwd)
|
|
2255
2481
|
--local-only Install hooks without a write key (local debugger only)
|
|
2482
|
+
--async Use async hooks (default: sync for headless compatibility)
|
|
2256
2483
|
|
|
2257
2484
|
hook Handle a Claude Code hook event (reads JSON from stdin)
|
|
2258
2485
|
mcp-serve Start the self-diagnostics MCP server (stdio)
|
|
2259
2486
|
status Check local debugger connectivity
|
|
2260
2487
|
enable Enable Raindrop hooks
|
|
2261
2488
|
disable Disable Raindrop hooks
|
|
2489
|
+
debug-on Enable debug logging to /tmp/raindrop-hooks.log
|
|
2490
|
+
debug-off Disable debug logging
|
|
2262
2491
|
version Print version
|
|
2263
2492
|
help Show this help message
|
|
2264
2493
|
|
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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raindrop-ai/claude-code",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Raindrop observability for Claude Code CLI
|
|
3
|
+
"version": "0.0.8",
|
|
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",
|
|
7
7
|
"main": "dist/index.cjs",
|