fix(claude-hook): derive span durations from start timestamps
This commit is contained in:
@@ -331,6 +331,8 @@ async function handleToolStart(input) {
|
|||||||
if (spanKey) {
|
if (spanKey) {
|
||||||
activeSpans.set(spanKey, spanId);
|
activeSpans.set(spanKey, spanId);
|
||||||
state.spans[spanKey] = spanId;
|
state.spans[spanKey] = spanId;
|
||||||
|
state.spanStartTimes = state.spanStartTimes || {};
|
||||||
|
state.spanStartTimes[spanId] = Date.now();
|
||||||
if (runId)
|
if (runId)
|
||||||
state.runId = runId;
|
state.runId = runId;
|
||||||
if (sessionKey)
|
if (sessionKey)
|
||||||
@@ -358,7 +360,13 @@ async function handleToolEnd(input) {
|
|||||||
const spanId = spanKey ? activeSpans.get(spanKey) || state.spans[spanKey] : void 0;
|
const spanId = spanKey ? activeSpans.get(spanKey) || state.spans[spanKey] : void 0;
|
||||||
const result = isRecord(input.result) ? input.result : isRecord(input.output) ? input.output : {};
|
const result = isRecord(input.result) ? input.result : isRecord(input.output) ? input.output : {};
|
||||||
const success = !result.error;
|
const success = !result.error;
|
||||||
const duration = pickNumber(input.duration_ms, input.elapsed_ms);
|
const startTime = spanId ? state.spanStartTimes?.[spanId] : void 0;
|
||||||
|
const duration = pickNumber(input.duration_ms, input.elapsed_ms) ?? (startTime ? Date.now() - startTime : void 0);
|
||||||
|
if (spanId && state.spanStartTimes) {
|
||||||
|
delete state.spanStartTimes[spanId];
|
||||||
|
if (sessionKey)
|
||||||
|
saveState(sessionKey, state);
|
||||||
|
}
|
||||||
enqueue(buildEnvelope("span.end", sessionKey, {
|
enqueue(buildEnvelope("span.end", sessionKey, {
|
||||||
runId,
|
runId,
|
||||||
spanId,
|
spanId,
|
||||||
@@ -387,6 +395,8 @@ async function handleSubagentStart(input) {
|
|||||||
if (sessionKey) {
|
if (sessionKey) {
|
||||||
activeSubagents.set(sessionKey, { name: agentName, spanId });
|
activeSubagents.set(sessionKey, { name: agentName, spanId });
|
||||||
state.subagent = { name: agentName, spanId };
|
state.subagent = { name: agentName, spanId };
|
||||||
|
state.spanStartTimes = state.spanStartTimes || {};
|
||||||
|
state.spanStartTimes[spanId] = Date.now();
|
||||||
if (runId)
|
if (runId)
|
||||||
state.runId = runId;
|
state.runId = runId;
|
||||||
saveState(sessionKey, state);
|
saveState(sessionKey, state);
|
||||||
@@ -412,8 +422,14 @@ async function handleSubagentStop(input) {
|
|||||||
const subagent = sessionKey ? activeSubagents.get(sessionKey) || state.subagent : void 0;
|
const subagent = sessionKey ? activeSubagents.get(sessionKey) || state.subagent : void 0;
|
||||||
const spanId = subagent?.spanId;
|
const spanId = subagent?.spanId;
|
||||||
const agentName = subagent?.name || pickString(input.agent, input.agent_name) || "unknown";
|
const agentName = subagent?.name || pickString(input.agent, input.agent_name) || "unknown";
|
||||||
const duration = pickNumber(input.duration_ms, input.elapsed_ms);
|
const startTime = spanId ? state.spanStartTimes?.[spanId] : void 0;
|
||||||
|
const duration = pickNumber(input.duration_ms, input.elapsed_ms) ?? (startTime ? Date.now() - startTime : void 0);
|
||||||
const usage = getUsage(input);
|
const usage = getUsage(input);
|
||||||
|
if (spanId && state.spanStartTimes) {
|
||||||
|
delete state.spanStartTimes[spanId];
|
||||||
|
if (sessionKey)
|
||||||
|
saveState(sessionKey, state);
|
||||||
|
}
|
||||||
enqueue(buildEnvelope("span.end", sessionKey, {
|
enqueue(buildEnvelope("span.end", sessionKey, {
|
||||||
runId,
|
runId,
|
||||||
spanId,
|
spanId,
|
||||||
@@ -439,6 +455,8 @@ async function handleCompactStart(input) {
|
|||||||
if (sessionKey) {
|
if (sessionKey) {
|
||||||
activeSpans.set(sessionKey + ":compact", spanId);
|
activeSpans.set(sessionKey + ":compact", spanId);
|
||||||
state.compactSpanId = spanId;
|
state.compactSpanId = spanId;
|
||||||
|
state.spanStartTimes = state.spanStartTimes || {};
|
||||||
|
state.spanStartTimes[spanId] = Date.now();
|
||||||
if (runId)
|
if (runId)
|
||||||
state.runId = runId;
|
state.runId = runId;
|
||||||
saveState(sessionKey, state);
|
saveState(sessionKey, state);
|
||||||
@@ -459,8 +477,14 @@ async function handleCompactEnd(input) {
|
|||||||
const runId = sessionKey ? activeRuns.get(sessionKey) || state.runId : void 0;
|
const runId = sessionKey ? activeRuns.get(sessionKey) || state.runId : void 0;
|
||||||
const spanKey = sessionKey ? sessionKey + ":compact" : void 0;
|
const spanKey = sessionKey ? sessionKey + ":compact" : void 0;
|
||||||
const spanId = spanKey ? activeSpans.get(spanKey) || state.compactSpanId : void 0;
|
const spanId = spanKey ? activeSpans.get(spanKey) || state.compactSpanId : void 0;
|
||||||
const duration = pickNumber(input.duration_ms, input.elapsed_ms);
|
const startTime = spanId ? state.spanStartTimes?.[spanId] : void 0;
|
||||||
|
const duration = pickNumber(input.duration_ms, input.elapsed_ms) ?? (startTime ? Date.now() - startTime : void 0);
|
||||||
const contextWindow = getContextWindow(input);
|
const contextWindow = getContextWindow(input);
|
||||||
|
if (spanId && state.spanStartTimes) {
|
||||||
|
delete state.spanStartTimes[spanId];
|
||||||
|
if (sessionKey)
|
||||||
|
saveState(sessionKey, state);
|
||||||
|
}
|
||||||
enqueue(buildEnvelope("span.end", sessionKey, {
|
enqueue(buildEnvelope("span.end", sessionKey, {
|
||||||
runId,
|
runId,
|
||||||
spanId,
|
spanId,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ interface Dict { [key: string]: any }
|
|||||||
interface SessionState {
|
interface SessionState {
|
||||||
runId?: string;
|
runId?: string;
|
||||||
spans: { [key: string]: string }; // key = sessionKey:toolName, value = spanId
|
spans: { [key: string]: string }; // key = sessionKey:toolName, value = spanId
|
||||||
|
spanStartTimes?: { [spanId: string]: number }; // spanId -> epoch ms
|
||||||
subagent?: { name: string; spanId: string };
|
subagent?: { name: string; spanId: string };
|
||||||
compactSpanId?: string;
|
compactSpanId?: string;
|
||||||
}
|
}
|
||||||
@@ -385,6 +386,8 @@ async function handleToolStart(input: Dict) {
|
|||||||
if (spanKey) {
|
if (spanKey) {
|
||||||
activeSpans.set(spanKey, spanId);
|
activeSpans.set(spanKey, spanId);
|
||||||
state.spans[spanKey] = spanId;
|
state.spans[spanKey] = spanId;
|
||||||
|
state.spanStartTimes = state.spanStartTimes || {};
|
||||||
|
state.spanStartTimes[spanId] = Date.now();
|
||||||
if (runId) state.runId = runId;
|
if (runId) state.runId = runId;
|
||||||
if (sessionKey) saveState(sessionKey, state);
|
if (sessionKey) saveState(sessionKey, state);
|
||||||
}
|
}
|
||||||
@@ -413,7 +416,12 @@ async function handleToolEnd(input: Dict) {
|
|||||||
const spanId = spanKey ? (activeSpans.get(spanKey) || state.spans[spanKey]) : undefined;
|
const spanId = spanKey ? (activeSpans.get(spanKey) || state.spans[spanKey]) : undefined;
|
||||||
const result = isRecord(input.result) ? input.result : isRecord(input.output) ? input.output : {};
|
const result = isRecord(input.result) ? input.result : isRecord(input.output) ? input.output : {};
|
||||||
const success = !result.error;
|
const success = !result.error;
|
||||||
const duration = pickNumber(input.duration_ms, input.elapsed_ms);
|
const startTime = spanId ? state.spanStartTimes?.[spanId] : undefined;
|
||||||
|
const duration = pickNumber(input.duration_ms, input.elapsed_ms) ?? (startTime ? Date.now() - startTime : undefined);
|
||||||
|
if (spanId && state.spanStartTimes) {
|
||||||
|
delete state.spanStartTimes[spanId];
|
||||||
|
if (sessionKey) saveState(sessionKey, state);
|
||||||
|
}
|
||||||
|
|
||||||
enqueue(buildEnvelope('span.end', sessionKey, {
|
enqueue(buildEnvelope('span.end', sessionKey, {
|
||||||
runId,
|
runId,
|
||||||
@@ -448,6 +456,8 @@ async function handleSubagentStart(input: Dict) {
|
|||||||
if (sessionKey) {
|
if (sessionKey) {
|
||||||
activeSubagents.set(sessionKey, { name: agentName, spanId });
|
activeSubagents.set(sessionKey, { name: agentName, spanId });
|
||||||
state.subagent = { name: agentName, spanId };
|
state.subagent = { name: agentName, spanId };
|
||||||
|
state.spanStartTimes = state.spanStartTimes || {};
|
||||||
|
state.spanStartTimes[spanId] = Date.now();
|
||||||
if (runId) state.runId = runId;
|
if (runId) state.runId = runId;
|
||||||
saveState(sessionKey, state);
|
saveState(sessionKey, state);
|
||||||
}
|
}
|
||||||
@@ -475,8 +485,13 @@ async function handleSubagentStop(input: Dict) {
|
|||||||
const subagent = sessionKey ? (activeSubagents.get(sessionKey) || state.subagent) : undefined;
|
const subagent = sessionKey ? (activeSubagents.get(sessionKey) || state.subagent) : undefined;
|
||||||
const spanId = subagent?.spanId;
|
const spanId = subagent?.spanId;
|
||||||
const agentName = subagent?.name || pickString(input.agent, input.agent_name) || 'unknown';
|
const agentName = subagent?.name || pickString(input.agent, input.agent_name) || 'unknown';
|
||||||
const duration = pickNumber(input.duration_ms, input.elapsed_ms);
|
const startTime = spanId ? state.spanStartTimes?.[spanId] : undefined;
|
||||||
|
const duration = pickNumber(input.duration_ms, input.elapsed_ms) ?? (startTime ? Date.now() - startTime : undefined);
|
||||||
const usage = getUsage(input);
|
const usage = getUsage(input);
|
||||||
|
if (spanId && state.spanStartTimes) {
|
||||||
|
delete state.spanStartTimes[spanId];
|
||||||
|
if (sessionKey) saveState(sessionKey, state);
|
||||||
|
}
|
||||||
|
|
||||||
enqueue(buildEnvelope('span.end', sessionKey, {
|
enqueue(buildEnvelope('span.end', sessionKey, {
|
||||||
runId,
|
runId,
|
||||||
@@ -507,6 +522,8 @@ async function handleCompactStart(input: Dict) {
|
|||||||
if (sessionKey) {
|
if (sessionKey) {
|
||||||
activeSpans.set(sessionKey + ':compact', spanId);
|
activeSpans.set(sessionKey + ':compact', spanId);
|
||||||
state.compactSpanId = spanId;
|
state.compactSpanId = spanId;
|
||||||
|
state.spanStartTimes = state.spanStartTimes || {};
|
||||||
|
state.spanStartTimes[spanId] = Date.now();
|
||||||
if (runId) state.runId = runId;
|
if (runId) state.runId = runId;
|
||||||
saveState(sessionKey, state);
|
saveState(sessionKey, state);
|
||||||
}
|
}
|
||||||
@@ -529,8 +546,13 @@ async function handleCompactEnd(input: Dict) {
|
|||||||
const runId = sessionKey ? (activeRuns.get(sessionKey) || state.runId) : undefined;
|
const runId = sessionKey ? (activeRuns.get(sessionKey) || state.runId) : undefined;
|
||||||
const spanKey = sessionKey ? sessionKey + ':compact' : undefined;
|
const spanKey = sessionKey ? sessionKey + ':compact' : undefined;
|
||||||
const spanId = spanKey ? (activeSpans.get(spanKey) || state.compactSpanId) : undefined;
|
const spanId = spanKey ? (activeSpans.get(spanKey) || state.compactSpanId) : undefined;
|
||||||
const duration = pickNumber(input.duration_ms, input.elapsed_ms);
|
const startTime = spanId ? state.spanStartTimes?.[spanId] : undefined;
|
||||||
|
const duration = pickNumber(input.duration_ms, input.elapsed_ms) ?? (startTime ? Date.now() - startTime : undefined);
|
||||||
const contextWindow = getContextWindow(input);
|
const contextWindow = getContextWindow(input);
|
||||||
|
if (spanId && state.spanStartTimes) {
|
||||||
|
delete state.spanStartTimes[spanId];
|
||||||
|
if (sessionKey) saveState(sessionKey, state);
|
||||||
|
}
|
||||||
|
|
||||||
enqueue(buildEnvelope('span.end', sessionKey, {
|
enqueue(buildEnvelope('span.end', sessionKey, {
|
||||||
runId,
|
runId,
|
||||||
|
|||||||
Reference in New Issue
Block a user