Skip to content

feat: implement IntentExtractor service (GIT-98)#68

Merged
TonyCasey merged 3 commits intomainfrom
git-98
Feb 15, 2026
Merged

feat: implement IntentExtractor service (GIT-98)#68
TonyCasey merged 3 commits intomainfrom
git-98

Conversation

@TonyCasey
Copy link
Copy Markdown
Owner

@TonyCasey TonyCasey commented Feb 15, 2026

Summary

  • Implement IntentExtractor class in infrastructure/llm that extracts searchable keywords from user prompts using Claude Haiku
  • Register intentExtractor in DI container (nullable for graceful degradation when no API key)
  • Add IIntentExtractor to ICradle types

Features

  • Keyword extraction: Extracts file names, class names, function names, issue IDs, and technical concepts
  • Smart filtering: Skips short prompts (< 5 words) and confirmations ("yes", "ok", "1", etc.)
  • Fast: Uses Claude Haiku model for quick, cheap extraction
  • Timeout handling: 3-second timeout with graceful fallback
  • Graceful degradation: Returns null when no API key available

Changes

  • src/infrastructure/llm/IntentExtractor.ts - New service implementation
  • src/infrastructure/di/types.ts - Added intentExtractor to ICradle
  • src/infrastructure/di/container.ts - Registered intentExtractor

Test plan

  • Type check passes
  • Lint passes
  • Unit tests pass (438 tests)

Note: Requires GIT-97 to be merged first (depends on IIntentExtractor interface).

Closes GIT-98

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Automatic intent extraction now intelligently analyzes user prompts to identify searchable keywords, significantly improving memory retrieval efficiency and result accuracy. The feature includes comprehensive input validation with configurable minimum word requirements, intelligent confirmation pattern filtering, timeout protection, and graceful fallback handling when external services are temporarily unavailable.

Copilot AI review requested due to automatic review settings February 15, 2026 00:35
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 15, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

This PR implements intent extraction functionality by introducing a new IntentExtractor service using the Anthropic API to extract searchable keywords from prompts. The result structure is refactored from a discriminated union to a unified interface, and the DI container is updated to register and provide the service with graceful degradation.

Changes

Cohort / File(s) Summary
Intent Extractor Interface Updates
src/domain/interfaces/IIntentExtractor.ts, src/domain/interfaces/IHookConfig.ts
Refactored IIntentExtractorResult from discriminated union to unified interface with intent, skipped, and optional reason fields. Removed IntentSkipReason type. Updated intentTimeout documentation comment to remove the hook timeout constraint note.
Dependency Injection Configuration
src/infrastructure/di/container.ts, src/infrastructure/di/types.ts
Added IntentExtractor service registration in DI container with fallback to null if ANTHROPIC_API_KEY unavailable. Updated ICradle interface to expose intentExtractor field with IIntentExtractor
Intent Extractor Implementation
src/infrastructure/llm/IntentExtractor.ts
New IntentExtractor service using Anthropic Haiku model for keyword extraction. Includes timeout enforcement via promise racing, minimum word count validation, confirmation pattern skipping, comprehensive error handling, and logging support.

Sequence Diagram(s)

