@respan/cli 0.6.1 → 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.
@@ -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
- return messages.map((msg) => ({
421
- role: String(msg.role ?? "user") === "model" ? "assistant" : String(msg.role ?? "user"),
422
- content: truncate(String(msg.content ?? ""), MAX_CHARS)
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;
@@ -711,6 +721,17 @@ function processChunk(hookData) {
711
721
  }
712
722
  }
713
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
+ }
714
735
  }
715
736
  const messages = hookData.llm_request?.messages ?? [];
716
737
  const currentMsgCount = messages.length;
@@ -738,6 +759,14 @@ function processChunk(hookData) {
738
759
  state.accumulated_text += chunkText;
739
760
  state.last_tokens = completionTokens || state.last_tokens;
740
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) {
741
770
  saveStreamState(sessionId, state);
742
771
  debug(`Accumulated chunk: +${chunkText.length} chars, total=${state.accumulated_text.length}`);
743
772
  }
@@ -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
- return messages.map((msg) => ({
122
- role: String(msg.role ?? 'user') === 'model' ? 'assistant' : String(msg.role ?? 'user'),
123
- content: truncate(String(msg.content ?? ''), MAX_CHARS),
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;
@@ -438,6 +449,18 @@ function processChunk(hookData) {
438
449
  }
439
450
  }
440
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
+ }
441
464
  }
442
465
  const messages = (hookData.llm_request?.messages ?? []);
443
466
  const currentMsgCount = messages.length;
@@ -462,7 +485,7 @@ function processChunk(hookData) {
462
485
  }
463
486
  }
464
487
  state.msg_count = currentMsgCount;
465
- // Accumulate text
488
+ // Accumulate text and grounding tool details
466
489
  if (chunkText) {
467
490
  if (!state.first_chunk_time)
468
491
  state.first_chunk_time = nowISO();
@@ -470,6 +493,15 @@ function processChunk(hookData) {
470
493
  state.last_tokens = completionTokens || state.last_tokens;
471
494
  if (thoughtsTokens > 0)
472
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) {
473
505
  saveStreamState(sessionId, state);
474
506
  debug(`Accumulated chunk: +${chunkText.length} chars, total=${state.accumulated_text.length}`);
475
507
  }