Skip to content

fix: anthropic tool call issues#275

Draft
jherr wants to merge 3 commits intomainfrom
patch/anthropic-tool-calls
Draft

fix: anthropic tool call issues#275
jherr wants to merge 3 commits intomainfrom
patch/anthropic-tool-calls

Conversation

@jherr
Copy link
Contributor

@jherr jherr commented Feb 7, 2026

🎯 Changes

This fixes tool calls in the anthropic adapter.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • New Features

    • Added Claude Opus 4.6 model with adaptive thinking and effort parameter support
  • Bug Fixes

    • Improved multi-turn tool-call handling: merges consecutive user messages, deduplicates tool results, filters empty assistant/model messages, and prevents duplicate stream end events
  • Tests

    • Added end-to-end and adapter tests for multi-turn tool flows and streaming behavior
  • Chores

    • Updated example and test dependencies; added a script to fix package version bumps

@jherr jherr marked this pull request as ready for review February 7, 2026 19:13
@jherr jherr requested a review from a team February 7, 2026 19:13
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 7, 2026

📝 Walkthrough

Walkthrough

This PR enforces alternating roles and deduplicates tool/function call blocks for Anthropic and Gemini adapters, enhances Anthropic stream lifecycle signaling (tool run/start/finish and text boundaries), adds Claude Opus 4.6 model + provider options, adds tests for multi-turn tool flows, updates several example/test dependency versions, and adds a version-fix script.

Changes

Cohort / File(s) Summary
Anthropic adapter & streaming
packages/typescript/ai-anthropic/src/adapters/text.ts
Merge consecutive same-role messages, deduplicate tool_result by tool_use_id, filter empty assistant messages, add stream lifecycle tracking (hasEmittedRunFinished, currentBlockType) and emit TOOL_CALL_START/END, TEXT_MESSAGE_START/END, RUN_STARTED/FINISHED, and related step events with correct guarding.
Anthropic model & options
packages/typescript/ai-anthropic/src/model-meta.ts, packages/typescript/ai-anthropic/src/text/text-provider-options.ts
Add CLAUDE_OPUS_4_6 model metadata (200k context, 128k output, adaptive thinking, effort options) and new provider option interfaces AnthropicAdaptiveThinkingOptions and AnthropicEffortOptions.
Anthropic tests
packages/typescript/ai-anthropic/tests/anthropic-adapter.test.ts
Add comprehensive multi-turn tests (merging user messages, deduping tool results, filtering empty assistants, and stream RUN/TEXT event behavior).
Gemini adapter & tests
packages/typescript/ai-gemini/src/adapters/text.ts, packages/typescript/ai-gemini/tests/gemini-adapter.test.ts
Add merging of consecutive same-role messages, filter empty model messages, deduplicate functionResponse parts by name; add tests validating merging and dedupe behaviors.
OpenRouter instrumentation & API logging
packages/typescript/ai-openrouter/src/adapters/text.ts, testing/panel/src/routes/api.chat.ts
Add detailed ingest/debug logging around stream requests, chunk-level logging, guarded chat(...) invocation with provider-specific error reporting, and stream-return logging.
Tests: multi-turn tool flows (e2e)
testing/panel/tests/tool-flow.spec.ts
Add multi-turn follow-up test suite per tool-capable provider with extended retries/timeouts to validate tool-call + follow-up message sequencing.
Dependency bumps (examples & tests)
examples/ts-group-chat/package.json, examples/ts-react-chat/package.json, examples/ts-solid-chat/package.json, testing/panel/package.json, packages/typescript/smoke-tests/e2e/package.json
Update several @tanstack package versions (react-router, react-start, router-plugin, nitro-v2-vite-plugin) across example and testing package.json files.
Routing normalization
examples/ts-react-chat/src/routeTree.gen.ts
Normalize guitars collection route to consistently use a trailing slash in generated route typings and mappings.
Version-fix utility
scripts/fix-version-bump.ts
Add script to detect packages with version "1.0.0", infer true version from CHANGELOG entries, and patch package.json and CHANGELOG.md accordingly.
Changeset
.changeset/fix-anthropic-multi-turn-tool-calls.md
Add patch-level changeset documenting Anthropic/Gemini multi-turn/tool-call fixes.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Adapter
  participant Provider as Anthropic API
  participant Tool

  Client->>Adapter: send messages (may contain tool_use/tool_result)
  Adapter->>Adapter: mergeConsecutiveSameRoleMessages()\ndedupe tool blocks\nformatMessages()
  Adapter->>Provider: start streaming (RUN_STARTED)
  Provider-->>Adapter: stream chunks (text/content_block_start)
  Adapter->>Adapter: on content_block_start -> emit TOOL_CALL_START / TEXT_MESSAGE_START
  Provider-->>Adapter: tool content / tool_result chunks
  Adapter->>Tool: parse tool_result -> TOOL_CALL_END (include parsedInput)
  Provider-->>Adapter: content_block_stop / message_stop
  Adapter->>Adapter: emit TEXT_MESSAGE_END / STEP_FINISHED as appropriate
  Adapter->>Provider: when done -> emit RUN_FINISHED (guarded)
  Adapter-->>Client: SSE stream of lifecycle events & final content
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • AlemTuzlak

