diff --git a/src/hooks/anthropic-context-window-limit-recovery/parser.ts b/src/hooks/anthropic-context-window-limit-recovery/parser.ts index 451d1ab18..24483e0fa 100644 --- a/src/hooks/anthropic-context-window-limit-recovery/parser.ts +++ b/src/hooks/anthropic-context-window-limit-recovery/parser.ts @@ -70,7 +70,7 @@ function isTokenLimitError(text: string): boolean { return false } const lower = text.toLowerCase() - return TOKEN_LIMIT_KEYWORDS.some((kw) => lower.includes(kw.toLowerCase())) + return TOKEN_LIMIT_KEYWORDS.some((kw) => lower.includes(kw)) } export function parseAnthropicTokenLimitError(err: unknown): ParsedTokenLimitError | null { diff --git a/src/hooks/runtime-fallback/error-classifier.ts b/src/hooks/runtime-fallback/error-classifier.ts index 46d136b44..e581f3fb8 100644 --- a/src/hooks/runtime-fallback/error-classifier.ts +++ b/src/hooks/runtime-fallback/error-classifier.ts @@ -28,6 +28,8 @@ export function getErrorMessage(error: unknown): string { } } +const DEFAULT_RETRY_PATTERN = new RegExp(`\\b(${DEFAULT_CONFIG.retry_on_errors.join("|")})\\b`) + export function extractStatusCode(error: unknown, retryOnErrors?: number[]): number | undefined { if (!error) return undefined @@ -45,8 +47,9 @@ export function extractStatusCode(error: unknown, retryOnErrors?: number[]): num return statusCode } - const codes = retryOnErrors ?? DEFAULT_CONFIG.retry_on_errors - const pattern = new RegExp(`\\b(${codes.join("|")})\\b`) + const pattern = retryOnErrors + ? new RegExp(`\\b(${retryOnErrors.join("|")})\\b`) + : DEFAULT_RETRY_PATTERN const message = getErrorMessage(error) const statusMatch = message.match(pattern) if (statusMatch) { diff --git a/src/hooks/think-mode/detector.ts b/src/hooks/think-mode/detector.ts index 9ef7561a9..5fcd9f5c1 100644 --- a/src/hooks/think-mode/detector.ts +++ b/src/hooks/think-mode/detector.ts @@ -32,8 +32,10 @@ const MULTILINGUAL_KEYWORDS = [ "fikir", "berfikir", ] -const MULTILINGUAL_PATTERNS = MULTILINGUAL_KEYWORDS.map((kw) => new RegExp(kw, "i")) -const THINK_PATTERNS = [...ENGLISH_PATTERNS, ...MULTILINGUAL_PATTERNS] +const COMBINED_THINK_PATTERN = new RegExp( + `\\b(?:ultrathink|think)\\b|${MULTILINGUAL_KEYWORDS.join("|")}`, + "i" +) const CODE_BLOCK_PATTERN = /```[\s\S]*?```/g const INLINE_CODE_PATTERN = /`[^`]+`/g @@ -44,7 +46,7 @@ function removeCodeBlocks(text: string): string { export function detectThinkKeyword(text: string): boolean { const textWithoutCode = removeCodeBlocks(text) - return THINK_PATTERNS.some((pattern) => pattern.test(textWithoutCode)) + return COMBINED_THINK_PATTERN.test(textWithoutCode) } export function extractPromptText( diff --git a/src/tools/hashline-edit/edit-operations.ts b/src/tools/hashline-edit/edit-operations.ts index caec23071..13b010f95 100644 --- a/src/tools/hashline-edit/edit-operations.ts +++ b/src/tools/hashline-edit/edit-operations.ts @@ -11,6 +11,14 @@ import { } from "./edit-operation-primitives" import { validateLineRefs } from "./validation" +function arraysEqual(a: string[], b: string[]): boolean { + if (a.length !== b.length) return false + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false + } + return true +} + export interface HashlineApplyReport { content: string noopEdits: number @@ -51,7 +59,7 @@ export function applyHashlineEditsWithReport(content: string, edits: HashlineEdi const next = edit.end ? applyReplaceLines(lines, edit.pos, edit.end, edit.lines, { skipValidation: true }) : applySetLine(lines, edit.pos, edit.lines, { skipValidation: true }) - if (next.join("\n") === lines.join("\n")) { + if (arraysEqual(next, lines)) { noopEdits += 1 break } @@ -62,7 +70,7 @@ export function applyHashlineEditsWithReport(content: string, edits: HashlineEdi const next = edit.pos ? applyInsertAfter(lines, edit.pos, edit.lines, { skipValidation: true }) : applyAppend(lines, edit.lines) - if (next.join("\n") === lines.join("\n")) { + if (arraysEqual(next, lines)) { noopEdits += 1 break } @@ -73,7 +81,7 @@ export function applyHashlineEditsWithReport(content: string, edits: HashlineEdi const next = edit.pos ? applyInsertBefore(lines, edit.pos, edit.lines, { skipValidation: true }) : applyPrepend(lines, edit.lines) - if (next.join("\n") === lines.join("\n")) { + if (arraysEqual(next, lines)) { noopEdits += 1 break } diff --git a/src/tools/hashline-edit/hash-computation.ts b/src/tools/hashline-edit/hash-computation.ts index ac8d3f55c..a6bf8da78 100644 --- a/src/tools/hashline-edit/hash-computation.ts +++ b/src/tools/hashline-edit/hash-computation.ts @@ -86,15 +86,17 @@ export async function* streamHashLinesFromUtf8( pending += text const chunksToYield: string[] = [] + let lastIdx = 0 while (true) { - const idx = pending.indexOf("\n") + const idx = pending.indexOf("\n", lastIdx) if (idx === -1) break - const line = pending.slice(0, idx) - pending = pending.slice(idx + 1) + const line = pending.slice(lastIdx, idx) + lastIdx = idx + 1 endedWithNewline = true chunksToYield.push(...pushLine(line)) } + pending = pending.slice(lastIdx) if (pending.length > 0) endedWithNewline = false return chunksToYield }