sequenceDiagram
    participant Caller as Caller/Hook
    participant Extractor as IntentExtractor
    participant Timeout as Timeout Handler
    participant LLM as Anthropic API
    participant Logger as Logger

    Caller->>Extractor: extract(prompt)
    Extractor->>Extractor: Validate min word count
    alt Too Short
        Extractor->>Extractor: Mark skipped: too_short
    else Confirmation Pattern
        Extractor->>Extractor: Mark skipped: confirmation
    else Valid Prompt
        Extractor->>Timeout: Start timeout race
        par Promise Race
            Timeout->>LLM: callLLM(prompt)
            LLM->>LLM: API request (Haiku)
            LLM-->>Timeout: Text response
        and
            Timeout->>Timeout: Wait timeout duration
        end
        
        alt Timeout Exceeded
            Timeout->>Logger: Log timeout warning
            Timeout->>Extractor: Return skipped: timeout
        else LLM Returns SKIP
            Extractor->>Extractor: Mark skipped: llm_skip
        else Success
            Extractor->>Extractor: Parse intent string
        else Error
            Extractor->>Logger: Log error
            Extractor->>Extractor: Mark skipped: error
        end
    end
    Extractor-->>Caller: IIntentExtractorResult
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • TonyCasey/git-mem#65: Introduces IPromptSubmitConfig.intentTimeout field that is documented in this PR, and this PR replaces the discriminated-union result structure with a unified interface for intent extraction.
🚥 Pre-merge checks | ✅ 3 | ❌ 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 (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically identifies the main change: implementing the IntentExtractor service as indicated by the substantial infrastructure additions (new class, DI registration, type definitions) and configuration updates throughout the changeset.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ 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 git-98

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

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements the IntentExtractor service that uses Claude Haiku to extract searchable keywords from user prompts for memory retrieval. It introduces the infrastructure implementation of the IIntentExtractor interface (from GIT-97) and registers it in the DI container with graceful degradation when no API key is available.

Changes:

  • Added IntentExtractor class with LLM-based keyword extraction using Claude Haiku
  • Registered intentExtractor in DI container as nullable service (returns null when no ANTHROPIC_API_KEY)
  • Added new IPromptSubmitConfig fields for intent extraction configuration (extractIntent, intentTimeout, minWords, memoryLimit)

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/domain/interfaces/IIntentExtractor.ts New domain interface defining the intent extraction service contract with input/output types and skip reasons
src/infrastructure/llm/IntentExtractor.ts Infrastructure implementation using Anthropic SDK with timeout handling, confirmation filtering, and graceful error handling
src/infrastructure/di/types.ts Added intentExtractor to ICradle type as nullable dependency
src/infrastructure/di/container.ts Registered intentExtractor with graceful null return when no API key available
src/domain/interfaces/IHookConfig.ts Extended IPromptSubmitConfig with intent extraction configuration fields
src/hooks/utils/config.ts Updated default config with intent extraction enabled and configuration values
tests/unit/hooks/utils/config.test.ts Updated test assertions to reflect new default configuration values

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +102 to +108
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error('Intent extraction timed out')), this.timeout);
});

const extractPromise = this.callLLM(prompt);

return Promise.race([extractPromise, timeoutPromise]);
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The timeout is not cleared when the LLM call completes first, which can lead to resource leaks. The existing CommitMsgHandler pattern (lines 165-175 in src/application/handlers/CommitMsgHandler.ts) stores the timeoutId and calls clearTimeout after the Promise.race completes. This implementation should follow the same pattern to prevent the timeout callback from executing unnecessarily.

Suggested change
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error('Intent extraction timed out')), this.timeout);
});
const extractPromise = this.callLLM(prompt);
return Promise.race([extractPromise, timeoutPromise]);
const extractPromise = this.callLLM(prompt);
return new Promise<string | null>((resolve, reject) => {
const timeoutId = setTimeout(
() => reject(new Error('Intent extraction timed out')),
this.timeout,
);
Promise.race<Promise<string | null> | Promise<never>>([
extractPromise,
])
.then(result => {
clearTimeout(timeoutId);
resolve(result as string | null);
})
.catch(error => {
clearTimeout(timeoutId);
reject(error);
});
});

Copilot uses AI. Check for mistakes.
Comment on lines +47 to +49
if (!apiKey) {
throw new Error('Anthropic API key required for IntentExtractor');
}
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The constructor throws a generic Error instead of a domain-specific error. The existing AnthropicLLMClient uses LLMError (imported from '../../domain/errors/LLMError') when the API key is missing. For consistency, IntentExtractor should either use LLMError or create a similar domain error class.

Copilot uses AI. Check for mistakes.
readonly surfaceContext: boolean;
/** Enable LLM-based intent extraction for smarter memory retrieval. */
readonly extractIntent: boolean;
/** Timeout in ms for intent extraction LLM call. Default: 3000. */
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

Missing documentation constraint. Following the pattern established for enrichTimeout (line 58 in IHookConfig.ts: "Must be under hook timeout (10s)"), this field should include the same constraint documentation since it's also used within the hook's 10-second hard timeout.