Poem

🐰 I stitched user hops and tool-run tails,
No double-finishes, no role-matching fails,
Opus 4.6 naps with effort to spare,
Streams sing tidy events through the air —
Hooray for neat logs and tests that care! 🎩

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: anthropic tool call issues' is specific and directly relates to the primary changes in this PR, which involve fixing tool call handling in the Anthropic adapter.
Description check ✅ Passed The description follows the template with all required sections completed: the Changes section describes the fix, all Checklist items are marked, and Release Impact indicates a changeset was generated as required.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch patch/anthropic-tool-calls

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/typescript/ai-anthropic/src/text/text-provider-options.ts (1)

147-156: ⚠️ Potential issue | 🔴 Critical

Fix type conflict: { type: 'adaptive' } is unassignable due to intersection with AnthropicThinkingOptions.

ExternalTextProviderOptions at line 152 intersects AnthropicThinkingOptions with Partial<AnthropicAdaptiveThinkingOptions> at line 155. Both define a thinking property with conflicting discriminated union types. TypeScript computes the intersection of these types — only discriminator variants present in BOTH unions are assignable. Since { type: 'adaptive' } exists only in AnthropicAdaptiveThinkingOptions, it cannot satisfy the intersection, preventing users from passing adaptive thinking configuration despite it being declared in the interfaces.

Replace AnthropicThinkingOptions with AnthropicAdaptiveThinkingOptions (which is a superset containing all variants including 'adaptive') to preserve type safety for all thinking modes:

Proposed fix
 export type ExternalTextProviderOptions = AnthropicContainerOptions &
   AnthropicContextManagementOptions &
   AnthropicMCPOptions &
   AnthropicServiceTierOptions &
   AnthropicStopSequencesOptions &
-  AnthropicThinkingOptions &
+  AnthropicAdaptiveThinkingOptions &
   AnthropicToolChoiceOptions &
   AnthropicSamplingOptions &
-  Partial<AnthropicAdaptiveThinkingOptions> &
   Partial<AnthropicEffortOptions>
🤖 Fix all issues with AI agents
In `@packages/typescript/ai-anthropic/src/adapters/text.ts`:
- Around line 684-695: The code emits a TEXT_MESSAGE_END for any
content_block_stop where currentBlockType !== 'tool_use', which can spuriously
include non-text types like 'thinking'; modify the guard so the yield of { type:
'TEXT_MESSAGE_END', ... } only happens when the block that just ended was
actually a text block (e.g., check currentBlockType === 'text' or explicitly
match the set of text block types) in addition to hasEmittedTextMessageStart and
accumulatedContent; update the conditional around the yield in the same block
where hasEmittedTextMessageStart, accumulatedContent, and currentBlockType are
referenced (symbols: currentBlockType, hasEmittedTextMessageStart,
accumulatedContent, and the TEXT_MESSAGE_END yield) so TEXT_MESSAGE_END is not
emitted for non-text/non-tool_use block types.

