Repository navigation
feat(anthropic): add Claude subscription support with OAuth 2.0 authentication - #844
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
✅ Single Commit Policy - COMPLIANTStatus: Policy requirements met • 1 commit • Valid format • Ready for merge 📊 View validation details📝 Commit Details
✅ Validation Results
🤖 Automated validation by NeuroLink Single Commit Enforcement |
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThis PR introduces Claude Subscription Support with OAuth 2.0 PKCE authentication for Anthropic. It adds OAuth token management, tier-based model access (free/pro/max/api), CLI authentication commands, secure token storage, and comprehensive documentation covering setup and usage patterns. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI as CLI Auth Command
participant OAuth as AnthropicOAuth
participant Browser as Browser/Callback
participant OAuthServer as Anthropic OAuth Server
participant TokenStore as TokenStore
participant Provider as AnthropicProvider
User->>CLI: login --method oauth
CLI->>OAuth: performOAuthFlow()
OAuth->>OAuth: generatePKCE()
OAuth->>OAuthServer: generateAuthUrl(codeChallenge, state)
OAuthServer-->>OAuth: authorizationUrl
OAuth->>Browser: Launch authorization URL
User->>Browser: Authorize & grant consent
Browser->>OAuthServer: Redirect with authCode
OAuthServer-->>Browser: Callback to localhost
Browser->>OAuth: Callback handler receives code
OAuth->>OAuthServer: exchangeCodeForTokens(code, codeVerifier)
OAuthServer-->>OAuth: accessToken, refreshToken, expiresAt
OAuth->>TokenStore: saveTokens(provider, tokens)
TokenStore-->>OAuth: Tokens stored securely
CLI-->>User: Login successful
User->>CLI: generate --provider anthropic-subscription
CLI->>Provider: new AnthropicProvider(config)
Provider->>Provider: resolveAuthToken()
Provider->>TokenStore: getValidToken(provider)
TokenStore->>TokenStore: Check token expiry
alt Token expired
TokenStore->>OAuthServer: refreshAccessToken(refreshToken)
OAuthServer-->>TokenStore: newAccessToken, newRefreshToken
TokenStore->>TokenStore: saveTokens(provider, updated)
end
TokenStore-->>Provider: validAccessToken
Provider->>Provider: validateModelAccess(model, tier)
Provider-->>CLI: Generate response
sequenceDiagram
participant App as Application
participant Provider as AnthropicProvider
participant TokenStore as TokenStore
participant OAuthServer as Anthropic OAuth
participant Anthropic as Anthropic API
App->>Provider: generate(prompt, {model, tier})
Provider->>Provider: validateModelAccess(model, tier)
alt Model not available for tier
Provider-->>App: ModelAccessError
else Model available
Provider->>TokenStore: getValidToken()
alt Token within buffer period
TokenStore->>OAuthServer: refreshAccessToken(refreshToken)
OAuthServer-->>TokenStore: newAccessToken, newRefreshToken
TokenStore->>TokenStore: persist refreshed tokens
end
TokenStore-->>Provider: validAccessToken
Provider->>Provider: getAuthHeaders()
Provider->>Anthropic: POST /messages (Bearer token, beta headers)
Anthropic-->>Provider: response (with usage, rate-limit headers)
Provider->>Provider: parseRateLimitHeaders()
Provider->>Provider: updateResponseMetadata()
Provider-->>App: EnhancedGenerateResult
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
@coderabbitai review Review Focus AreasThis PR adds Claude subscription support with OAuth 2.0 authentication across 34 files (+12,930/-295 lines). Please focus your review on these critical areas: 1. Security (HIGH PRIORITY)
2. Type System Architecture (HIGH PRIORITY)
3. OAuth Fetch Wrapper (HIGH PRIORITY)
4. Token Refresh Pattern (MEDIUM PRIORITY)
5. CLI Auth Commands (MEDIUM PRIORITY)
6. Configuration Helpers (MEDIUM PRIORITY)
7. Documentation Accuracy (LOW PRIORITY)
8. Cross-Reference with Recent PRsPlease review patterns from these recent March/February 2026 PRs on the
Key Patterns to Enforce
Thank you for the thorough review! |
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
Documentation Validation Results🚀 Documentation validation passed!
📦 Build artifact uploaded successfully. Ready for deployment preview. Commit: |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain
🏁 Script executed: Length of output: 56788 🏁 Script executed: Length of output: 38542 🏁 Script executed: Length of output: 49810 🏁 Script executed: Length of output: 52438 🏁 Script executed: Length of output: 35647 🏁 Script executed: Length of output: 19262 🏁 Script executed: Length of output: 6593 🏁 Script executed: Length of output: 1943 🏁 Script executed: Length of output: 1936 🏁 Script executed: Length of output: 6229 🏁 Script executed: Length of output: 17675 🏁 Script executed: Length of output: 9731 🏁 Script executed: Length of output: 515 🏁 Script executed: Length of output: 10833 🏁 Script executed: Length of output: 6323 🏁 Script executed: Length of output: 7590
Executive summary
Details by focus area
Proposed minimal diffs A) Fix type export collision - export type {
- ExternalMCPConfigValidation,
- ExternalMCPManagerConfig,
- ExternalMCPOperationResult,
- ExternalMCPServerEvents,
- ExternalMCPServerHealth,
- ExternalMCPServerInstance,
- ExternalMCPServerStatus,
- ExternalMCPToolContext,
- ExternalMCPToolInfo,
- ExternalMCPToolResult,
- ExternalToolExecutionOptions,
- FlexibleValidationResult,
- HTTPRetryConfig,
- MCPClientResult,
- MCPConnectedServer,
- MCPDiscoveredServer,
- MCPExecutableTool,
- MCPOAuthConfig,
- OAuthTokens,
- RateLimitConfig,
- TokenBucketRateLimitConfig,
- TokenExchangeRequest,
- TokenStorage,
- ToolDiscoveryResult,
- ToolRegistryEvents,
- ToolValidationResult,
- } from "./mcpTypes.js";
+ export type {
+ ExternalMCPConfigValidation,
+ ExternalMCPManagerConfig,
+ ExternalMCPOperationResult,
+ ExternalMCPServerEvents,
+ ExternalMCPServerHealth,
+ ExternalMCPServerInstance,
+ ExternalMCPServerStatus,
+ ExternalMCPToolContext,
+ ExternalMCPToolInfo,
+ ExternalMCPToolResult,
+ ExternalToolExecutionOptions,
+ FlexibleValidationResult,
+ HTTPRetryConfig,
+ MCPClientResult,
+ MCPConnectedServer,
+ MCPDiscoveredServer,
+ MCPExecutableTool,
+ MCPOAuthConfig,
+ OAuthTokens as McpOAuthTokens,
+ RateLimitConfig,
+ TokenBucketRateLimitConfig,
+ TokenExchangeRequest,
+ TokenStorage,
+ ToolDiscoveryResult,
+ ToolRegistryEvents,
+ ToolValidationResult,
+ } from "./mcpTypes.js";B) Respect enableBetaFeatures and improve streaming tool name stripping; add refresh lock and atomic write; use TOKEN_EXPIRY_BUFFER_MS -import {
- CLAUDE_CLI_USER_AGENT,
- CLAUDE_CODE_CLIENT_ID,
- ANTHROPIC_TOKEN_URL,
- MCP_TOOL_PREFIX,
-} from "../auth/anthropicOAuth.js";
+import {
+ CLAUDE_CLI_USER_AGENT,
+ CLAUDE_CODE_CLIENT_ID,
+ ANTHROPIC_TOKEN_URL,
+ MCP_TOOL_PREFIX,
+} from "../auth/anthropicOAuth.js";
+import { TOKEN_EXPIRY_BUFFER_MS } from "../constants/enums.js";
@@
-function createOAuthFetch(getToken: () => string): typeof fetch {
+function createOAuthFetch(
+ getToken: () => string,
+ includeOptionalBetas: boolean,
+): typeof fetch {
@@
- const mergedBetas = [
- "oauth-2025-04-20",
- "interleaved-thinking-2025-05-14",
- ...(includeClaudeCode ? ["claude-code-20250219"] : []),
- ].join(",");
+ const mergedBetas = [
+ "oauth-2025-04-20",
+ ...(includeOptionalBetas ? ["interleaved-thinking-2025-05-14"] : []),
+ ...(includeClaudeCode ? ["claude-code-20250219"] : []),
+ ].join(",");
@@
- if (response.body) {
+ if (response.body) {
const reader = response.body.getReader();
const decoder = new TextDecoder();
const encoder = new TextEncoder();
+ let carry = "";
const stream = new ReadableStream({
async pull(controller) {
const { done, value } = await reader.read();
if (done) {
- controller.close();
+ if (carry) {
+ const flushed = carry.replace(/"name"\s*:\s*"mcp_([^"]+)"/g, '"name": "$1"');
+ controller.enqueue(encoder.encode(flushed));
+ carry = "";
+ }
+ controller.close();
return;
}
- let text = decoder.decode(value, { stream: true });
- // Remove mcp_ prefix from tool names in response
- text = text.replace(/"name"\s*:\s*"mcp_([^"]+)"/g, '"name": "$1"');
- controller.enqueue(encoder.encode(text));
+ // Handle potential cross-chunk splits of `"name": "mcp_..."`
+ const chunkText = decoder.decode(value, { stream: true });
+ const combined = carry + chunkText;
+ const replaced = combined.replace(/"name"\s*:\s*"mcp_([^"]+)"/g, '"name": "$1"');
+ // Keep a small tail as carry (covers `"name": "mcp_` split)
+ const TAIL = 24;
+ carry = combined.slice(-TAIL);
+ const emitLen = Math.max(0, replaced.length - carry.length);
+ const toEmit = replaced.slice(0, emitLen);
+ if (toEmit) controller.enqueue(encoder.encode(toEmit));
},
});
@@
- const tokenRef = this.oauthToken;
- const oauthFetch = createOAuthFetch(() => tokenRef.accessToken);
+ const tokenRef = this.oauthToken;
+ const oauthFetch = createOAuthFetch(
+ () => tokenRef.accessToken,
+ this.enableBetaFeatures,
+ );
@@
- public async refreshAuthIfNeeded(): Promise<void> {
+ private refreshPromise?: Promise<void>;
+
+ public async refreshAuthIfNeeded(): Promise<void> {
@@
- const expiryBuffer = 5 * 60 * 1000; // 5 minutes in milliseconds
+ const expiryBuffer = TOKEN_EXPIRY_BUFFER_MS;
const isExpired = this.oauthToken.expiresAt <= now;
const isExpiringSoon = this.oauthToken.expiresAt <= now + expiryBuffer;
@@
- try {
+ if (this.refreshPromise) {
+ await this.refreshPromise;
+ return;
+ }
+
+ this.refreshPromise = (async () => {
+ try {
const response = await fetch(ANTHROPIC_TOKEN_URL, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": CLAUDE_CLI_USER_AGENT,
},
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: this.oauthToken.refreshToken,
client_id: CLAUDE_CODE_CLIENT_ID,
}),
});
@@
- // Persist the refreshed token to disk so subsequent provider instances
+ // Persist the refreshed token to disk atomically so subsequent provider instances
// and the CLI pick up the new credentials automatically.
try {
const credentialsDir = join(homedir(), ".neurolink");
if (!existsSync(credentialsDir)) {
mkdirSync(credentialsDir, { recursive: true });
}
const credentialsPath = join(
credentialsDir,
"anthropic-credentials.json",
);
+ const tmpPath = `${credentialsPath}.tmp`;
const existingRaw = existsSync(credentialsPath)
? JSON.parse(readFileSync(credentialsPath, "utf-8"))
: {};
const updated = {
...existingRaw,
type: "oauth",
oauth: updatedToken,
updatedAt: Date.now(),
};
- writeFileSync(credentialsPath, JSON.stringify(updated, null, 2), {
- mode: 0o600,
- });
+ writeFileSync(tmpPath, JSON.stringify(updated, null, 2), { mode: 0o600 });
+ // Atomic replace on most filesystems
+ const { renameSync } = await import("fs");
+ renameSync(tmpPath, credentialsPath);
logger.debug("Refreshed OAuth credentials persisted to disk");
} catch (persistError) {
@@
- } catch (error) {
+ })();
+ try {
+ await this.refreshPromise;
+ } catch (error) {
if (error instanceof AuthenticationError) {
throw error;
}
throw new AuthenticationError(
`Failed to refresh OAuth token: ${error instanceof Error ? error.message : String(error)}`,
this.providerName,
);
- }
+ } finally {
+ this.refreshPromise = undefined;
+ }C) Normalize env var handling in configuration helpers - const accessToken = process.env.ANTHROPIC_OAUTH_ACCESS_TOKEN;
- const refreshToken = process.env.ANTHROPIC_OAUTH_REFRESH_TOKEN;
+ // Canonical + fallbacks for backward compatibility
+ const accessToken =
+ process.env.ANTHROPIC_OAUTH_TOKEN ??
+ process.env.CLAUDE_OAUTH_TOKEN ??
+ process.env.ANTHROPIC_OAUTH_ACCESS_TOKEN;
+ const refreshToken = process.env.ANTHROPIC_OAUTH_REFRESH_TOKEN;
@@
- description:
- authMethod === "oauth"
- ? `Anthropic OAuth Token (${tier} tier)`
- : "Anthropic API Key",
+ description:
+ authMethod === "oauth"
+ ? `Anthropic OAuth Token (${tier} tier)`
+ : "Anthropic API Key",
@@
- ? "https://claude.ai/settings"
+ ? "https://claude.ai/settings"
: "https://console.anthropic.com/",
@@
- ? ["ANTHROPIC_API_KEY"]
- : ["ANTHROPIC_OAUTH_ACCESS_TOKEN"], // Fall back to OAuth if API key not present
+ ? ["ANTHROPIC_API_KEY"]
+ : ["ANTHROPIC_OAUTH_TOKEN", "CLAUDE_OAUTH_TOKEN", "ANTHROPIC_OAUTH_ACCESS_TOKEN"], // OAuth fallbacksD) Prefer open package in CLI (with safe fallback) -import { execFile } from "child_process";
+import { execFile } from "child_process";
+import open from "open";
@@
async function openBrowser(url: string): Promise<void> {
- return new Promise((resolve, reject) => {
+ // Prefer cross-platform "open" package to avoid shell-specific quirks
+ try {
+ await open(url);
+ return;
+ } catch {
+ // Fallback to execFile per-platform
+ }
+ return new Promise((resolve, reject) => {
const platform = process.platform;
let command: string;
let args: string[];
@@
- // Use execFile instead of exec to prevent command injection
+ // Use execFile instead of exec to prevent command injection
execFile(command, args, (error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}E) Redact OAuth error bodies in logs +function redactTokens(s: string): string {
+ // Replace likely JWTs and long opaque tokens
+ return s
+ .replace(/[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+/g, "[JWT]")
+ .replace(/\b[A-Za-z0-9\-_]{32,}\b/g, "[TOKEN]");
+}
@@
- const errorBody = await response.text();
- logger.error("Token exchange failed", {
+ const errorBody = await response.text();
+ logger.error("Token exchange failed", {
status: response.status,
- error: errorBody,
+ error: redactTokens(errorBody).slice(0, 500),
});
@@
- const errorBody = await response.text();
- logger.error("Token refresh failed", {
+ const errorBody = await response.text();
+ logger.error("Token refresh failed", {
status: response.status,
- error: errorBody,
+ error: redactTokens(errorBody).slice(0, 500),
});
@@
- const errorBody = await response.text();
- logger.error("Token revocation failed", {
+ const errorBody = await response.text();
+ logger.error("Token revocation failed", {
status: response.status,
- error: errorBody,
+ error: redactTokens(errorBody).slice(0, 500),
});Notes and smaller nits
If you want, I can open a follow-up PR with the above minimal diffs as separate commits (types, provider, utils, CLI, oauth-log-redaction) to keep the history tidy. |
There was a problem hiding this comment.
Actionable comments posted: 3
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
docs/getting-started/provider-setup.md (1)
1159-1183:⚠️ Potential issue | 🟠 MajorAnthropic default model in this guide does not match current provider behavior.
The doc sets the default/example to
claude-sonnet-4-5-20250929, but the provider integration tests assertclaude-3-5-sonnet-20241022as the default when unspecified.🛠️ Proposed doc fix
-export ANTHROPIC_MODEL="claude-sonnet-4-5-20250929" # Default model +export ANTHROPIC_MODEL="claude-3-5-sonnet-20241022" # Default model ... - model: "claude-sonnet-4-5-20250929", + model: "claude-3-5-sonnet-20241022",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/getting-started/provider-setup.md` around lines 1159 - 1183, The documentation shows the default Anthropic model as "claude-sonnet-4-5-20250929" (and uses that in the ANTHROPIC_MODEL env example and the Usage Example), but the provider integration/tests assert the real default is "claude-3-5-sonnet-20241022"; update the doc to match behavior by replacing the default/example model string (and any mentions in the Supported Models list and the ANTHROPIC_MODEL example) with "claude-3-5-sonnet-20241022" so the env var ANTHROPIC_MODEL, the Usage Example model, and default label are consistent with the provider integration.src/lib/constants/index.ts (1)
308-314:⚠️ Potential issue | 🟡 Minor
CONSTANTS_METADATAcan silently drift after export changes.
CATEGORIESwas updated, but staticLAST_UPDATED/TOTAL_CONSTANTSare still manual and likely stale over time. Consider deriving/validating these to keep metadata trustworthy.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/constants/index.ts` around lines 308 - 314, CONSTANTS_METADATA currently contains hardcoded LAST_UPDATED and TOTAL_CONSTANTS that can drift; change it to compute/validate these values at module load or build time: derive TOTAL_CONSTANTS from the actual exported constants collection (e.g., compute Object.keys(...) length for the top-level constants object referenced by your module) and set LAST_UPDATED from a deterministic source (preferably a build/git timestamp injected via process.env or a build script, with a fallback to new Date().toISOString().split('T')[0]), and add an optional runtime assertion that CATEGORIES matches the categories inferred from the constants to fail-fast if they diverge—update the CONSTANTS_METADATA construction to use these computed values instead of fixed strings/numbers.
🟠 Major comments (19)
src/lib/constants/enums.ts-796-800 (1)
796-800:⚠️ Potential issue | 🟠 MajorAvoid dual sources of truth for
AnthropicBetaFeature.Line 796 introduces a second definition for beta feature values while canonical subscription feature typing already exists in
src/lib/types/subscriptionTypes.ts. This can drift and cause mismatched validation across modules.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/constants/enums.ts` around lines 796 - 800, The new AnthropicBetaFeature enum duplicates canonical beta-feature values already defined in the subscription typing; remove this enum and instead import and reuse the existing canonical subscription feature type/const from the subscriptionTypes definition so there is a single source of truth (replace references to AnthropicBetaFeature with the imported symbol, or re-export the canonical symbol under the same name if needed for compatibility).src/lib/types/errors.ts-162-175 (1)
162-175:⚠️ Potential issue | 🟠 MajorKeep SDK-specific errors in the
BaseErrorhierarchy.Line 162 and Line 184 define NeuroLink domain errors by extending
Errordirectly, which can bypass shared error handling that depends onBaseErrorlineage.Suggested fix
-export class TokenStoreError extends Error { +export class TokenStoreError extends BaseError { constructor( message: string, public readonly code: | "STORAGE_ERROR" | "ENCRYPTION_ERROR" | "VALIDATION_ERROR" | "NOT_FOUND" | "REFRESH_ERROR" = "STORAGE_ERROR", ) { super(message); this.name = "TokenStoreError"; } } @@ -export class ModelAccessError extends Error { +export class ModelAccessError extends BaseError { public readonly model: string; public readonly tier: string; public readonly requiredTier: string;Also applies to: 184-199
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/types/errors.ts` around lines 162 - 175, TokenStoreError and the NeuroLink domain error classes currently extend Error directly, bypassing the shared BaseError hierarchy; change TokenStoreError (and the NeuroLink domain error class(es) around the 184-199 range) to extend BaseError instead of Error, import BaseError, pass the message (and any code) into the BaseError constructor as required by its signature, and preserve the existing public readonly code union and this.name assignments so these exceptions participate in the shared error handling pipeline.src/cli/factories/authCommandFactory.ts-53-83 (1)
53-83: 🛠️ Refactor suggestion | 🟠 MajorWrap async subcommand handlers with
withTimeoutfor consistent failure behavior.Each handler performs async work (dynamic import + command execution) without timeout protection, so CLI auth subcommands can hang indefinitely under I/O stalls.
As per coding guidelines:
src/**/*.ts: "All async operations should be wrapped with withTimeout utility for consistent timeout handling".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/factories/authCommandFactory.ts` around lines 53 - 83, The async subcommand handlers for the auth commands (the arrow functions that dynamically import and call handleLogin, handleLogout, handleStatus, and handleRefresh) must be wrapped with the withTimeout utility so they cannot hang indefinitely; update each command's final async handler to call withTimeout(() => import("../commands/auth.js").then(m => m.handleX(argv as AuthCommandArgs))) (replace handleX with the appropriate handler name) and ensure you import/use withTimeout where these handlers are defined so login, logout, status, and refresh all have consistent timeout protection.test/integration/anthropic-subscription.test.ts-1331-1335 (1)
1331-1335:⚠️ Potential issue | 🟠 Major
expiresAtfixture uses seconds instead of milliseconds.This fixture uses
Date.now() / 1000 + 3600, but token expiry in this codebase is Unix milliseconds. The current value can mask expiry/refresh logic issues.🛠️ Proposed fix
process.env.ANTHROPIC_OAUTH_TOKEN = JSON.stringify({ accessToken: "oauth-access-token", refreshToken: "oauth-refresh-token", - expiresAt: Date.now() / 1000 + 3600, + expiresAt: Date.now() + 3600 * 1000, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/integration/anthropic-subscription.test.ts` around lines 1331 - 1335, The test fixture sets process.env.ANTHROPIC_OAUTH_TOKEN.expiresAt in seconds (Date.now() / 1000 + 3600) but the codebase expects Unix milliseconds; update the fixture to use milliseconds (e.g., Date.now() + 3600 * 1000 or Date.now() + 3600000) so expiresAt matches the production numeric format used by the token handling/refresh logic (refer to the ANTHROPIC_OAUTH_TOKEN env variable in the anthropic-subscription tests).docs/getting-started/providers/anthropic.md-190-198 (1)
190-198:⚠️ Potential issue | 🟠 MajorConflicting Anthropic default-model guidance in the same doc.
Line 122 says the default is
claude-3-5-sonnet-20241022, while Line 197 says API tier defaults toclaude-sonnet-4-20250514. These conflict and can mislead fallback/debug behavior.🛠️ Proposed doc fix
| Tier | Default Model | | ------- | --------------------------- | | Free | `claude-3-5-haiku-20241022` | | Pro | `claude-sonnet-4-20250514` | | Max | `claude-opus-4-20250514` | | Max 5x | `claude-opus-4-20250514` | | Max 20x | `claude-opus-4-20250514` | -| API | `claude-sonnet-4-20250514` | +| API | `claude-3-5-sonnet-20241022` |🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/getting-started/providers/anthropic.md` around lines 190 - 198, The doc contains conflicting Anthropic default-model values: one place uses "claude-3-5-sonnet-20241022" (earlier paragraph) while the table's API tier row lists "claude-sonnet-4-20250514"; pick the correct canonical default and make both places consistent. Update the earlier paragraph and the table row (and any other mentions) to the chosen model name (e.g., replace all occurrences of claude-3-5-sonnet-20241022 or claude-sonnet-4-20250514 with the agreed value), and if there is related fallback/debug guidance tied to a symbol like "default-model" or "API tier", ensure that text references the same unified model string.docs/getting-started/providers/anthropic.md-612-620 (1)
612-620:⚠️ Potential issue | 🟠 Major
anthropic-subscriptionis documented as a provider value, but current CLI support isanthropiconly.This option table currently advertises a provider slug that is not reflected in the auth command factory provider list.
🛠️ Proposed doc fix
-| `--provider` / `-p` | `anthropic` | Use Anthropic provider | +| `--provider` / `-p` | `anthropic` | Use Anthropic provider |🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/getting-started/providers/anthropic.md` around lines 612 - 620, The docs list `anthropic-subscription` as an accepted value for the `--provider` flag but the CLI only supports `anthropic`; update the docs or the auth command factory to match. Either remove `anthropic-subscription` from the `--provider` / `-p` value list in this table (and any related flags such as `--auth-method`, `--subscription-tier`, `--enable-beta`, `--model`) OR add `anthropic-subscription` to the auth command factory provider list so the CLI recognizes it; ensure the `--provider` documentation and the auth command factory provider list are consistent.test/integration/anthropic-subscription.test.ts-107-117 (1)
107-117:⚠️ Potential issue | 🟠 MajorRestore
global.fetchafter each test to avoid cross-test state bleed.
global.fetchis reassigned inbeforeEachbut never reset to its original value, which can affect later suites.🛠️ Proposed fix
describe("1. OAuth Flow Tests", () => { let originalEnv: NodeJS.ProcessEnv; + const originalFetch = global.fetch; let mockFetch: Mock<typeof fetch>; @@ afterEach(() => { process.env = originalEnv; + global.fetch = originalFetch; vi.restoreAllMocks(); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/integration/anthropic-subscription.test.ts` around lines 107 - 117, The tests reassign global.fetch in beforeEach (mockFetch) but never restore it; capture the original global.fetch (e.g., const originalFetch or originalGlobalFetch) before overriding in the beforeEach and then restore global.fetch = originalFetch in afterEach (alongside process.env and vi.restoreAllMocks()) so mockFetch doesn't leak into other suites; update the beforeEach/afterEach around the mockFetch/global.fetch manipulation and keep identifiers mockFetch and global.fetch consistent.src/cli/factories/commandFactory.ts-658-670 (1)
658-670:⚠️ Potential issue | 🟠 MajorDefaulted subscription tier is not propagated into
anthropicAuthConfig.Line 669 mutates
options.subscriptionTier, but Line 709 still uses the stalesubscriptionTiercaptured before mutation (Line 651). This drops the default tier fromauthConfig.💡 Suggested fix
- const subscriptionTier = options.subscriptionTier as string | undefined; + let subscriptionTier = options.subscriptionTier as string | undefined; @@ if (isSubscriptionMode && !subscriptionTier) { @@ options.subscriptionTier = "api"; + subscriptionTier = "api"; } @@ const authConfig: AnthropicAuthConfig = { method: (authMethod === "oauth" ? "oauth" : "api_key") as AnthropicAuthMethod, - subscriptionTier: subscriptionTier as - | ClaudeSubscriptionTier - | undefined, + subscriptionTier: subscriptionTier as ClaudeSubscriptionTier | undefined, };Also applies to: 705-712
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/factories/commandFactory.ts` around lines 658 - 670, The default subscription tier set via options.subscriptionTier is not being propagated into anthropicAuthConfig because code later uses the stale local variable subscriptionTier; update the code that builds anthropicAuthConfig (or any use of subscriptionTier) to read the value from options.subscriptionTier (or reassign subscriptionTier = options.subscriptionTier after defaulting) so the auth config reflects the defaulted tier; ensure any references in the anthropicAuthConfig creation use options.subscriptionTier (or the reassigned subscriptionTier) rather than the original captured variable.src/cli/commands/auth.ts-1294-1310 (1)
1294-1310:⚠️ Potential issue | 🟠 MajorCredentials are persisted in plaintext provider files.
Line 1308 writes API keys and OAuth tokens to
${provider}-credentials.jsonas readable JSON (despite 0o600). This bypasses the secure token-store path and increases local secret exposure.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/commands/auth.ts` around lines 1294 - 1310, The saveStoredCredentials function currently writes provider credentials to a plaintext JSON file (`${provider}-credentials.json`), exposing API keys/OAuth tokens; change it to store secrets in the secure token store (e.g., call the existing secure storage utility or integrate keytar) instead of writing them to disk, and only fall back to an encrypted file with strict 0o600 permissions if the secure store is unavailable; update references to NEUROLINK_CONFIG_DIR/credentialsFile accordingly and remove plaintext JSON writes so credentials are never persisted as readable JSON by saveStoredCredentials.src/lib/auth/tokenStore.ts-36-40 (1)
36-40:⚠️ Potential issue | 🟠 Major
refreshTokenshould not be mandatory for stored OAuth tokens.Line 40 and Line 570 enforce refresh-token presence. OAuth responses can be valid without
refresh_token, so this rejects legitimate tokens.💡 Suggested fix
export interface StoredOAuthTokens { /** The access token for API authentication */ accessToken: string; /** The refresh token for obtaining new access tokens */ - refreshToken: string; + refreshToken?: string; /** Unix timestamp (ms) when the access token expires */ expiresAt: number; /** Token type, typically "Bearer" */ tokenType: string; @@ - if (!tokens.refreshToken || typeof tokens.refreshToken !== "string") { + if ( + tokens.refreshToken !== undefined && + typeof tokens.refreshToken !== "string" + ) { throw new TokenStoreError( - "Invalid refresh token: must be a non-empty string", + "Invalid refresh token: must be a string when provided", "VALIDATION_ERROR", ); }Also applies to: 570-575
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/auth/tokenStore.ts` around lines 36 - 40, StoredOAuthTokens currently requires refreshToken, which rejects valid OAuth responses that omit it; make refreshToken optional (change its type to string | undefined or mark optional) in the StoredOAuthTokens interface and update any validation or enforcement logic in this file (the token validation/storage code around lines ~570-575) to no longer throw or reject when refreshToken is absent—only require a refresh token when an operation actually needs it (e.g., when attempting a refresh). Also update JSDoc/comments to reflect that refreshToken may be missing.src/cli/commands/auth.ts-421-423 (1)
421-423: 🛠️ Refactor suggestion | 🟠 MajorUse
ErrorFactoryinstead of rawErrorin new auth paths.The changed branches throw raw
Errorobjects, which breaks typed error consistency across the auth surface.As per coding guidelines, “Use ErrorFactory for creating typed errors instead of throwing raw Error objects.”
Also applies to: 846-848, 880-882, 893-894, 938-938
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/commands/auth.ts` around lines 421 - 423, Replace raw throws like throw new Error(`Token refresh failed: ${tokenResponse.status} - ${errorText}`) with an ErrorFactory-created typed error (use the project ErrorFactory API) so the auth surface remains consistent; construct the error via ErrorFactory (e.g., ErrorFactory.createAuthError / ErrorFactory.authTokenRefreshFailed or the equivalent factory method in your codebase), include the status and body/message (pass tokenResponse.status and errorText) as structured metadata, and apply the same replacement for the other raw throws in this file (the similar throw sites around the token refresh and auth paths referenced at the other locations).src/lib/auth/anthropicOAuth.ts-1117-1122 (1)
1117-1122:⚠️ Potential issue | 🟠 MajorReject callbacks when
stateis missing.Line 1117 only rejects mismatched states when one is present; missing callback state currently passes. For OAuth CSRF protection, missing state must fail too.
💡 Suggested fix
- if (callbackResult.state && callbackResult.state !== state) { + if (!callbackResult.state || callbackResult.state !== state) { throw new OAuthError( "State mismatch - possible CSRF attack", "STATE_MISMATCH", ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/auth/anthropicOAuth.ts` around lines 1117 - 1122, The current check only throws an OAuthError when callbackResult.state exists but mismatches; change the logic in the callback handling (where callbackResult and state are compared) to also reject when callbackResult.state is missing by replacing the condition with one that throws if callbackResult.state is falsy or callbackResult.state !== state; keep using OAuthError with the "State mismatch - possible CSRF attack" / "STATE_MISMATCH" identifiers so missing or incorrect states are both rejected.src/lib/auth/anthropicOAuth.ts-431-438 (1)
431-438: 🛠️ Refactor suggestion | 🟠 MajorApply
withTimeoutto OAuth HTTP requests.These network calls are currently unbounded and can stall the auth flow indefinitely under degraded network conditions.
As per coding guidelines, “All async operations should be wrapped with withTimeout utility for consistent timeout handling.”
Also applies to: 514-521, 585-595, 633-643, 713-720
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/auth/anthropicOAuth.ts` around lines 431 - 438, Wrap each OAuth-related fetch call in anthropicOAuth.ts with the withTimeout utility so network requests cannot hang indefinitely; specifically replace direct await fetch(...) uses (e.g., the token request that uses config.tokenUrl || this.tokenUrl and the other fetch blocks around lines 514-521, 585-595, 633-643, 713-720) with await withTimeout(fetch(...), <appropriateTimeoutMs>) (or await withTimeout(..., timeoutMs) according to the utility signature) and ensure any subsequent response handling still awaits the result of withTimeout. Import or reference the existing withTimeout helper where needed and use a consistent timeout constant for all OAuth HTTP calls so all async fetch operations in functions/methods handling token exchange and userinfo requests are bounded.src/cli/commands/auth.ts-405-417 (1)
405-417: 🛠️ Refactor suggestion | 🟠 MajorWrap outbound auth HTTP calls with
withTimeout.These fetch operations are unbounded and can hang CLI flows under network issues. This file should apply the project timeout wrapper consistently.
As per coding guidelines, “All async operations should be wrapped with withTimeout utility for consistent timeout handling.”
Also applies to: 828-841, 1058-1071, 1194-1199, 1355-1367
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/commands/auth.ts` around lines 405 - 417, The fetch call creating tokenResponse (and the other outbound auth HTTP fetches) must be wrapped with the project's withTimeout utility to avoid unbounded waits; update the code around the token exchange that uses ANTHROPIC_OAUTH_CONFIG and tokenResponse to call withTimeout(fetch(...), <appropriate timeout>) instead of awaiting fetch directly, import/use the existing withTimeout helper and the project timeout constant, and apply the same change to the other fetch locations referenced (lines around 828-841, 1058-1071, 1194-1199, 1355-1367) so all async auth HTTP operations consistently time out and propagate errors as expected.src/cli/factories/commandFactory.ts-96-100 (1)
96-100:⚠️ Potential issue | 🟠 MajorCLI tier options omit supported
max_5andmax_20tiers.Line 98, Line 632, and setup option/type unions currently reject valid canonical tiers, which can block supported subscription flows.
💡 Suggested fix
- choices: ["free", "pro", "max", "api"], + choices: ["free", "pro", "max", "max_5", "max_20", "api"], @@ subscriptionTier: argv.subscriptionTier as | "free" | "pro" | "max" + | "max_5" + | "max_20" | "api" | undefined, @@ - choices: ["free", "pro", "max", "api"], + choices: ["free", "pro", "max", "max_5", "max_20", "api"], @@ - subscriptionTier?: "free" | "pro" | "max" | "api"; + subscriptionTier?: + | "free" + | "pro" + | "max" + | "max_5" + | "max_20" + | "api";Also applies to: 631-636, 1563-1566, 1595-1596
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/factories/commandFactory.ts` around lines 96 - 100, The CLI's subscription tier choices and related type unions currently omit the canonical "max_5" and "max_20" tiers; update the subscriptionTier option in commandFactory (the choices array on the subscriptionTier option) to include "max_5" and "max_20", and then update any corresponding type/union declarations or setup option checks that reference tier strings (e.g., the union/type handling that validates tiers and any SetupOptions/option parsers that enumerate ["free","pro","max","api"]) to include these two new constants so validation and flows accept "max_5" and "max_20".src/lib/auth/tokenStore.ts-36-60 (1)
36-60: 🛠️ Refactor suggestion | 🟠 MajorMove exported token types to
src/lib/types.Line 36 onward introduces exported/shared auth token types in
src/lib/auth/tokenStore.ts. These should live undersrc/lib/types/and be imported here.Based on learnings, “In the juspay/neurolink repository, all new type definitions must be placed in src/lib/types/. New type definitions outside this directory should be flagged and blocked in code reviews.”
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/auth/tokenStore.ts` around lines 36 - 60, Move the exported type definitions StoredOAuthTokens, OAuthTokens, and TokenRefresher out of src/lib/auth/tokenStore.ts into the central types module in src/lib/types (create or add to an appropriate file there), export them from that types module, and then replace the local definitions in tokenStore.ts with imports of those types; ensure the imported names match (StoredOAuthTokens, OAuthTokens, TokenRefresher) and update any references in tokenStore.ts to use the imported types.src/cli/commands/auth.ts-906-913 (1)
906-913:⚠️ Potential issue | 🟠 MajorHardcoded
subscriptionTier: "pro"can misclassify users.Line 910 forces all API-key-via-OAuth users into
pro, which can incorrectly limit/maximize tier behavior formax,max_5, ormax_20accounts.💡 Suggested fix
await saveStoredCredentials(provider, { type: "api-key", apiKey: apiKeyData.raw_key, provider, - subscriptionTier: "pro", // Assume Pro/Max since using OAuth + subscriptionTier: + (await detectSubscriptionTier(tokenData.access_token)) ?? undefined, createdAt: Date.now(), updatedAt: Date.now(), });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/commands/auth.ts` around lines 906 - 913, The code hardcodes subscriptionTier: "pro" when calling saveStoredCredentials, which misclassifies OAuth-issued API keys; instead derive the tier from the API response (e.g., properties on apiKeyData such as apiKeyData.tier, apiKeyData.subscriptionTier, or any returned plan/model field) and pass that computed value into saveStoredCredentials, falling back to a safe default only if the tier field is absent; update the call site around saveStoredCredentials and any helper that parses apiKeyData to ensure correct mapping for "pro", "max", "max_5", "max_20", etc.src/cli/commands/auth.ts-618-625 (1)
618-625:⚠️ Potential issue | 🟠 MajorNon-interactive API-key flow can fail without failing the command.
Line 624 returns after logging, so
handleLogincan exit successfully without authenticating. This should hard-fail (or actually read and persist env credentials) in non-interactive mode.💡 Suggested fix
if (!interactive) { - logger.error( - chalk.red( - "Non-interactive mode requires --method api-key with ANTHROPIC_API_KEY environment variable", - ), - ); - return; + const envVar = getEnvVarName(provider); + const envKey = process.env[envVar]?.trim(); + if (!envKey) { + throw new Error( + `Non-interactive mode requires ${envVar} when using --method api-key`, + ); + } + await saveStoredCredentials(provider, { + type: "api-key", + apiKey: envKey, + provider, + createdAt: Date.now(), + updatedAt: Date.now(), + }); + logger.always(chalk.green(`Using ${envVar} from environment.`)); + return; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/commands/auth.ts` around lines 618 - 625, The non-interactive branch in handleLogin currently logs and returns, allowing success without authentication; instead, when !interactive and method is "api-key" check process.env.ANTHROPIC_API_KEY: if present, perform the same authentication/persistence path used for interactive API-key entry (reuse the existing login/save logic), otherwise call logger.error with a clear message and exit with a non-zero status (e.g., process.exit(1)). Update the code paths in handleLogin so the ANTHROPIC_API_KEY env var is consumed and persisted or the command hard-fails rather than simply returning.src/lib/providers/anthropic.ts-771-782 (1)
771-782:⚠️ Potential issue | 🟠 MajorToken refresh fetch lacks timeout handling.
The OAuth token refresh request has no timeout, which could cause the operation to hang indefinitely if the Anthropic token endpoint is unresponsive. As per coding guidelines, all async operations should be wrapped with timeout handling.
🔧 Proposed fix using AbortController with timeout
+ const REFRESH_TIMEOUT_MS = 30000; // 30 seconds + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), REFRESH_TIMEOUT_MS); + try { const response = await fetch(ANTHROPIC_TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", "User-Agent": CLAUDE_CLI_USER_AGENT, }, body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: this.oauthToken.refreshToken, client_id: CLAUDE_CODE_CLIENT_ID, }), + signal: controller.signal, }); + clearTimeout(timeoutId); if (!response.ok) {As per coding guidelines: "All async operations should be wrapped with withTimeout utility for consistent timeout handling"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/providers/anthropic.ts` around lines 771 - 782, The token refresh fetch to ANTHROPIC_TOKEN_URL currently has no timeout; update the refresh logic (the fetch block that posts grant_type=refresh_token using this.oauthToken.refreshToken and CLAUDE_CODE_CLIENT_ID with CLAUDE_CLI_USER_AGENT) to use the project's withTimeout helper so the request is aborted on timeout (or use AbortController wired into withTimeout). Wrap the existing fetch call with withTimeout(...) (or call fetch via a promise that withTimeout rejects/aborts after the configured timeout) and ensure any AbortController signal is passed into fetch so the request cannot hang indefinitely.
🟡 Minor comments (12)
docs/changelog.md-12-13 (1)
12-13:⚠️ Potential issue | 🟡 MinorCurrent version labels appear stale relative to package metadata.
Line 12 and Line 252 state
v9.13.0, butpackage.jsonin this PR is9.14.0. Please align these to avoid conflicting version signals in release docs.Also applies to: 251-252
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/changelog.md` around lines 12 - 13, Update the stale version label "v9.13.0" in docs/changelog.md to match package.json's 9.14.0: replace the occurrences of "v9.13.0" (the current release header and the duplicate at the bottom) with "v9.14.0" so the changelog's header and footer align with the package metadata (search for the exact token "v9.13.0" to locate the spots to change).docs/reference/provider-comparison.md-3-4 (1)
3-4:⚠️ Potential issue | 🟡 MinorVersion header is inconsistent with the current package version.
Line 4 shows
9.12.2, while this PR context includes newer versioning elsewhere. Please align this version stamp with the actual release version used in the repo.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/reference/provider-comparison.md` around lines 3 - 4, The version header "**NeuroLink Version:** 9.12.2" is out of date; update that header to match the repository's current package/release version (the same value used elsewhere in this PR) and ensure the "**Last Updated:**" date is also adjusted if needed; locate and edit the header block containing the "**NeuroLink Version:**" line to the correct version string so the document aligns with the repo's release metadata.docs/reference/provider-comparison.md-1019-1019 (1)
1019-1019:⚠️ Potential issue | 🟡 MinorCredential storage footnote should match the canonical token-store path.
Line 1019 references
~/.neurolink/anthropic-credentials.json, which conflicts with the token-store documentation using~/.neurolink/tokens.json. Please standardize or explicitly note legacy compatibility.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/reference/provider-comparison.md` at line 1019, Update the credential storage note to match the canonical token-store path: replace or clarify the reference to `~/.neurolink/anthropic-credentials.json` so it uses the standard `~/.neurolink/tokens.json` (or explicitly state that `anthropic-credentials.json` is a legacy/alternate path for backward compatibility); ensure the text around the Anthropic auth description mentions the canonical path `~/.neurolink/tokens.json` and, if keeping legacy support, adds a short parenthetical noting legacy compatibility with `~/.neurolink/anthropic-credentials.json`.docs/reference/provider-comparison.md-69-72 (1)
69-72:⚠️ Potential issue | 🟡 MinorAnthropic free-tier model claim conflicts with tier matrix guidance.
Line 71 says free-tier access includes Sonnet models, but the subscription docs and tier mapping indicate free-tier is Haiku-only. Please correct this to avoid user misconfiguration.
📝 Suggested text fix
-- Access to Claude Sonnet models +- Access to Claude 3 Haiku and Claude 3.5 Haiku models🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/reference/provider-comparison.md` around lines 69 - 72, The doc currently claims "Access to Claude Sonnet models" in the free-tier bullet, which conflicts with the subscription/tier matrix that lists Sonnet as paid; update the free-tier bullet (the line containing the exact text "Access to Claude Sonnet models") to indicate Haiku-only access (e.g., "Access to Claude Haiku models") and ensure the wording aligns with the tier matrix and subscription docs so free-tier references and the tier mapping are consistent.docs/features/claude-subscription.md-217-270 (1)
217-270:⚠️ Potential issue | 🟡 MinorToken storage path is inconsistent in this guide.
Line 219/Line 267 document
~/.neurolink/anthropic-credentials.json, while Line 240 documents~/.neurolink/tokens.json. Please normalize to a single canonical path (or explicitly document legacy compatibility and precedence).📝 Suggested wording alignment
-Credentials are stored at `~/.neurolink/anthropic-credentials.json` with `0o600` file permissions. +Credentials are stored at `~/.neurolink/tokens.json` with `0o600` file permissions.-1. Stored credentials file (`~/.neurolink/anthropic-credentials.json`) -- highest priority +1. Stored credentials file (`~/.neurolink/tokens.json`) -- highest priority🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/features/claude-subscription.md` around lines 217 - 270, The docs currently reference two different token storage locations causing confusion; update the guide so all mentions use a single canonical token file path (choose one) and adjust the examples and prose accordingly, and also add a brief note in the TokenStore documentation (TokenStore class in src/lib/auth/tokenStore.ts) describing any legacy compatibility and the exact precedence order used when detecting OAuth credentials (stored file vs environment variables). Ensure the SDK example, the JSON snippet, and the "auto-detects OAuth credentials" list all reference the same canonical path and clearly state precedence.src/cli/parser.ts-30-38 (1)
30-38:⚠️ Potential issue | 🟡 MinorFix the auth help example in the epilogue.
Line 37 currently suggests
neurolink auth anthropic, but the registered command requires a subcommand. Useneurolink auth login anthropic(orstatus,logout,refreshvariants).Suggested fix
- "Use 'neurolink auth anthropic' to configure authentication", + "Use 'neurolink auth login anthropic' to configure authentication",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/parser.ts` around lines 30 - 38, The epilogue message currently references the incorrect command "neurolink auth anthropic"; update the string passed to epilogue in src/cli/parser.ts so it suggests the correct registered subcommand(s), e.g., "neurolink auth login anthropic" (and optionally mention variants like "neurolink auth status anthropic", "neurolink auth logout anthropic", "neurolink auth refresh anthropic") to match the auth command tree; ensure this change is applied where epilogue(...) is called so help text aligns with the auth command handlers.docs/cli/commands.md-107-110 (1)
107-110:⚠️ Potential issue | 🟡 MinorAuth/subscription flag naming is inconsistent across docs.
This section uses
--authMethod/--subscriptionTier/--enableBeta, while other docs describe kebab-case forms. Please standardize or explicitly document both accepted forms to avoid copy-paste failures.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/cli/commands.md` around lines 107 - 110, The docs use camelCase flags (`--authMethod`, `--subscriptionTier`, `--enableBeta`) which conflicts with kebab-case used elsewhere; update this section to use the canonical kebab-case flag names (`--auth-method`, `--subscription-tier`, `--enable-beta`) and either replace all occurrences of the camelCase variants or explicitly state that both forms are accepted (list both forms for `authMethod`/`auth-method`, `subscriptionTier`/`subscription-tier`, and `enableBeta`/`enable-beta`) so copy-paste from this page won't break consumers and examples remain consistent with other docs.docs/getting-started/provider-setup.md-1197-1199 (1)
1197-1199:⚠️ Potential issue | 🟡 MinorAPI key setup URL should point directly to the Anthropic console.
Users generally create/manage keys at
console.anthropic.com; sending them to the marketing domain adds friction.🛠️ Proposed doc fix
-1. **API Key**: Visit [anthropic.com](https://www.anthropic.com), navigate to API Keys, and export as `ANTHROPIC_API_KEY` +1. **API Key**: Visit [console.anthropic.com](https://console.anthropic.com), navigate to API Keys, and export as `ANTHROPIC_API_KEY`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/getting-started/provider-setup.md` around lines 1197 - 1199, The API key instruction currently links to the marketing domain; update the "API Key" line so the URL points directly to the Anthropic console (use console.anthropic.com) and mention exporting the key as ANTHROPIC_API_KEY; edit the same bullet that starts "API Key: Visit anthropic.com" to use the console URL instead to reduce user friction.src/lib/types/providers.ts-453-460 (1)
453-460:⚠️ Potential issue | 🟡 Minor
oauthConfigpresence alone is too permissive for Anthropic detection.Line 454 currently classifies any object with a non-null
oauthConfigas Anthropic. Tighten this check (e.g., provider identity or stricter Anthropic-shaped fields) to avoid false positives in provider routing.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/types/providers.ts` around lines 453 - 460, The current check treating any non-null object at configObj.oauthConfig as Anthropic is too permissive; update the Anthropic-detection logic (the block referencing configObj.oauthConfig) to require a stricter signal such as an explicit provider identity (e.g., configObj.provider === "anthropic" or configObj.type === "anthropic") or validate Anthropic-shaped fields inside oauthConfig (e.g., oauthConfig.clientId and oauthConfig.clientSecret or a specific oauthConfig.providerId) instead of just non-null object presence so false positives are avoided during provider routing.src/lib/types/subscriptionTypes.ts-845-873 (1)
845-873:⚠️ Potential issue | 🟡 MinorMisleading deprecation:
SubscriptionInfoSummaryis not equivalent toSubscriptionInfo.The comment states this type "has been consolidated into SubscriptionInfo" but the two types have different fields:
SubscriptionInfoSummaryhas:tierName,description,messagesPerDay,contextWindow,priorityAccess,expiresAtSubscriptionInfohas:startDate,renewalDate,rateLimit, but lacks the above fieldsThis could mislead developers into using
SubscriptionInfoas a drop-in replacement, causing runtime issues.📝 Proposed fix: Correct the deprecation notice
/** - * Subscription information summary (deprecated alias) + * Subscription information summary for display purposes * - * `@description` This type has been consolidated into SubscriptionInfo above. - * See the SubscriptionInfo type at line ~205 for the canonical definition. - * `@deprecated` Use SubscriptionInfo instead + * `@description` Extended subscription information including human-readable + * tier descriptions and usage data. Use for UI display and status reporting. + * For basic subscription state, see SubscriptionInfo. */ export type SubscriptionInfoSummary = {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/types/subscriptionTypes.ts` around lines 845 - 873, The deprecation note on SubscriptionInfoSummary is misleading because SubscriptionInfoSummary and SubscriptionInfo are not equivalent; update the comment on the SubscriptionInfoSummary type to remove the claim that it was consolidated into SubscriptionInfo and instead mark it as deprecated with a clear note that it is a legacy/summary shape that differs from SubscriptionInfo (reference the types SubscriptionInfoSummary and SubscriptionInfo in the comment) and advise which fields are missing or incompatible and whether migration is manual or not.src/lib/providers/anthropic.ts-514-514 (1)
514-514:⚠️ Potential issue | 🟡 MinorFix implicit
anytype onanthropicvariable.Static analysis correctly flagged this variable as having an implicit
anytype. This violates the strict TypeScript requirement.🔧 Proposed fix
- let anthropic; + let anthropic: ReturnType<typeof createAnthropic>;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/providers/anthropic.ts` at line 514, The variable "anthropic" currently has an implicit any; explicitly type it as the Anthropic client (or optional) by importing the client type from the Anthropic SDK and updating the declaration to something like "let anthropic: Anthropic | undefined" (or the specific exported client type name the package provides, e.g., "AnthropicClient"), ensuring you add an "import type { Anthropic } from 'anthropic'" (or the correct type name) at the top; if the SDK does not export a type, use "unknown" instead ("let anthropic: unknown" or "let anthropic: unknown | undefined") and narrow the type where it's used.src/lib/providers/anthropic.ts-232-261 (1)
232-261:⚠️ Potential issue | 🟡 MinorStreaming response transformation may fail on chunk boundaries.
The regex replacement
text.replace(/"name"\s*:\s*"mcp_([^"]+)"/g, ...)is applied to individual stream chunks. If a tool name spans across chunk boundaries (e.g.,"name": "mcp_in one chunk andmy_tool"in the next), themcp_prefix won't be stripped correctly.This is an edge case but could cause tool name mismatches in the response.
🔧 Proposed fix: Buffer incomplete JSON patterns across chunks
const stream = new ReadableStream({ + buffer: "", async pull(controller) { const { done, value } = await reader.read(); if (done) { + // Process any remaining buffered content + if (this.buffer) { + const text = this.buffer.replace(/"name"\s*:\s*"mcp_([^"]+)"/g, '"name": "$1"'); + controller.enqueue(encoder.encode(text)); + } controller.close(); return; } - let text = decoder.decode(value, { stream: true }); - // Remove mcp_ prefix from tool names in response - text = text.replace(/"name"\s*:\s*"mcp_([^"]+)"/g, '"name": "$1"'); - controller.enqueue(encoder.encode(text)); + let text = this.buffer + decoder.decode(value, { stream: true }); + // Check if text ends with a potential incomplete pattern + const lastQuoteIdx = text.lastIndexOf('"name"'); + if (lastQuoteIdx !== -1 && lastQuoteIdx > text.length - 50) { + // Buffer the potentially incomplete portion + this.buffer = text.slice(lastQuoteIdx); + text = text.slice(0, lastQuoteIdx); + } else { + this.buffer = ""; + } + // Remove mcp_ prefix from tool names in response + text = text.replace(/"name"\s*:\s*"mcp_([^"]+)"/g, '"name": "$1"'); + controller.enqueue(encoder.encode(text)); }, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/providers/anthropic.ts` around lines 232 - 261, The streaming transform in the ReadableStream pull uses decoder.decode per chunk so the regex in the pull function (text.replace(/"name"\s*:\s*"mcp_([^"]+)"/g,...)) can miss matches split across chunk boundaries; fix by adding a persistent buffer string outside pull (e.g., remainder) that you prepend to each decoded chunk, run the replacement on the combined string, but keep any trailing partial match in remainder (detect patterns like /"name"\s*:\s*"mcp_[^"]*$/) and only enqueue the fully-processed portion (excluding the new remainder); update references in this code block (reader, decoder, encoder, ReadableStream, pull) so the final Response stream strips mcp_ correctly even when tool names span chunks.
🧹 Nitpick comments (7)
src/lib/types/index.ts (1)
248-250: Remove duplicate HITL re-export.Line 249 duplicates the existing HITL barrel export from Line 203. Keeping only one export keeps the type index cleaner.
♻️ Proposed cleanup
-// HITL (Human-in-the-Loop) types -export * from "./hitlTypes.js"; - // Subscription types (Claude subscription tiers, authentication, usage tracking) export * from "./subscriptionTypes.js";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/types/index.ts` around lines 248 - 250, Remove the duplicate HITL barrel re-export: there are two identical lines exporting "./hitlTypes.js" (one is the diff-export shown as export * from "./hitlTypes.js";). Keep a single export * from "./hitlTypes.js"; and delete the redundant duplicate so the types index only re-exports HITL once.test/integration/anthropic-subscription.test.ts (1)
1562-1615: Several tests are tautological and don’t validate production behavior.These cases mostly assert local expressions (
!!process.env, string prefix checks) rather than invoking auth/config logic. They’ll pass even if actual detection/validation code regresses.Also applies to: 1848-1871
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/integration/anthropic-subscription.test.ts` around lines 1562 - 1615, The tests are tautological because they only inspect process.env instead of exercising the app's detection/config logic; replace those direct env assertions with calls to the project's production helpers (for example use hasAnthropicOAuthCredentials() as done already) and the config/getter functions that read ANTHROPIC_API_KEY, ANTHROPIC_SUBSCRIPTION_TIER and ANTHROPIC_MODEL (use the library functions that return the API key presence, subscription tier, and chosen model rather than raw process.env checks); update the tests to import and assert the outputs of those helpers so changes in detection logic will be caught.src/lib/auth/tokenStore.ts (1)
527-534: Use atomic write + permission hardening insaveStorageData.Line 533 writes directly to the final token file. This differs from
saveTokens(temp file + chmod + rename) and is more fragile for critical secret storage.💡 Suggested fix
private async saveStorageData(data: TokenStorageData): Promise<void> { try { const content = this.encryptionEnabled ? this.obfuscate(JSON.stringify(data)) : JSON.stringify(data, null, 2); - - await writeFile(this.storagePath, content, "utf-8"); + const tempPath = `${this.storagePath}.tmp`; + await writeFile(tempPath, content, "utf-8"); + await chmod(tempPath, TokenStore.FILE_PERMISSIONS); + await fs.rename(tempPath, this.storagePath); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new TokenStoreError(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/auth/tokenStore.ts` around lines 527 - 534, The saveStorageData method writes secrets directly to storagePath; change it to perform an atomic write like saveTokens does: serialize (using this.obfuscate when this.encryptionEnabled) to a temp file in the same directory, write the temp file, fsync it, set strict permissions (e.g., 0o600) with chmod, then rename/move the temp file to this.storagePath; ensure any errors clean up the temp file and preserve the same behavior/return type of saveStorageData and keep using this.obfuscate for encrypted content.src/lib/types/providers.ts (1)
425-433: Centralize runtime tier/auth validators to avoid drift.Line 425 and Line 426 duplicate literal values already defined in canonical subscription types. If tiers/auth methods evolve, this guard can silently reject valid Anthropic configs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/types/providers.ts` around lines 425 - 433, The duplicated literal arrays validAuthMethods and validSubscriptionTiers should be replaced with references to the canonical/shared constants to avoid drift; update the runtime guard in this file to import and reuse the project's authoritative auth/tier definitions (rather than hardcoding strings) — replace usages of validAuthMethods and validSubscriptionTiers with the imported canonical arrays/sets (and if needed wrap them in a Set for O(1) checks) so any future changes to the central subscription/auth enums automatically propagate here.src/lib/types/subscriptionTypes.ts (1)
49-74: Consider consolidatingOAuthTokenandOAuthTokenstypes.These two types are nearly identical but have subtle differences that could cause confusion:
Field OAuthToken(line 49)OAuthTokens(line 583)Scopes scopes?: string[]scope?: stringNaming Singular Plural Description "OAuth token structure" "OAuth tokens structure for storage" The difference in
scopes(array) vsscope(string) matches raw API response format vs parsed format, but this isn't clearly documented.📝 Proposed documentation clarification
/** * OAuth tokens structure for Claude subscription authentication * * `@description` Contains OAuth token information for authenticated sessions. - * This is the preferred type for OAuth token storage. + * This is the preferred type for OAuth token storage, matching the raw API response format. + * + * `@see` OAuthToken - Use this type for parsed/processed tokens where scopes are an array */ export type OAuthTokens = {Or consider deprecating one in favor of the other:
/** * OAuth tokens structure for Claude subscription authentication + * `@deprecated` Use OAuthToken instead - this type exists for backward compatibility */ export type OAuthTokens = {Also applies to: 583-608
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/types/subscriptionTypes.ts` around lines 49 - 74, The OAuthToken and OAuthTokens types are effectively duplicates with only subtle differences (notably OAuthToken.scopes?: string[] vs OAuthTokens.scope?: string) which is confusing; consolidate them by choosing a single canonical type name (e.g., OAuthToken) and unify the fields (use both rawResponseScope?: string and scopes?: string[] if you need to represent both API raw string and parsed array) or deprecate one type and add clear JSDoc on the remaining type explaining the raw API format vs parsed format; update references to OAuthTokens to use the canonical type and keep unique identifiers OAuthToken and OAuthTokens in the comments so reviewers can find and replace usages.src/lib/models/anthropicModels.ts (1)
401-420: Consider documenting tier ordering semantics.The
getMinimumTierForModelfunction checks tiers in a specific order where "api" comes last. This means for models available on "api" tier but not subscription tiers, the function returns "api" as the minimum. This is correct for wildcard-access models, but the ordering may cause confusion in other tier comparison contexts (e.g., "Isapitier higher thanmax_20?").Consider adding a clarifying comment about tier ordering semantics.
📝 Proposed documentation addition
/** * Get the minimum subscription tier required for a model * * `@param` model - The model ID to check * `@returns` The minimum tier required, or "api" if model not found + * + * `@remarks` + * Tiers are checked in order: free → pro → max → max_5 → max_20 → api. + * The "api" tier is checked last because it has wildcard access to all models, + * so returning "api" means the model isn't available through any subscription tier. * * `@example`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/models/anthropicModels.ts` around lines 401 - 420, getMinimumTierForModel's tierOrder places "api" last which makes it a fallback for wildcard-access models; add a clarifying comment above the tierOrder (or at the top of getMinimumTierForModel) that documents the semantics: tiers are checked from lowest subscription tier to highest subscription tier, with "api" intentionally placed last as a fallback representing wildcard/API-only availability (not necessarily a numeric "higher" subscription level). Reference the tierOrder array and isModelAvailableForTier in the comment so future readers understand why "api" is ordered last and how the function determines the returned minimum tier.src/lib/utils/providerConfig.ts (1)
725-740: Misleading function name and logic inneedsOAuthRefresh.The function name suggests it checks if OAuth refresh is needed (i.e., token is expired/expiring), but it actually returns
truewhenever a refresh token exists. This could confuse callers and lead to unnecessary refresh attempts.The comment on line 736-738 acknowledges this is a placeholder, but the function is exported and could be used by external code expecting proper expiration checking.
♻️ Proposed fix: Implement actual expiration checking or rename
/** - * Checks if OAuth refresh is needed based on token state - * This is a placeholder for actual token expiration checking + * Checks if OAuth refresh token is available + * NOTE: Does not check token expiration - use refreshAuthIfNeeded() in AnthropicProvider + * for actual expiration-based refresh logic. * `@returns` True if refresh is needed */ -export function needsOAuthRefresh(): boolean { +export function hasOAuthRefreshToken(): boolean { const auth = detectAnthropicAuth(); if (auth.method !== "oauth" || !auth.isConfigured) { return false; } - // In a real implementation, you would check token expiration - // For now, we just check if a refresh token is available - // The actual refresh logic would be in the OAuth client return !!auth.refreshToken; } + +/** + * `@deprecated` Use hasOAuthRefreshToken() instead - this name is misleading + */ +export const needsOAuthRefresh = hasOAuthRefreshToken;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/utils/providerConfig.ts` around lines 725 - 740, The exported function needsOAuthRefresh currently returns true whenever a refresh token exists, which is misleading; update the function (needsOAuthRefresh) to perform a real expiration check by using detectAnthropicAuth() fields (e.g., auth.expiresAt or auth.expiryTimestamp) and return true only if the access token is missing/expired or will expire within a short buffer (e.g., Date.now() + 5*60*1000), and fall back to false if no expiry info; alternatively, if expiry data is not available in detectAnthropicAuth(), rename the function to hasOAuthRefreshToken (and update all usages) so its behavior matches its name.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (33)
.gitignoreREADME.mddocs-site/sidebars.tsdocs/changelog.mddocs/cli/commands.mddocs/features/claude-subscription-testing.mddocs/features/claude-subscription.mddocs/features/index.mddocs/getting-started/environment-variables.mddocs/getting-started/provider-setup.mddocs/getting-started/providers/anthropic.mddocs/getting-started/providers/index.mddocs/index.mddocs/reference/provider-comparison.mdeslint.config.jspackage.jsonsrc/cli/commands/auth.tssrc/cli/factories/authCommandFactory.tssrc/cli/factories/commandFactory.tssrc/cli/parser.tssrc/lib/auth/anthropicOAuth.tssrc/lib/auth/index.tssrc/lib/auth/tokenStore.tssrc/lib/constants/enums.tssrc/lib/constants/index.tssrc/lib/models/anthropicModels.tssrc/lib/providers/anthropic.tssrc/lib/types/errors.tssrc/lib/types/index.tssrc/lib/types/providers.tssrc/lib/types/subscriptionTypes.tssrc/lib/utils/providerConfig.tstest/integration/anthropic-subscription.test.ts
| | :material-database-search: **[RAG Document Processing](rag.md)** | Comprehensive document chunking (10 strategies), hybrid search (BM25 + vector), and reranking (5 types) for retrieval-augmented generation. | | ||
| | :material-compress-arrows: **[Context Compaction](context-compaction.md)** | 4-stage context compaction pipeline with automatic budget management, per-provider token estimation, and non-destructive message tagging. | | ||
| | :material-brain: **[Memory](memory.md)** | Per-user condensed memory that persists across conversations. LLM-powered condensation with S3, Redis, or SQLite storage backends. | | ||
| | :material-account-key: **[Claude Subscription Support](claude-subscription.md)** | Multiple authentication methods for Claude (API key, OAuth) with support for Free, Pro, Max, and API tiers. | |
There was a problem hiding this comment.
Broken Claude subscription links are blocking docs build.
Line 25 and Line 92 currently resolve to /docs/claude-subscription in CI, which is reported as broken. Use an explicit same-folder relative path.
Suggested fix
-| :material-account-key: **[Claude Subscription Support](claude-subscription.md)** | Multiple authentication methods for Claude (API key, OAuth) with support for Free, Pro, Max, and API tiers. |
+| :material-account-key: **[Claude Subscription Support](./claude-subscription.md)** | Multiple authentication methods for Claude (API key, OAuth) with support for Free, Pro, Max, and API tiers. |
@@
-| **Anthropic** | Claude 4.5/4.0 Sonnet, Opus, Haiku | ❌ | ✅ Full | ✅ Production | [Setup Guide](../getting-started/provider-setup.md#anthropic), [Subscription Guide](claude-subscription.md) |
+| **Anthropic** | Claude 4.5/4.0 Sonnet, Opus, Haiku | ❌ | ✅ Full | ✅ Production | [Setup Guide](../getting-started/provider-setup.md#anthropic), [Subscription Guide](./claude-subscription.md) |Also applies to: 92-92
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/features/index.md` at line 25, The markdown links to the Claude
subscription doc are using an ambiguous path and resolve incorrectly in CI;
update the link targets in docs/features/index.md for the Claude Subscription
entries (the link text "**[Claude Subscription
Support](claude-subscription.md)**") to use an explicit same-folder relative
path (e.g., "./claude-subscription.md") for both occurrences (the ones at the
two listed positions) so the build resolves the file correctly.
| if (error) { | ||
| // OAuth error response | ||
| res.writeHead(400, { "Content-Type": "text/html" }); | ||
| res.end(` | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <head><title>Authentication Error</title></head> | ||
| <body> | ||
| <h1>Authentication Failed</h1> | ||
| <p>Error: ${error}</p> | ||
| <p>${errorDescription || "Please try again."}</p> | ||
| <p>You can close this window.</p> | ||
| </body> | ||
| </html> | ||
| `); | ||
| cleanup(); | ||
| reject( | ||
| new OAuthCallbackServerError( | ||
| `OAuth error: ${error} - ${errorDescription}`, | ||
| ), | ||
| ); |
There was a problem hiding this comment.
Escape callback query values before rendering HTML.
Line 892 and Line 893 reflect user-controlled query parameters directly into HTML. This is a reflected XSS risk.
🛡️ Suggested fix
+ const escapeHtml = (value: string): string =>
+ value
+ .replace(/&/g, "&")
+ .replace(/</g, "<")
+ .replace(/>/g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+
if (error) {
// OAuth error response
res.writeHead(400, { "Content-Type": "text/html" });
res.end(`
<!DOCTYPE html>
<html>
<head><title>Authentication Error</title></head>
<body>
<h1>Authentication Failed</h1>
- <p>Error: ${error}</p>
- <p>${errorDescription || "Please try again."}</p>
+ <p>Error: ${escapeHtml(error)}</p>
+ <p>${escapeHtml(errorDescription || "Please try again.")}</p>
<p>You can close this window.</p>
</body>
</html>
`);🧰 Tools
🪛 GitHub Check: CodeQL
[failure] 886-897: Reflected cross-site scripting
Cross-site scripting vulnerability due to a user-provided value.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/auth/anthropicOAuth.ts` around lines 883 - 903, The HTML response
directly inserts user-controlled query params (error and errorDescription)
causing reflected XSS; update the OAuth callback response in the handler (the
block that constructs the HTML, and where OAuthCallbackServerError is created)
to HTML-escape both error and errorDescription before interpolating them into
the template. Add or reuse a small safe-escape helper (e.g., escapeHtml or
encodeHtmlEntities) and call it on error and errorDescription (with fallback
text) when building the res.end HTML string and when composing the error
message, ensuring no raw query values are rendered unescaped.
| // Claude 3.5 Haiku | ||
| [AnthropicModel.CLAUDE_3_5_HAIKU]: { | ||
| displayName: "Claude 3.5 Haiku", | ||
| contextWindow: 200000, | ||
| maxOutputTokens: 8192, | ||
| supportsVision: false, | ||
| supportsExtendedThinking: false, | ||
| supportsToolUse: true, | ||
| supportsStreaming: true, | ||
| deprecated: false, | ||
| family: "haiku", | ||
| description: "Improved fast model with better performance", | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Claude 3.5 Haiku vision image support Anthropic documentation
💡 Result:
Yes—Claude 3.5 Haiku supports vision (image input).
- Anthropic’s model comparison table lists “Vision: Yes” for Claude Haiku 3.5 (API model:
claude-3-5-haiku-20241022, alias:claude-3-5-haiku-latest). [1] - Anthropic’s API release notes explicitly say they “added vision support to Claude Haiku 3.5”. [2]
- Anthropic’s Vision guide explains how to send images via Claude.ai, the Console Workbench, or the API, and notes you can include multiple images per request (up to 100 via API). [3]
- Google Cloud’s Vertex AI page for Claude 3.5 Haiku also lists Inputs: Text, Code, Images. [4]
Anthropic docs to use: the Models overview page (to confirm the model ID/alias + vision availability) and the Vision guide (to implement image input). [1] [3]
Sources: [1] [2] [3] [4]
Update Claude 3.5 Haiku vision support flag to true.
Claude 3.5 Haiku supports vision (image input) according to Anthropic's official documentation, API release notes, and model comparison table. The flag supportsVision: false is incorrect and should be changed to supportsVision: true.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/models/anthropicModels.ts` around lines 117 - 129, Update the Claude
3.5 Haiku model entry: change the supportsVision flag on the
AnthropicModel.CLAUDE_3_5_HAIKU object from false to true so the model correctly
reflects image-input support; locate the object literal for Claude 3.5 Haiku
(displayName "Claude 3.5 Haiku", family "haiku") and set supportsVision: true,
leaving all other properties unchanged.
04843a7 to
b3c75ec
Compare
|
@coderabbitai review Cycle 1 — Fixes AppliedAll issues from your initial review have been addressed in this amended commit: Security Fixes
Type System
Env Var Normalization
OAuth Refresh Improvements
OAuth Fetch Wrapper
Documentation
Not Fixed (intentional)
Verification
|
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 2263 🏁 Script executed: Length of output: 473 🏁 Script executed: Length of output: 4984 🏁 Script executed: Length of output: 2872 🏁 Script executed: Length of output: 3395 🏁 Script executed: Length of output: 2661 🏁 Script executed: Length of output: 161
✅ Verified FixesSecurity (Fixes 1-2)
Type System (Fix 3)
Env Var Normalization (Fix 4)
OAuth Refresh (Fixes 5-7)
OAuth Fetch Wrapper (Fixes 8-9)
Documentation (Fix 10)
Intentional DeferralsThe three deferred items are reasonable follow-ups:
Final VerdictAll critical issues resolved. The implementation is production-ready with proper security hardening (XSS, log redaction), concurrency safety (refresh lock), data integrity (atomic writes), and type safety (no collisions). Your test coverage (2,244 passed) and clean static analysis confirm the changes are solid. 🚢 Ready to merge. |
b3c75ec to
dae1f69
Compare
Cycle 2 Review Fixes (dae1f69)Security Fixes
Bug Fixes
Type Safety & Architecture
Documentation Fixes
Verification
@coderabbitai review |
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 1699 🏁 Script executed: Length of output: 2433 🏁 Script executed: Length of output: 2241 🏁 Script executed: Length of output: 1066 🏁 Script executed: Length of output: 417 🏁 Script executed: Length of output: 2164 🏁 Script executed: Length of output: 2755
✅ All Fixes Verified (25/25)Security Fixes (2/2) ✅
Bug Fixes (4/4) ✅
Type Safety & Architecture (6/6) ✅
Documentation Fixes (13/13) ✅
Final AssessmentAll 25 fixes correctly implemented. The code is production-ready:
🚢 Ready to merge! |
|
🎉 This PR is included in version 9.15.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Summary
Add full Claude Pro/Max subscription authentication alongside existing API key support for the Anthropic provider. This enables users to authenticate with their Claude subscription via OAuth 2.0 with PKCE, unlocking tier-based model access, usage tracking, and automatic token refresh.
34 files changed, +12,930 / -295 lines across 10 categories.
What's New
Core Auth Module (
src/lib/auth/)~/.neurolink/tokens.json): Multi-provider support, auto-refresh, atomic writes, XOR obfuscation,0o600file permissionsauth/index.tsType System (
src/lib/types/)subscriptionTypes.ts(1,083 lines): Canonical types —ClaudeSubscriptionTier(free/pro/max/max_5/max_20/api),OAuthToken,AnthropicAuthConfig,ClaudeUsageInfo,SubscriptionFeatures,OAuthFlowTokens,AnthropicModelMetadata, and 20+ moreerrors.ts: OAuth error hierarchy —OAuthErrorbase with 6 subclasses (OAuthConfigurationError,OAuthTokenExchangeError,OAuthTokenRefreshError,OAuthTokenValidationError,OAuthTokenRevocationError,OAuthCallbackServerError), plusTokenStoreError,ModelAccessErrorproviders.ts: ExtendedAnthropicProviderConfigwithoauthToken,oauthConfig, subscription tier, auth method fields, andisAnthropicConfig()type guardauth/,models/,providers/,utils/) intotypes/folder with backward-compatible re-exportsModel Tier Access (
src/lib/models/anthropicModels.ts)isModelAvailableForTier,getDefaultModelForTier,getMinimumTierForModel,validateModelAccess,compareTiers, etc.Provider Changes (
src/lib/providers/anthropic.ts, +954 lines)oauth-2025-04-20), tool namemcp_prefixing/stripping, User-Agent header,?beta=truequery paramClaudeUsageInfotrackingdetectSubscriptionTier,detectAuthMethod,validateModelAccessConfiguration (
src/lib/utils/providerConfig.ts, +755 lines)detectAnthropicAuth,getAnthropicAuthConfig,detectSubscriptionTier,shouldEnableBetaFeatures,getSubscriptionTierLimits,hasSubscriptionFeature,describeAnthropicConfigANTHROPIC_OAUTH_TOKEN,ANTHROPIC_SUBSCRIPTION_TIER,ANTHROPIC_ENABLE_BETA_FEATURES,ANTHROPIC_AUTH_METHOD,ANTHROPIC_OAUTH_REFRESH_TOKENCLI (
src/cli/)authcommand withlogin/logout/status/refreshsubcommandsapi-key(interactive),oauth(browser PKCE + manual code fallback),create-api-key(console OAuth → API key creation)AuthCommandFactoryfollowing existing factory pattern--authMethod,--subscriptionTier,--enableBetaexec()withexecFile()for browser opening (prevents command injection)Constants (
src/lib/constants/)AnthropicBetaFeatureenum,TOKEN_EXPIRY_BUFFER_MSconstantClaudeSubscriptionTierenum (4 values) in favor of canonical type alias (6 values)AnthropicAuthMethodenumDocumentation (3 new + 8 updated)
docs/features/claude-subscription.md(1,009 lines) — complete feature guide with SDK programmatic API sectiondocs/features/claude-subscription-testing.md(981 lines) — 99 test cases documenteddocs/getting-started/providers/anthropic.md(762 lines) — dedicated provider guideBuild/Config
opendependency for browser-based OAuth flowfetch,URL,Headers)coverage/to.gitignoreTests (99 tests across 7 suites)
Usage Examples
CLI
SDK
Test Plan
auth --helpdisplays correctlySummary by CodeRabbit
Release Notes
New Features
login,status,refresh,logoutfor credential managementDocumentation