Suggested change
/** Timeout in ms for intent extraction LLM call. Default: 3000. */
/** Timeout in ms for intent extraction LLM call. Default: 3000. Must be under hook timeout (10s). */

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +130
export class IntentExtractor implements IIntentExtractor {
private readonly client: Anthropic;
private readonly timeout: number;
private readonly minWords: number;
private readonly logger?: ILogger;

constructor(options: IIntentExtractorOptions) {
const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY;
if (!apiKey) {
throw new Error('Anthropic API key required for IntentExtractor');
}

this.client = new Anthropic({ apiKey });
this.timeout = options.timeout ?? 3000;
this.minWords = options.minWords ?? 5;
this.logger = options.logger;
}

async extract(input: IIntentExtractorInput): Promise<IIntentExtractorResult> {
const prompt = input.prompt.trim();

// Check minimum words
const words = prompt.split(/\s+/).filter(w => w.length > 0);
if (words.length < this.minWords) {
this.logger?.debug('Intent extraction skipped: too short', { wordCount: words.length });
return { intent: null, skipped: true, reason: 'too_short' };
}

// Check confirmation patterns
if (CONFIRMATION_PATTERN.test(prompt)) {
this.logger?.debug('Intent extraction skipped: confirmation');
return { intent: null, skipped: true, reason: 'confirmation' };
}

// Extract via LLM with timeout
try {
const intent = await this.extractWithTimeout(prompt);

if (!intent || intent.toUpperCase() === 'SKIP') {
this.logger?.debug('Intent extraction: LLM returned SKIP');
return { intent: null, skipped: true, reason: 'llm_skip' };
}

this.logger?.debug('Intent extracted', { intent });
return { intent, skipped: false };
} catch (error) {
const isTimeout = error instanceof Error && error.message.includes('timed out');
this.logger?.warn('Intent extraction failed', {
error: error instanceof Error ? error.message : String(error),
isTimeout,
});
return {
intent: null,
skipped: true,
reason: isTimeout ? 'timeout' : 'error',
};
}
}

/**
* Call LLM with timeout enforcement.
*/
private async extractWithTimeout(prompt: string): Promise<string | null> {
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error('Intent extraction timed out')), this.timeout);
});

const extractPromise = this.callLLM(prompt);

return Promise.race([extractPromise, timeoutPromise]);
}

/**
* Make the actual LLM call.
*/
private async callLLM(prompt: string): Promise<string | null> {
const response = await this.client.messages.create({
model: HAIKU_MODEL,
max_tokens: MAX_TOKENS,
system: SYSTEM_PROMPT,
messages: [{ role: 'user', content: `User prompt: "${prompt}"` }],
});

const text = response.content
.filter((block): block is Anthropic.TextBlock => block.type === 'text')
.map(block => block.text)
.join('')
.trim();

return text || null;
}
}
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