In `@testing/panel/package.json`:
- Around line 24-27: Remove the legacy package entry "@tanstack/start" from
dependencies and update the TanStack package versions to be consistent with
"@tanstack/react-start"; specifically, delete the "@tanstack/start": "^1.120.20"
line and change "@tanstack/react-router": "^1.158.4" to "^1.159.0" so it matches
"@tanstack/react-start": "^1.159.0" (leave "@tanstack/nitro-v2-vite-plugin"
unchanged unless other TanStack version constraints require alignment).
🧹 Nitpick comments (7)
scripts/fix-version-bump.ts (4)

78-78: Use Array<T> generic syntax per project lint rules.

ESLint reports @typescript-eslint/array-type violations on these two lines.

🔧 Proposed fix
-  const packagesToFix: PackageToFix[] = []
+  const packagesToFix: Array<PackageToFix> = []
-  const errors: string[] = []
+  const errors: Array<string> = []

Also applies to: 108-108


146-150: Commit message guidance uses a hardcoded placeholder.

Line 149 suggests committing with "fix: correct version bump to X.Y.Z" but at this point the actual target version is known. Consider interpolating the resolved version for a more helpful message.

🔧 Proposed fix
+  const resolvedVersion = cliVersion || packagesToFix[0].detectedVersion!
   console.log('\n✅ Done! Version bump fixed.')
   console.log('\nNext steps:')
   console.log('  1. Review the changes: git diff')
-  console.log('  2. Commit: git add -A && git commit -m "fix: correct version bump to X.Y.Z"')
+  console.log(`  2. Commit: git add -A && git commit -m "fix: correct version bump to ${resolvedVersion}"`)

14-23: CLI flag value joined with = is not handled.

parseArgs only supports --version X.Y.Z (space-separated), but common CLI convention also allows --version=X.Y.Z. Users passing the = form will silently get version: null and the script will fall back to auto-detection or error out.

