@respan/cli 0.6.0 → 0.6.2
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/hooks/claude-code.cjs +2 -2
- package/dist/hooks/claude-code.js +2 -2
- package/dist/hooks/codex-cli.cjs +16 -5
- package/dist/hooks/codex-cli.js +17 -5
- package/dist/hooks/gemini-cli.cjs +41 -11
- package/dist/hooks/gemini-cli.js +46 -12
- package/oclif.manifest.json +820 -820
- package/package.json +1 -1
|
@@ -607,7 +607,7 @@ function createSpans(sessionId, turnNum, userMsg, assistantMsgs, toolResults, co
|
|
|
607
607
|
provider_id: "",
|
|
608
608
|
span_path: "",
|
|
609
609
|
input: promptMessages.length ? JSON.stringify(promptMessages) : "",
|
|
610
|
-
output:
|
|
610
|
+
output: finalOutput,
|
|
611
611
|
timestamp: timestampStr,
|
|
612
612
|
start_time: startTimeStr,
|
|
613
613
|
metadata,
|
|
@@ -627,7 +627,7 @@ function createSpans(sessionId, turnNum, userMsg, assistantMsgs, toolResults, co
|
|
|
627
627
|
provider_id: "anthropic",
|
|
628
628
|
metadata: {},
|
|
629
629
|
input: promptMessages.length ? JSON.stringify(promptMessages) : "",
|
|
630
|
-
output:
|
|
630
|
+
output: finalOutput,
|
|
631
631
|
prompt_messages: promptMessages,
|
|
632
632
|
completion_message: completionMessage,
|
|
633
633
|
timestamp: genEnd,
|
|
@@ -256,7 +256,7 @@ function createSpans(sessionId, turnNum, userMsg, assistantMsgs, toolResults, co
|
|
|
256
256
|
provider_id: '',
|
|
257
257
|
span_path: '',
|
|
258
258
|
input: promptMessages.length ? JSON.stringify(promptMessages) : '',
|
|
259
|
-
output:
|
|
259
|
+
output: finalOutput,
|
|
260
260
|
timestamp: timestampStr,
|
|
261
261
|
start_time: startTimeStr,
|
|
262
262
|
metadata,
|
|
@@ -277,7 +277,7 @@ function createSpans(sessionId, turnNum, userMsg, assistantMsgs, toolResults, co
|
|
|
277
277
|
provider_id: 'anthropic',
|
|
278
278
|
metadata: {},
|
|
279
279
|
input: promptMessages.length ? JSON.stringify(promptMessages) : '',
|
|
280
|
-
output:
|
|
280
|
+
output: finalOutput,
|
|
281
281
|
prompt_messages: promptMessages,
|
|
282
282
|
completion_message: completionMessage,
|
|
283
283
|
timestamp: genEnd,
|
package/dist/hooks/codex-cli.cjs
CHANGED
|
@@ -475,6 +475,7 @@ function extractTurns(events) {
|
|
|
475
475
|
tool_calls: [],
|
|
476
476
|
tool_outputs: [],
|
|
477
477
|
reasoning: false,
|
|
478
|
+
reasoning_text: "",
|
|
478
479
|
token_usage: {}
|
|
479
480
|
};
|
|
480
481
|
} else if (msgType === "task_complete" && current) {
|
|
@@ -512,13 +513,23 @@ function extractTurns(events) {
|
|
|
512
513
|
timestamp
|
|
513
514
|
});
|
|
514
515
|
} else if (itemType === "reasoning") {
|
|
515
|
-
if (current)
|
|
516
|
+
if (current) {
|
|
517
|
+
current.reasoning = true;
|
|
518
|
+
const summary = String(payload.summary ?? payload.text ?? "");
|
|
519
|
+
if (summary) current.reasoning_text += (current.reasoning_text ? "\n" : "") + summary;
|
|
520
|
+
}
|
|
516
521
|
} else if (itemType === "web_search_call") {
|
|
517
522
|
const action = payload.action ?? {};
|
|
523
|
+
const syntheticId = `web_search_${timestamp}`;
|
|
518
524
|
current.tool_calls.push({
|
|
519
525
|
name: "web_search",
|
|
520
526
|
arguments: JSON.stringify({ query: action.query ?? "" }),
|
|
521
|
-
call_id:
|
|
527
|
+
call_id: syntheticId,
|
|
528
|
+
timestamp
|
|
529
|
+
});
|
|
530
|
+
current.tool_outputs.push({
|
|
531
|
+
call_id: syntheticId,
|
|
532
|
+
output: `Search: ${action.query ?? ""}`,
|
|
522
533
|
timestamp
|
|
523
534
|
});
|
|
524
535
|
}
|
|
@@ -570,7 +581,7 @@ function createSpans(sessionId, turnNum, turn, config) {
|
|
|
570
581
|
provider_id: "",
|
|
571
582
|
span_path: "",
|
|
572
583
|
input: promptMessages.length ? JSON.stringify(promptMessages) : "",
|
|
573
|
-
output:
|
|
584
|
+
output: turn.assistant_message,
|
|
574
585
|
timestamp: endTimeStr,
|
|
575
586
|
start_time: startTimeStr,
|
|
576
587
|
metadata,
|
|
@@ -587,7 +598,7 @@ function createSpans(sessionId, turnNum, turn, config) {
|
|
|
587
598
|
provider_id: "openai",
|
|
588
599
|
metadata: {},
|
|
589
600
|
input: promptMessages.length ? JSON.stringify(promptMessages) : "",
|
|
590
|
-
output:
|
|
601
|
+
output: turn.assistant_message,
|
|
591
602
|
prompt_messages: promptMessages,
|
|
592
603
|
completion_message: completionMessage,
|
|
593
604
|
timestamp: endTimeStr,
|
|
@@ -607,7 +618,7 @@ function createSpans(sessionId, turnNum, turn, config) {
|
|
|
607
618
|
provider_id: "",
|
|
608
619
|
metadata: reasoningTokens > 0 ? { reasoning_tokens: reasoningTokens } : {},
|
|
609
620
|
input: "",
|
|
610
|
-
output: reasoningTokens > 0 ? `[Reasoning: ${reasoningTokens} tokens]` : "[Reasoning]",
|
|
621
|
+
output: turn.reasoning_text || (reasoningTokens > 0 ? `[Reasoning: ${reasoningTokens} tokens]` : "[Reasoning]"),
|
|
611
622
|
timestamp: endTimeStr,
|
|
612
623
|
start_time: startTimeStr
|
|
613
624
|
});
|
package/dist/hooks/codex-cli.js
CHANGED
|
@@ -99,6 +99,7 @@ function extractTurns(events) {
|
|
|
99
99
|
tool_calls: [],
|
|
100
100
|
tool_outputs: [],
|
|
101
101
|
reasoning: false,
|
|
102
|
+
reasoning_text: '',
|
|
102
103
|
token_usage: {},
|
|
103
104
|
};
|
|
104
105
|
}
|
|
@@ -147,15 +148,26 @@ function extractTurns(events) {
|
|
|
147
148
|
});
|
|
148
149
|
}
|
|
149
150
|
else if (itemType === 'reasoning') {
|
|
150
|
-
if (current)
|
|
151
|
+
if (current) {
|
|
151
152
|
current.reasoning = true;
|
|
153
|
+
const summary = String(payload.summary ?? payload.text ?? '');
|
|
154
|
+
if (summary)
|
|
155
|
+
current.reasoning_text += (current.reasoning_text ? '\n' : '') + summary;
|
|
156
|
+
}
|
|
152
157
|
}
|
|
153
158
|
else if (itemType === 'web_search_call') {
|
|
154
159
|
const action = (payload.action ?? {});
|
|
160
|
+
const syntheticId = `web_search_${timestamp}`;
|
|
155
161
|
current.tool_calls.push({
|
|
156
162
|
name: 'web_search',
|
|
157
163
|
arguments: JSON.stringify({ query: action.query ?? '' }),
|
|
158
|
-
call_id:
|
|
164
|
+
call_id: syntheticId,
|
|
165
|
+
timestamp,
|
|
166
|
+
});
|
|
167
|
+
// Web search has no separate output event; record query as output
|
|
168
|
+
current.tool_outputs.push({
|
|
169
|
+
call_id: syntheticId,
|
|
170
|
+
output: `Search: ${action.query ?? ''}`,
|
|
159
171
|
timestamp,
|
|
160
172
|
});
|
|
161
173
|
}
|
|
@@ -218,7 +230,7 @@ function createSpans(sessionId, turnNum, turn, config) {
|
|
|
218
230
|
provider_id: '',
|
|
219
231
|
span_path: '',
|
|
220
232
|
input: promptMessages.length ? JSON.stringify(promptMessages) : '',
|
|
221
|
-
output:
|
|
233
|
+
output: turn.assistant_message,
|
|
222
234
|
timestamp: endTimeStr,
|
|
223
235
|
start_time: startTimeStr,
|
|
224
236
|
metadata,
|
|
@@ -236,7 +248,7 @@ function createSpans(sessionId, turnNum, turn, config) {
|
|
|
236
248
|
provider_id: 'openai',
|
|
237
249
|
metadata: {},
|
|
238
250
|
input: promptMessages.length ? JSON.stringify(promptMessages) : '',
|
|
239
|
-
output:
|
|
251
|
+
output: turn.assistant_message,
|
|
240
252
|
prompt_messages: promptMessages,
|
|
241
253
|
completion_message: completionMessage,
|
|
242
254
|
timestamp: endTimeStr,
|
|
@@ -257,7 +269,7 @@ function createSpans(sessionId, turnNum, turn, config) {
|
|
|
257
269
|
provider_id: '',
|
|
258
270
|
metadata: reasoningTokens > 0 ? { reasoning_tokens: reasoningTokens } : {},
|
|
259
271
|
input: '',
|
|
260
|
-
output: reasoningTokens > 0 ? `[Reasoning: ${reasoningTokens} tokens]` : '[Reasoning]',
|
|
272
|
+
output: turn.reasoning_text || (reasoningTokens > 0 ? `[Reasoning: ${reasoningTokens} tokens]` : '[Reasoning]'),
|
|
261
273
|
timestamp: endTimeStr,
|
|
262
274
|
start_time: startTimeStr,
|
|
263
275
|
});
|
|
@@ -417,10 +417,20 @@ function clearStreamState(sessionId) {
|
|
|
417
417
|
function extractMessages(hookData) {
|
|
418
418
|
const llmReq = hookData.llm_request ?? {};
|
|
419
419
|
const messages = llmReq.messages ?? [];
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
420
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
421
|
+
const role = String(messages[i].role ?? "user");
|
|
422
|
+
if (role === "user") {
|
|
423
|
+
return [{ role: "user", content: truncate(String(messages[i].content ?? ""), MAX_CHARS) }];
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (messages.length > 0) {
|
|
427
|
+
const last = messages[messages.length - 1];
|
|
428
|
+
return [{
|
|
429
|
+
role: String(last.role ?? "user") === "model" ? "assistant" : String(last.role ?? "user"),
|
|
430
|
+
content: truncate(String(last.content ?? ""), MAX_CHARS)
|
|
431
|
+
}];
|
|
432
|
+
}
|
|
433
|
+
return [];
|
|
424
434
|
}
|
|
425
435
|
function detectModel(hookData) {
|
|
426
436
|
const override = process.env.RESPAN_GEMINI_MODEL;
|
|
@@ -443,8 +453,9 @@ function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurn
|
|
|
443
453
|
spanName: "gemini-cli"
|
|
444
454
|
});
|
|
445
455
|
const safeId = sessionId.replace(/[/\\]/g, "_").slice(0, 50);
|
|
446
|
-
const
|
|
447
|
-
const
|
|
456
|
+
const turnTs = beginTime.replace(/[^0-9]/g, "").slice(0, 14);
|
|
457
|
+
const traceUniqueId = `gcli_${safeId}_${turnTs}`;
|
|
458
|
+
const rootSpanId = `gcli_${safeId}_${turnTs}_root`;
|
|
448
459
|
const threadId = `gcli_${sessionId}`;
|
|
449
460
|
const llmReq = hookData.llm_request ?? {};
|
|
450
461
|
const reqConfig = llmReq.config ?? {};
|
|
@@ -463,7 +474,7 @@ function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurn
|
|
|
463
474
|
provider_id: "",
|
|
464
475
|
span_path: "",
|
|
465
476
|
input: promptMessages.length ? JSON.stringify(promptMessages) : "",
|
|
466
|
-
output:
|
|
477
|
+
output: truncate(outputText, MAX_CHARS),
|
|
467
478
|
timestamp: endTime,
|
|
468
479
|
start_time: beginTime,
|
|
469
480
|
metadata,
|
|
@@ -471,7 +482,7 @@ function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurn
|
|
|
471
482
|
});
|
|
472
483
|
const genSpan = {
|
|
473
484
|
trace_unique_id: traceUniqueId,
|
|
474
|
-
span_unique_id: `gcli_${safeId}_gen`,
|
|
485
|
+
span_unique_id: `gcli_${safeId}_${turnTs}_gen`,
|
|
475
486
|
span_parent_id: rootSpanId,
|
|
476
487
|
span_name: "gemini.chat",
|
|
477
488
|
span_workflow_name: workflowName,
|
|
@@ -480,7 +491,7 @@ function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurn
|
|
|
480
491
|
provider_id: "google",
|
|
481
492
|
metadata: {},
|
|
482
493
|
input: promptMessages.length ? JSON.stringify(promptMessages) : "",
|
|
483
|
-
output:
|
|
494
|
+
output: truncate(outputText, MAX_CHARS),
|
|
484
495
|
timestamp: endTime,
|
|
485
496
|
start_time: beginTime,
|
|
486
497
|
prompt_tokens: tokens.prompt_tokens,
|
|
@@ -494,7 +505,7 @@ function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurn
|
|
|
494
505
|
if (thoughtsTokens > 0) {
|
|
495
506
|
spans.push({
|
|
496
507
|
trace_unique_id: traceUniqueId,
|
|
497
|
-
span_unique_id: `gcli_${safeId}_reasoning`,
|
|
508
|
+
span_unique_id: `gcli_${safeId}_${turnTs}_reasoning`,
|
|
498
509
|
span_parent_id: rootSpanId,
|
|
499
510
|
span_name: "Reasoning",
|
|
500
511
|
span_workflow_name: workflowName,
|
|
@@ -522,7 +533,7 @@ function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurn
|
|
|
522
533
|
const toolLat = latencySeconds(toolStart, toolEnd);
|
|
523
534
|
spans.push({
|
|
524
535
|
trace_unique_id: traceUniqueId,
|
|
525
|
-
span_unique_id: `gcli_${safeId}_tool_${i + 1}`,
|
|
536
|
+
span_unique_id: `gcli_${safeId}_${turnTs}_tool_${i + 1}`,
|
|
526
537
|
span_parent_id: rootSpanId,
|
|
527
538
|
span_name: `Tool: ${displayName}`,
|
|
528
539
|
span_workflow_name: workflowName,
|
|
@@ -710,6 +721,17 @@ function processChunk(hookData) {
|
|
|
710
721
|
}
|
|
711
722
|
}
|
|
712
723
|
}
|
|
724
|
+
const grounding = candidates[0].groundingMetadata ?? llmResp.groundingMetadata;
|
|
725
|
+
if (grounding && typeof grounding === "object") {
|
|
726
|
+
const queries = grounding.webSearchQueries ?? grounding.searchQueries ?? [];
|
|
727
|
+
if (queries.length > 0) {
|
|
728
|
+
chunkToolDetails.push({
|
|
729
|
+
name: "google_web_search",
|
|
730
|
+
args: { queries },
|
|
731
|
+
output: truncate(queries.join(", "), MAX_CHARS)
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
}
|
|
713
735
|
}
|
|
714
736
|
const messages = hookData.llm_request?.messages ?? [];
|
|
715
737
|
const currentMsgCount = messages.length;
|
|
@@ -737,6 +759,14 @@ function processChunk(hookData) {
|
|
|
737
759
|
state.accumulated_text += chunkText;
|
|
738
760
|
state.last_tokens = completionTokens || state.last_tokens;
|
|
739
761
|
if (thoughtsTokens > 0) state.thoughts_tokens = thoughtsTokens;
|
|
762
|
+
}
|
|
763
|
+
const groundingDetails = chunkToolDetails.filter((d) => d.name === "google_web_search");
|
|
764
|
+
if (groundingDetails.length) {
|
|
765
|
+
state.tool_details = [...state.tool_details ?? [], ...groundingDetails];
|
|
766
|
+
state.tool_turns = (state.tool_turns ?? 0) + groundingDetails.length;
|
|
767
|
+
debug(`Grounding search detected: ${groundingDetails.length} queries`);
|
|
768
|
+
}
|
|
769
|
+
if (chunkText || groundingDetails.length) {
|
|
740
770
|
saveStreamState(sessionId, state);
|
|
741
771
|
debug(`Accumulated chunk: +${chunkText.length} chars, total=${state.accumulated_text.length}`);
|
|
742
772
|
}
|
package/dist/hooks/gemini-cli.js
CHANGED
|
@@ -118,10 +118,21 @@ function clearStreamState(sessionId) {
|
|
|
118
118
|
function extractMessages(hookData) {
|
|
119
119
|
const llmReq = (hookData.llm_request ?? {});
|
|
120
120
|
const messages = (llmReq.messages ?? []);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
// Only include the last user message, not the full conversation history
|
|
122
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
123
|
+
const role = String(messages[i].role ?? 'user');
|
|
124
|
+
if (role === 'user') {
|
|
125
|
+
return [{ role: 'user', content: truncate(String(messages[i].content ?? ''), MAX_CHARS) }];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (messages.length > 0) {
|
|
129
|
+
const last = messages[messages.length - 1];
|
|
130
|
+
return [{
|
|
131
|
+
role: String(last.role ?? 'user') === 'model' ? 'assistant' : String(last.role ?? 'user'),
|
|
132
|
+
content: truncate(String(last.content ?? ''), MAX_CHARS),
|
|
133
|
+
}];
|
|
134
|
+
}
|
|
135
|
+
return [];
|
|
125
136
|
}
|
|
126
137
|
function detectModel(hookData) {
|
|
127
138
|
const override = process.env.RESPAN_GEMINI_MODEL;
|
|
@@ -146,8 +157,10 @@ function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurn
|
|
|
146
157
|
spanName: 'gemini-cli',
|
|
147
158
|
});
|
|
148
159
|
const safeId = sessionId.replace(/[/\\]/g, '_').slice(0, 50);
|
|
149
|
-
|
|
150
|
-
const
|
|
160
|
+
// Use first chunk timestamp to differentiate turns within the same session
|
|
161
|
+
const turnTs = beginTime.replace(/[^0-9]/g, '').slice(0, 14);
|
|
162
|
+
const traceUniqueId = `gcli_${safeId}_${turnTs}`;
|
|
163
|
+
const rootSpanId = `gcli_${safeId}_${turnTs}_root`;
|
|
151
164
|
const threadId = `gcli_${sessionId}`;
|
|
152
165
|
// LLM config
|
|
153
166
|
const llmReq = (hookData.llm_request ?? {});
|
|
@@ -171,7 +184,7 @@ function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurn
|
|
|
171
184
|
provider_id: '',
|
|
172
185
|
span_path: '',
|
|
173
186
|
input: promptMessages.length ? JSON.stringify(promptMessages) : '',
|
|
174
|
-
output:
|
|
187
|
+
output: truncate(outputText, MAX_CHARS),
|
|
175
188
|
timestamp: endTime,
|
|
176
189
|
start_time: beginTime,
|
|
177
190
|
metadata,
|
|
@@ -180,7 +193,7 @@ function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurn
|
|
|
180
193
|
// Generation child span
|
|
181
194
|
const genSpan = {
|
|
182
195
|
trace_unique_id: traceUniqueId,
|
|
183
|
-
span_unique_id: `gcli_${safeId}_gen`,
|
|
196
|
+
span_unique_id: `gcli_${safeId}_${turnTs}_gen`,
|
|
184
197
|
span_parent_id: rootSpanId,
|
|
185
198
|
span_name: 'gemini.chat',
|
|
186
199
|
span_workflow_name: workflowName,
|
|
@@ -189,7 +202,7 @@ function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurn
|
|
|
189
202
|
provider_id: 'google',
|
|
190
203
|
metadata: {},
|
|
191
204
|
input: promptMessages.length ? JSON.stringify(promptMessages) : '',
|
|
192
|
-
output:
|
|
205
|
+
output: truncate(outputText, MAX_CHARS),
|
|
193
206
|
timestamp: endTime,
|
|
194
207
|
start_time: beginTime,
|
|
195
208
|
prompt_tokens: tokens.prompt_tokens,
|
|
@@ -206,7 +219,7 @@ function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurn
|
|
|
206
219
|
if (thoughtsTokens > 0) {
|
|
207
220
|
spans.push({
|
|
208
221
|
trace_unique_id: traceUniqueId,
|
|
209
|
-
span_unique_id: `gcli_${safeId}_reasoning`,
|
|
222
|
+
span_unique_id: `gcli_${safeId}_${turnTs}_reasoning`,
|
|
210
223
|
span_parent_id: rootSpanId,
|
|
211
224
|
span_name: 'Reasoning',
|
|
212
225
|
span_workflow_name: workflowName,
|
|
@@ -237,7 +250,7 @@ function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurn
|
|
|
237
250
|
const toolLat = latencySeconds(toolStart, toolEnd);
|
|
238
251
|
spans.push({
|
|
239
252
|
trace_unique_id: traceUniqueId,
|
|
240
|
-
span_unique_id: `gcli_${safeId}_tool_${i + 1}`,
|
|
253
|
+
span_unique_id: `gcli_${safeId}_${turnTs}_tool_${i + 1}`,
|
|
241
254
|
span_parent_id: rootSpanId,
|
|
242
255
|
span_name: `Tool: ${displayName}`,
|
|
243
256
|
span_workflow_name: workflowName,
|
|
@@ -436,6 +449,18 @@ function processChunk(hookData) {
|
|
|
436
449
|
}
|
|
437
450
|
}
|
|
438
451
|
}
|
|
452
|
+
// Detect server-side grounding (google_web_search) from groundingMetadata
|
|
453
|
+
const grounding = (candidates[0].groundingMetadata ?? llmResp.groundingMetadata);
|
|
454
|
+
if (grounding && typeof grounding === 'object') {
|
|
455
|
+
const queries = (grounding.webSearchQueries ?? grounding.searchQueries ?? []);
|
|
456
|
+
if (queries.length > 0) {
|
|
457
|
+
chunkToolDetails.push({
|
|
458
|
+
name: 'google_web_search',
|
|
459
|
+
args: { queries },
|
|
460
|
+
output: truncate(queries.join(', '), MAX_CHARS),
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
439
464
|
}
|
|
440
465
|
const messages = (hookData.llm_request?.messages ?? []);
|
|
441
466
|
const currentMsgCount = messages.length;
|
|
@@ -460,7 +485,7 @@ function processChunk(hookData) {
|
|
|
460
485
|
}
|
|
461
486
|
}
|
|
462
487
|
state.msg_count = currentMsgCount;
|
|
463
|
-
// Accumulate text
|
|
488
|
+
// Accumulate text and grounding tool details
|
|
464
489
|
if (chunkText) {
|
|
465
490
|
if (!state.first_chunk_time)
|
|
466
491
|
state.first_chunk_time = nowISO();
|
|
@@ -468,6 +493,15 @@ function processChunk(hookData) {
|
|
|
468
493
|
state.last_tokens = completionTokens || state.last_tokens;
|
|
469
494
|
if (thoughtsTokens > 0)
|
|
470
495
|
state.thoughts_tokens = thoughtsTokens;
|
|
496
|
+
}
|
|
497
|
+
// Save grounding tool details (these arrive with text, not as separate tool turns)
|
|
498
|
+
const groundingDetails = chunkToolDetails.filter(d => d.name === 'google_web_search');
|
|
499
|
+
if (groundingDetails.length) {
|
|
500
|
+
state.tool_details = [...(state.tool_details ?? []), ...groundingDetails];
|
|
501
|
+
state.tool_turns = (state.tool_turns ?? 0) + groundingDetails.length;
|
|
502
|
+
debug(`Grounding search detected: ${groundingDetails.length} queries`);
|
|
503
|
+
}
|
|
504
|
+
if (chunkText || groundingDetails.length) {
|
|
471
505
|
saveStreamState(sessionId, state);
|
|
472
506
|
debug(`Accumulated chunk: +${chunkText.length} chars, total=${state.accumulated_text.length}`);
|
|
473
507
|
}
|