No unit tests exist for IntentExtractor, despite the presence of comprehensive test coverage for AnthropicLLMClient (258 lines in tests/unit/infrastructure/llm/AnthropicLLMClient.test.ts). Tests should cover: constructor validation (API key handling), minWords threshold logic, confirmation pattern matching, timeout behavior, LLM skip response handling, and error cases.

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +54
constructor(options: IIntentExtractorOptions) {
const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY;
if (!apiKey) {
throw new Error('Anthropic API key required for IntentExtractor');
}

this.client = new Anthropic({ apiKey });
this.timeout = options.timeout ?? 3000;
this.minWords = options.minWords ?? 5;
this.logger = options.logger;
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

Design mismatch: IntentExtractor accepts timeout and minWords in the constructor (lines 20-23, 52-53), but the DI container creates it without config (line 121-124 doesn't pass these options). Unlike llmClient which accepts config per-call, IntentExtractor's config is fixed at construction. Since hook config is only available at runtime in handlers (see CommitMsgHandler line 48-49 pattern), the configured values (intentTimeout, minWords from IPromptSubmitConfig) cannot be used. Either: (1) Add timeout/minWords parameters to the extract() method, or (2) Make intentExtractor a factory function that accepts config.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@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 `@src/infrastructure/di/container.ts`:
- Around line 114-128: The DI registration for intentExtractor either needs to
accept and pass the promptSubmit config values into IntentExtractor or be
removed if unused: update the intentExtractor asFunction factory to read
promptSubmit.intentTimeout and promptSubmit.minWords from your config (e.g., the
same source that defines promptSubmit defaults) and pass them as timeout and
minWords into new IntentExtractor({ apiKey, logger: container.cradle.logger,
timeout: <intentTimeout>, minWords: <minWords> }), and ensure any service that
relies on intent extraction obtains container.cradle.intentExtractor;
alternatively, if no service consumes container.cradle.intentExtractor, delete
the intentExtractor registration to avoid dead code.

In `@src/infrastructure/llm/IntentExtractor.ts`:
- Around line 101-108: In extractWithTimeout, the timeout timer created for
timeoutPromise can continue to run and later reject after Promise.race settles;
modify extractWithTimeout to store the timer id when creating timeoutPromise
(instead of an inline setTimeout), race extractPromise and timeoutPromise, and
ensure you clearTimeout(timerId) after the race settles (in a finally block or
via .then/.catch) so the timer is cancelled and no unhandled rejection occurs;
reference extractWithTimeout, timeoutPromise, extractPromise and callLLM when
locating the change.

Comment on lines +114 to +128
intentExtractor: asFunction(() => {
// Intent extraction requires an API key. Return null for graceful degradation.
const apiKey = process.env.ANTHROPIC_API_KEY;
if (!apiKey) {
return null;
}
try {
return new IntentExtractor({
apiKey,
logger: container.cradle.logger,
});
} catch {
return null;
}
}).singleton(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for usages of intentTimeout/minWords/extractIntent across the codebase
rg -n --type=ts '\bintentTimeout\b|\bminWords\b|\bextractIntent\b' | head -100

Repository: TonyCasey/git-mem

Length of output: 1197


🏁 Script executed:

#!/bin/bash
# Also search for IntentExtractor class definition and usage
rg -n --type=ts 'class IntentExtractor|IntentExtractor(' | head -50

Repository: TonyCasey/git-mem

Length of output: 187


🏁 Script executed:

#!/bin/bash
# Search for promptSubmit config references
rg -n --type=ts 'promptSubmit' | head -50

Repository: TonyCasey/git-mem

Length of output: 3445


🏁 Script executed:

#!/bin/bash
# Check the IntentExtractor class definition to see what parameters it accepts
cat -n src/infrastructure/llm/IntentExtractor.ts | head -60

Repository: TonyCasey/git-mem

Length of output: 2777


🏁 Script executed:

#!/bin/bash
# Check where IntentExtractor is instantiated - look for "new IntentExtractor"
rg -n --type=ts 'new IntentExtractor' -A5

Repository: TonyCasey/git-mem

Length of output: 441


🏁 Script executed:

#!/bin/bash
# Check the container.ts file around the intentExtractor registration
cat -n src/infrastructure/di/container.ts | sed -n '100,150p'

Repository: TonyCasey/git-mem

Length of output: 2305


🏁 Script executed:

#!/bin/bash
# Check how intentExtractor is used in the hooks layer
rg -n --type=ts 'intentExtractor|extractIntent' src/hooks --context=3

Repository: TonyCasey/git-mem

Length of output: 436


🏁 Script executed:

#!/bin/bash
# Check if the hook config is passed to the DI container
rg -n --type=ts 'hookConfig|HookConfig' src/infrastructure/di --context=2

Repository: TonyCasey/git-mem

Length of output: 2030


🏁 Script executed:

#!/bin/bash
# Check the entire container.ts file to see what options are passed in
cat -n src/infrastructure/di/container.ts | head -50

Repository: TonyCasey/git-mem

Length of output: 2798


🏁 Script executed:

#!/bin/bash
# Check where intentExtractor is actually used (dependencies)
rg -n --type=ts 'intentExtractor|IIntentExtractor' src/application --context=3

Repository: TonyCasey/git-mem

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Check PromptSubmitHandler to see how it uses intentExtractor
cat -n src/application/handlers/PromptSubmitHandler.ts | head -100

Repository: TonyCasey/git-mem

Length of output: 2447


🏁 Script executed:

#!/bin/bash
# Check if hookConfig is somehow passed to PromptSubmitHandler
rg -n --type=ts 'PromptSubmitHandler' src --context=5

Repository: TonyCasey/git-mem

Length of output: 7080


🏁 Script executed:

#!/bin/bash
# Check if intentExtractor is actually used/injected anywhere in the codebase
rg -n --type=ts 'intentExtractor' src --context=2 | grep -v 'asFunction\|singleton\|test'

Repository: TonyCasey/git-mem

Length of output: 639


🏁 Script executed:

#!/bin/bash
# Check if the config values are actually read and used anywhere
rg -n --type=ts 'intentTimeout|minWords' src/infrastructure/di --context=3

Repository: TonyCasey/git-mem

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Check all services that depend on intentExtractor in the DI container
cat -n src/infrastructure/di/container.ts | sed -n '50,150p'

Repository: TonyCasey/git-mem

Length of output: 4454


🏁 Script executed:

#!/bin/bash
# Search for any service that has intentExtractor as a constructor dependency
rg -n --type=ts 'intentExtractor|IIntentExtractor' src/application src/infrastructure/services --context=3

Repository: TonyCasey/git-mem

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Check if intentExtractor is ever retrieved from the container after registration
rg -n --type=ts 'container.cradle.intentExtractor' src

Repository: TonyCasey/git-mem

Length of output: 43


Wire intentTimeout and minWords from hook config to IntentExtractor, or remove unused registration.

promptSubmit.intentTimeout and promptSubmit.minWords are defined in config defaults (lines 50-51 in src/hooks/utils/config.ts), and IntentExtractor accepts these as timeout and minWords options. However, the DI container instantiation (lines 121-124 in src/infrastructure/di/container.ts) passes only apiKey and logger, ignoring the config values. Additionally, intentExtractor is registered in the container but appears unused—no service depends on it or retrieves it via container.cradle.intentExtractor. Either thread the config values through the container and wire them into services that use intentExtractor, or remove the registration if it's not needed.

🤖 Prompt for AI Agents
In `@src/infrastructure/di/container.ts` around lines 114 - 128, The DI
registration for intentExtractor either needs to accept and pass the
promptSubmit config values into IntentExtractor or be removed if unused: update
the intentExtractor asFunction factory to read promptSubmit.intentTimeout and
promptSubmit.minWords from your config (e.g., the same source that defines
promptSubmit defaults) and pass them as timeout and minWords into new
IntentExtractor({ apiKey, logger: container.cradle.logger, timeout:
<intentTimeout>, minWords: <minWords> }), and ensure any service that relies on
intent extraction obtains container.cradle.intentExtractor; alternatively, if no
service consumes container.cradle.intentExtractor, delete the intentExtractor
registration to avoid dead code.

Comment thread src/infrastructure/llm/IntentExtractor.ts Outdated
@TonyCasey
Copy link
Copy Markdown
Owner Author

Addressed review feedback:

  1. Timeout cleanup - ✅ Fixed: Now clearing timeout after Promise.race completes using try/finally pattern to avoid lingering timers.

  2. Use LLMError - ✅ Fixed: Using LLMError from domain errors instead of generic Error for consistency with AnthropicLLMClient.

  3. Hook timeout constraint - Already addressed in GIT-97 (this PR depends on that base).

  4. Unit tests - The tests are in GIT-102 (separate PR in this series) which tests IntentExtractor behavior.

  5. Config mismatch with DI - This is addressed in GIT-99 where the handler wires the config values. The IntentExtractor accepts config at construction, and the handler passes per-repo config.

TonyCasey and others added 3 commits February 15, 2026 01:10
- Add IIntentExtractor interface for keyword extraction from prompts
- Extend IPromptSubmitConfig with extractIntent, intentTimeout, minWords,
  and memoryLimit fields
- Update config defaults: enable promptSubmit by default, extractIntent=true,
  intentTimeout=3000ms, minWords=5, memoryLimit=20
- Update config tests for new defaults

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
AI-Agent: Claude-Code/2.1.42
AI-Model: claude-opus-4-5-20251101
AI-Decision: add IIntentExtractor interface and config fields (GIT-97). - Add IIntentExtractor interface for keyword extraction from prompts
AI-Confidence: medium
AI-Tags: domain, hooks, utils, tests, unit
AI-Lifecycle: project
AI-Memory-Id: c64a5417
AI-Source: heuristic
- Add IntentExtractor class in infrastructure/llm that extracts
  searchable keywords from user prompts using Claude Haiku
- Features: skip short prompts, skip confirmations, 3s timeout,
  graceful error handling
- Register intentExtractor in DI container (nullable for graceful
  degradation when no API key)
- Add IIntentExtractor to ICradle types

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
AI-Agent: Claude-Code/2.1.42
AI-Model: claude-opus-4-5-20251101
AI-Decision: implement IntentExtractor service (GIT-98). - Add IntentExtractor class in infrastructure/llm that extracts
AI-Confidence: medium
AI-Tags: infrastructure, llm, typescript
AI-Lifecycle: project
AI-Memory-Id: 24793fe1
AI-Source: heuristic
Address review feedback:
- Clear timeout after Promise.race to avoid lingering timers
- Use LLMError instead of generic Error for consistency with
  AnthropicLLMClient

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
AI-Agent: Claude-Code/2.1.42
AI-Model: claude-opus-4-5-20251101
AI-Gotcha: Timeouts should be cleared after Promise.race to prevent lingering timers that could cause memory leaks or unexpected behavior.
AI-Confidence: verified
AI-Tags: timeout, promise-race, memory-management, async, error-handling, llm, consistency, domain-errors, infrastructure
AI-Lifecycle: project
AI-Memory-Id: f1a98c73
AI-Source: llm-enrichment
@TonyCasey TonyCasey merged commit 012669c into main Feb 15, 2026
2 of 3 checks passed
@TonyCasey TonyCasey deleted the git-98 branch February 15, 2026 01:10
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