🔧 Proposed fix
 function parseArgs(): { version: string | null } {
   const args = process.argv.slice(2)
-  const versionIndex = args.findIndex((arg) => arg === '--version' || arg === '-v')
-  
-  if (versionIndex !== -1 && args[versionIndex + 1]) {
-    return { version: args[versionIndex + 1] }
+  for (const arg of args) {
+    if (arg.startsWith('--version=') || arg.startsWith('-v=')) {
+      return { version: arg.split('=')[1] || null }
+    }
+  }
+  const versionIndex = args.findIndex((a) => a === '--version' || a === '-v')
+  if (versionIndex !== -1 && args[versionIndex + 1]) {
+    return { version: args[versionIndex + 1] }
   }
-  
   return { version: null }
 }

49-56: Regex-based JSON editing is fragile.

fixPackageJson uses a regex to replace the version string in raw JSON text. This works for typical package.json files but could silently corrupt files with unusual formatting or multiple "version": "1.0.0" fields (e.g., in nested metadata). Since this is a one-off internal script rather than a library, the risk is low—just worth noting.

packages/typescript/ai-anthropic/src/text/text-provider-options.ts (1)

207-217: validateThinking doesn't handle type: 'adaptive'.

The validation function only handles type: 'enabled' and would silently skip type: 'adaptive'. If adaptive thinking has any constraints (e.g., it shouldn't coexist with budget_tokens), they aren't enforced here. If no validation is needed for adaptive mode, a brief comment explaining that would help maintainability.

packages/typescript/ai-anthropic/tests/anthropic-adapter.test.ts (1)

399-511: Thorough reproduction of the real-world multi-turn scenario.

Minor note: tools: [weatherTool] (line 477) doesn't match the tool names used in the messages (getGuitars, recommendGuitar). This doesn't affect correctness since the test focuses on message formatting/deduplication, not tool dispatch — but aligning the tools would improve readability if this test is ever used as a reference for future debugging.

packages/typescript/ai-anthropic/src/adapters/text.ts (1)

696-708: hasEmittedRunFinished naming is slightly misleading for the max_tokens case.

When stop_reason is max_tokens, the code emits RUN_ERROR (not RUN_FINISHED), but the flag name hasEmittedRunFinished still prevents a follow-up RUN_FINISHED from message_stop. The behavior is correct — we shouldn't emit both — but a name like hasEmittedTerminalEvent would better express intent. Minor nit, no functional issue.

Comment on lines +684 to +695
} else {
// Emit TEXT_MESSAGE_END only for text blocks (not tool_use blocks)
if (hasEmittedTextMessageStart && accumulatedContent) {
yield {
type: 'TEXT_MESSAGE_END',
messageId,
model,
timestamp,
}
}
}

// Emit TEXT_MESSAGE_END if we had text content
if (hasEmittedTextMessageStart && accumulatedContent) {
currentBlockType = null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

TEXT_MESSAGE_END may emit spuriously for non-text, non-tool_use block types (e.g., thinking).

The else branch fires for any content_block_stop where currentBlockType !== 'tool_use', which includes thinking blocks. If a text block preceded the thinking block, this would emit a spurious TEXT_MESSAGE_END when the thinking block stops — because hasEmittedTextMessageStart and accumulatedContent are both still truthy.

In practice Anthropic puts thinking blocks before text blocks, so this isn't currently triggered. But if the response ordering changes or new block types are added, it could surface.

Suggested guard
          } else {
-            // Emit TEXT_MESSAGE_END only for text blocks (not tool_use blocks)
-            if (hasEmittedTextMessageStart && accumulatedContent) {
+            // Emit TEXT_MESSAGE_END only when a text block ends
+            if (
+              currentBlockType === 'text' &&
+              hasEmittedTextMessageStart &&
+              accumulatedContent
+            ) {
              yield {
                type: 'TEXT_MESSAGE_END',
                messageId,
                model,
                timestamp,
              }
            }
          }
🤖 Prompt for AI Agents
In `@packages/typescript/ai-anthropic/src/adapters/text.ts` around lines 684 -
695, The code emits a TEXT_MESSAGE_END for any content_block_stop where
currentBlockType !== 'tool_use', which can spuriously include non-text types
like 'thinking'; modify the guard so the yield of { type: 'TEXT_MESSAGE_END',
... } only happens when the block that just ended was actually a text block
(e.g., check currentBlockType === 'text' or explicitly match the set of text
block types) in addition to hasEmittedTextMessageStart and accumulatedContent;
update the conditional around the yield in the same block where
hasEmittedTextMessageStart, accumulatedContent, and currentBlockType are
referenced (symbols: currentBlockType, hasEmittedTextMessageStart,
accumulatedContent, and the TEXT_MESSAGE_END yield) so TEXT_MESSAGE_END is not
emitted for non-text/non-tool_use block types.

Comment on lines +24 to 27
"@tanstack/nitro-v2-vite-plugin": "^1.154.7",
"@tanstack/react-router": "^1.158.4",
"@tanstack/react-start": "^1.159.0",
"@tanstack/start": "^1.120.20",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if `@tanstack/start` is used elsewhere and what versions are pinned
rg '"@tanstack/start"' --type json -n

Repository: TanStack/ai

Length of output: 120


🌐 Web query:

@tanstack/start vs @tanstack/react-start version alignment requirements

💡 Result:

  • Don’t try to “align” @tanstack/start with @tanstack/react-start: they’re effectively different generations of TanStack Start. The current docs list React support as @tanstack/react-start (and Solid as @tanstack/solid-start) rather than using @tanstack/start directly. [2] A commonly referenced migration path is to remove @tanstack/start and install @tanstack/react-start. [3]

  • @tanstack/react-start expects same-version TanStack Router/Start internals: the package has hard-pinned dependencies (not ranges) on @tanstack/react-router and multiple Start core packages (eg @tanstack/react-router: "1.158.1" when @tanstack/react-start is 1.158.1). This means you should keep these TanStack packages on the exact same version (or let your package manager resolve them via @tanstack/react-start), otherwise you risk duplicate/mismatched installs. [1]

  • Practical rule

    • If you’re on modern Start: install/upgrade as a matched set: @tanstack/react-start == @tanstack/react-router (and any other @tanstack/*start* packages you add). [1]
    • If you still have legacy @tanstack/start in a project, treat it as a migration candidate, not something to version-match with @tanstack/react-start. [3]

Sources:
[1] @tanstack/react-start package.json (deps pinned to same version) https://app.unpkg.com/%40tanstack/react-start%401.158.1/files/package.json
[2] TanStack Start docs – supported frameworks (React uses @tanstack/react-start) https://tanstack.com/start/v0/docs/framework
[3] LogRocket migration article (remove @tanstack/start, install @tanstack/react-start) https://blog.logrocket.com/migrating-tanstack-start-vinxi-vite/


🏁 Script executed:

cat -n testing/panel/package.json

Repository: TanStack/ai

Length of output: 1958


Remove legacy @tanstack/start in favor of modern @tanstack/react-start.

The repository includes both @tanstack/start (^1.120.20) and @tanstack/react-start (^1.159.0). According to TanStack's migration guidance, @tanstack/start is a legacy package; modern React projects should use only @tanstack/react-start. Additionally, @tanstack/react-start has hard-pinned dependencies on matching versions of related TanStack packages—ensure @tanstack/react-router (currently ^1.158.4) is kept at the same version as @tanstack/react-start (^1.159.0) to avoid duplicate or mismatched installs.

🤖 Prompt for AI Agents
In `@testing/panel/package.json` around lines 24 - 27, Remove the legacy package
entry "@tanstack/start" from dependencies and update the TanStack package
versions to be consistent with "@tanstack/react-start"; specifically, delete the
"@tanstack/start": "^1.120.20" line and change "@tanstack/react-router":
"^1.158.4" to "^1.159.0" so it matches "@tanstack/react-start": "^1.159.0"
(leave "@tanstack/nitro-v2-vite-plugin" unchanged unless other TanStack version
constraints require alignment).

@nx-cloud
Copy link

nx-cloud bot commented Feb 7, 2026

🤖 Nx Cloud AI Fix Eligible

An automatically generated fix could have helped fix failing tasks for this run, but Self-healing CI is disabled for this workspace. Visit workspace settings to enable it and get automatic fixes in future runs.

To disable these notifications, a workspace admin can disable them in workspace settings.


View your CI Pipeline Execution ↗ for commit 6842032

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ❌ Failed 3m 6s View ↗
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 1m 14s View ↗

☁️ Nx Cloud last updated this comment at 2026-02-07 21:24:37 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 7, 2026

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@275

@tanstack/ai-anthropic

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-anthropic@275

@tanstack/ai-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@275

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-devtools-core@275

@tanstack/ai-gemini

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini@275

@tanstack/ai-grok

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok@275

@tanstack/ai-ollama

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-ollama@275

@tanstack/ai-openai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@275

@tanstack/ai-openrouter

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openrouter@275

@tanstack/ai-preact

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-preact@275

@tanstack/ai-react

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@275

@tanstack/ai-react-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react-ui@275

@tanstack/ai-solid

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid@275

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid-ui@275

@tanstack/ai-svelte

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-svelte@275

@tanstack/ai-vue

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue@275

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue-ui@275

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/preact-ai-devtools@275

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/react-ai-devtools@275

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/solid-ai-devtools@275

commit: 6842032

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/typescript/ai-openrouter/src/adapters/text.ts`:
- Around line 114-138: Remove all local debug instrumentation in the chatStream
flow: delete the fetch-based "agent log" blocks and their surrounding
//#region...//#endregion comments inside the chatStream implementation, and
remove the related chunkCountOuter and chunkCount tracking variables; ensure any
references to these variables are also removed or replaced. Specifically, inside
the chatStream method (and any helper logic that calls mapTextOptionsToSDK)
remove the hardcoded fetch calls to 127.0.0.1:7244/ingest and the comment
regions, then run the linter/tests to ensure no leftover references to
chunkCountOuter, chunkCount, or the debug blocks remain.

In `@testing/panel/src/routes/api.chat.ts`:
- Around line 152-171: Remove all temporary debug "agent log" blocks that
perform fire-and-forget fetches to http://127.0.0.1:7244/ingest/... within this
file; specifically delete each // `#region` agent log ... // `#endregion` block (the
POST handler entry block around the POST handler, and the similar blocks near
lines referenced) so no hardcoded fetches or leaked internal data remain; search
for the exact region marker string "// `#region` agent log" and the fetch URL to
find every occurrence (e.g., the block inside the POST handler that logs
'[DEBUG] POST /api/chat handler entered') and remove them, and if persistent
telemetry is required replace them with calls to the app’s structured
logging/observability API instead of direct fetches.
🧹 Nitpick comments (4)
scripts/fix-version-bump.ts (2)

77-77: Use Array<T> generic syntax per project ESLint config.

The project's @typescript-eslint/array-type rule requires the generic form.

♻️ Proposed fix
-  const packagesToFix: PackageToFix[] = []
+  const packagesToFix: Array<PackageToFix> = []
-  const errors: string[] = []
+  const errors: Array<string> = []

Also applies to: 110-110


14-25: Consider validating the CLI-provided version string.

If a user passes a non-semver string (e.g., --version oops), it will be written verbatim into package.json and CHANGELOG.md. A simple regex check would guard against typos.

🛡️ Suggested guard
 function parseArgs(): { version: string | null } {
   const args = process.argv.slice(2)
   const versionIndex = args.findIndex(
     (arg) => arg === '--version' || arg === '-v',
   )

   if (versionIndex !== -1 && args[versionIndex + 1]) {
-    return { version: args[versionIndex + 1] }
+    const version = args[versionIndex + 1]
+    if (!/^\d+\.\d+\.\d+/.test(version)) {
+      console.error(`❌ Invalid version format: ${version}`)
+      process.exit(1)
+    }
+    return { version }
   }

   return { version: null }
 }
testing/panel/src/routes/api.chat.ts (1)

267-294: Wrapping chat() in try/catch is fine, but the catch only re-throws.

Without the debug logging (which should be removed), the inner try/catch on lines 268–319 serves no purpose — it catches chatError and immediately re-throws it, which the outer catch on line 345 already handles.

If the intent is to add provider-specific error handling or transformation, implement that logic here; otherwise, remove the redundant try/catch to keep the code straightforward.

Proposed simplification (after removing debug logging)
-          let stream: AsyncIterable<any>
-          try {
-            stream = chat({
+          const stream = chat({
               ...options,
               adapter,
               tools: [
                 getGuitars,
                 recommendGuitarToolDef,
                 addToCartToolServer,
                 addToWishListToolDef,
                 getPersonalGuitarPreferenceToolDef,
               ],
               systemPrompts: [SYSTEM_PROMPT],
               agentLoopStrategy: maxIterations(20),
               messages,
               modelOptions: {},
               abortController,
-            })
-          } catch (chatError: any) {
-            throw chatError
-          }
+          })
packages/typescript/ai-openrouter/src/adapters/text.ts (1)

139-168: The inner try/catch around chat.send is a good pattern — keep it, but clean it up.

Wrapping the await this.client.chat.send(...) call in its own try/catch (lines 140–168) is useful for distinguishing SDK initialization errors from stream-processing errors. After removing the debug logging, simplify to just re-throw or add meaningful error wrapping:

Proposed cleanup
       let stream: AsyncIterable<any>
       try {
         stream = await this.client.chat.send(
           { ...requestParams, stream: true },
           { signal: options.request?.signal },
         )
       } catch (sendError: any) {
-        // `#region` agent log
-        fetch(
-          'http://127.0.0.1:7244/ingest/830522ab-8098-40b9-a021-890f9c041588',
-          { ... },
-        ).catch(() => {})
-        // `#endregion`
         throw sendError
       }

Comment on lines +114 to +138
let chunkCountOuter = 0
try {
const requestParams = this.mapTextOptionsToSDK(options)
const stream = await this.client.chat.send(
{ ...requestParams, stream: true },
{ signal: options.request?.signal },
)
// #region agent log
fetch(
'http://127.0.0.1:7244/ingest/830522ab-8098-40b9-a021-890f9c041588',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
location: 'openrouter/text.ts:chatStream',
message: 'chatStream entry - request params',
data: {
model: requestParams.model,
messageCount: requestParams.messages?.length,
hasTools: !!requestParams.tools,
toolCount: requestParams.tools?.length,
keys: Object.keys(requestParams),
},
timestamp: Date.now(),
hypothesisId: 'C',
}),
},
).catch(() => {})
// #endregion
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Remove all debug instrumentation from this published package.

This file is part of @tanstack/ai-openrouter — a published npm package. The hardcoded fetch calls to http://127.0.0.1:7244/ingest/... would execute for every consumer of this adapter. Even though they're fire-and-forget with .catch(() => {}), they:

  1. Generate unnecessary network requests on every chatStream call and on every streamed chunk (lines 176–202, 235–262)
  2. Leak internal diagnostic data (error stacks, model params, content lengths) to a localhost endpoint
  3. Introduce latency and resource overhead in production

All // #regionagent log … //#endregion`` blocks (lines 117–138, 146–166, 174–203, 233–263, 287–308, 310–330) and the supporting chunkCountOuter / `chunkCount` tracking variables (lines 114, 170, 172–173) that exist solely for this logging must be removed before merge.

🤖 Prompt for AI Agents
In `@packages/typescript/ai-openrouter/src/adapters/text.ts` around lines 114 -
138, Remove all local debug instrumentation in the chatStream flow: delete the
fetch-based "agent log" blocks and their surrounding //#region...//#endregion
comments inside the chatStream implementation, and remove the related
chunkCountOuter and chunkCount tracking variables; ensure any references to
these variables are also removed or replaced. Specifically, inside the
chatStream method (and any helper logic that calls mapTextOptionsToSDK) remove
the hardcoded fetch calls to 127.0.0.1:7244/ingest and the comment regions, then
run the linter/tests to ensure no leftover references to chunkCountOuter,
chunkCount, or the debug blocks remain.

Comment on lines +152 to +171
// #region agent log
console.log(
'[DEBUG] POST /api/chat handler entered at',
new Date().toISOString(),
)
fetch(
'http://127.0.0.1:7244/ingest/830522ab-8098-40b9-a021-890f9c041588',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
location: 'api.chat.ts:POST:ENTRY',
message: 'POST handler entered',
data: { url: request.url, method: request.method },
timestamp: Date.now(),
hypothesisId: 'ENTRY0',
}),
},
).catch(() => {})
// #endregion
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove debug instrumentation before merging to main.

These fire-and-forget fetch calls to a hardcoded http://127.0.0.1:7244/ingest/... endpoint are clearly temporary debugging artifacts. They appear in multiple regions throughout this file (lines 152–171, 226–249, 295–319, 321–342). Shipping them to main adds dead code that will silently fail in any environment without that local service, clutters the handler, and leaks internal details (e.g. errorStack) to an uncontrolled endpoint.

Please strip all // #regionagent log … //#endregion`` blocks from this file before merge. If persistent telemetry is desired, consider a proper structured logging or observability solution.

🤖 Prompt for AI Agents
In `@testing/panel/src/routes/api.chat.ts` around lines 152 - 171, Remove all
temporary debug "agent log" blocks that perform fire-and-forget fetches to
http://127.0.0.1:7244/ingest/... within this file; specifically delete each //
`#region` agent log ... // `#endregion` block (the POST handler entry block around
the POST handler, and the similar blocks near lines referenced) so no hardcoded
fetches or leaked internal data remain; search for the exact region marker
string "// `#region` agent log" and the fetch URL to find every occurrence (e.g.,
the block inside the POST handler that logs '[DEBUG] POST /api/chat handler
entered') and remove them, and if persistent telemetry is required replace them
with calls to the app’s structured logging/observability API instead of direct
fetches.

@jherr jherr marked this pull request as draft February 7, 2026 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants