mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-11-06 12:50:54 -04:00
fix: remove next 40 unused local findings (batch 5) (#2457)
This commit is contained in:
@@ -8,7 +8,6 @@ import {
|
||||
HEAP_PERCENTAGE_ENV,
|
||||
HEAP_PERCENTAGE_FLAG,
|
||||
HEAP_SIZE_ENV,
|
||||
HEAP_SIZE_FLAG,
|
||||
formatPercentageUnavailableStderr,
|
||||
getAvailableMemoryBytes,
|
||||
hasHeapLimitFlag,
|
||||
|
||||
@@ -97,10 +97,6 @@ async function resolveAtomicChatDefaultModel(): Promise<string | null> {
|
||||
return models[0] ?? null
|
||||
}
|
||||
|
||||
function runCommand(command: string, env: NodeJS.ProcessEnv): Promise<number> {
|
||||
return runProcess(command, [], env)
|
||||
}
|
||||
|
||||
export async function runProcess(command: string, args: string[], env: NodeJS.ProcessEnv): Promise<number> {
|
||||
try {
|
||||
const child = Bun.spawn([command, ...args], {
|
||||
|
||||
@@ -32,7 +32,7 @@ function getUserClaudeMdDisplayPath(): string {
|
||||
return getDisplayPath(join(getClaudeConfigHomeDir(), 'CLAUDE.md'));
|
||||
}
|
||||
export function ClaudeMdExternalIncludesDialog(t0: Props) {
|
||||
const $ = _c(18);
|
||||
_c(18);
|
||||
const { onDone, isStandaloneDialog, externalIncludes, scope } = t0;
|
||||
React.useEffect(_temp, []);
|
||||
const handleSelection = React.useCallback((value: 'yes' | 'no') => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { c as _c } from "react-compiler-runtime";
|
||||
import chalk from 'chalk';
|
||||
import { extname } from 'path';
|
||||
import React, { Suspense, use, useMemo } from 'react';
|
||||
import React, { Suspense, use } from 'react';
|
||||
import { Ansi, Text } from '../../ink.js';
|
||||
import { getCliHighlightPromise } from '../../utils/cliHighlight.js';
|
||||
import { logForDebugging } from '../../utils/debug.js';
|
||||
|
||||
@@ -496,7 +496,6 @@ function PromptInput({
|
||||
}, [rawFooterSelection, footerItemSelected, setAppState]);
|
||||
const tasksSelected = footerItemSelected === 'tasks';
|
||||
const tmuxSelected = footerItemSelected === 'tmux';
|
||||
const bagelSelected = footerItemSelected === 'bagel';
|
||||
const teamsSelected = footerItemSelected === 'teams';
|
||||
const bridgeSelected = footerItemSelected === 'bridge';
|
||||
function selectFooterItem(item: FooterItem | null): void {
|
||||
|
||||
@@ -401,7 +401,7 @@ class SDKSessionImpl implements SDKSession {
|
||||
requestSdkRootAbort(this._abortController, source, 'sdk_session')
|
||||
}
|
||||
// Deny all pending permission prompts before clearing
|
||||
for (const [toolUseId, pending] of this.pendingPermissionPrompts) {
|
||||
for (const [, pending] of this.pendingPermissionPrompts) {
|
||||
pending.resolve({
|
||||
behavior: 'deny',
|
||||
message: 'Session interrupted',
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import { writeFileSync, existsSync, mkdirSync, readFileSync } from 'fs'
|
||||
import { join, dirname } from 'path'
|
||||
import { join } from 'path'
|
||||
import { getAutoMemPath } from './paths.js'
|
||||
import { isAutoMemoryEnabled } from './paths.js'
|
||||
import { isMemoryWriteApprovalRequired } from '../utils/governancePolicy.js'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it, beforeEach, afterEach } from 'bun:test'
|
||||
import { mkdtempSync, writeFileSync, rmSync, existsSync, mkdirSync, symlinkSync } from 'fs'
|
||||
import { mkdtempSync, writeFileSync, rmSync, existsSync, symlinkSync } from 'fs'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
import {
|
||||
|
||||
@@ -225,7 +225,6 @@ import { IdeOnboardingDialog } from '../components/IdeOnboardingDialog.js';
|
||||
import { EffortCallout, shouldShowEffortCallout } from '../components/EffortCallout.js';
|
||||
import { getDisplayedEffortLevel, type EffortValue } from '../utils/effort.js';
|
||||
import { RemoteCallout } from '../components/RemoteCallout.js';
|
||||
import { getAPIProvider } from '../utils/model/providers.js';
|
||||
import { activityManager } from '../utils/activityManager.js';
|
||||
import { createAbortController } from '../utils/abortController.js';
|
||||
import {
|
||||
@@ -361,12 +360,6 @@ async function launchUltraplan(_opts: {
|
||||
throw new Error('Ultraplan is not available in this build');
|
||||
}
|
||||
|
||||
function median(values: number[]): number {
|
||||
const sorted = [...values].sort((a, b) => a - b);
|
||||
const mid = Math.floor(sorted.length / 2);
|
||||
return sorted.length % 2 === 0 ? Math.round((sorted[mid - 1]! + sorted[mid]!) / 2) : sorted[mid]!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Small component to display transcript mode footer with dynamic keybinding.
|
||||
* Must be rendered inside KeybindingSetup to access keybinding context.
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
getVendor,
|
||||
} from '../../integrations/index.js'
|
||||
import {
|
||||
getRouteDescriptor,
|
||||
resolveRouteCredentialValue,
|
||||
} from '../../integrations/routeMetadata.js'
|
||||
import { firstUsableCredential, hasInvalidCredentialPlaceholder } from './credentialPool.js'
|
||||
|
||||
@@ -357,26 +357,6 @@ async function drainGenerator<T>(
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForPromise<T>(
|
||||
promise: Promise<T>,
|
||||
timeoutMs: number,
|
||||
timeoutMessage: string,
|
||||
): Promise<T> {
|
||||
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
||||
try {
|
||||
return await Promise.race([
|
||||
promise,
|
||||
new Promise<never>((_, reject) => {
|
||||
timeoutId = setTimeout(() => {
|
||||
reject(new Error(timeoutMessage))
|
||||
}, timeoutMs)
|
||||
}),
|
||||
])
|
||||
} finally {
|
||||
if (timeoutId !== undefined) clearTimeout(timeoutId)
|
||||
}
|
||||
}
|
||||
|
||||
function makeParams(context: { model: string }): BetaMessageStreamParams {
|
||||
return {
|
||||
model: context.model,
|
||||
|
||||
@@ -91,7 +91,6 @@ import { executeOpenAIRequest } from './openaiShim/requestExecutor.js'
|
||||
import {
|
||||
getLocalProviderRetryBaseUrls,
|
||||
isAzureStyleBaseUrl,
|
||||
isLocalProviderUrl,
|
||||
resolveProviderRequest,
|
||||
shouldAttemptLocalToollessRetry,
|
||||
} from './providerConfig.js'
|
||||
@@ -103,11 +102,6 @@ import {
|
||||
} from './openaiErrorClassification.js'
|
||||
import { redactSecretValueForDisplay } from '../../utils/providerProfile.js'
|
||||
import { logApiCallStart, logApiCallEnd } from '../../utils/requestLogging.js'
|
||||
import {
|
||||
createStreamState,
|
||||
processStreamChunk,
|
||||
getStreamStats,
|
||||
} from '../../utils/streamingOptimizer.js'
|
||||
import { stableStringifyJson } from '../../utils/stableStringify.js'
|
||||
import {
|
||||
type NonStreamingOpenAIResponse,
|
||||
@@ -124,7 +118,6 @@ import {
|
||||
hasGeminiApiHost as matchesGeminiApiHost,
|
||||
hasMistralApiHost,
|
||||
isGithubModelsMode,
|
||||
isGeminiModelName,
|
||||
mergeGeminiThoughtSignature,
|
||||
shouldPreserveGeminiThoughtSignature as shouldPreserveGeminiThoughtSignatureForRoute,
|
||||
} from './openaiShim/providerCompatibility.js'
|
||||
@@ -147,8 +140,6 @@ import {
|
||||
const GITHUB_429_MAX_RETRIES = 3
|
||||
const GITHUB_429_BASE_DELAY_SEC = 1
|
||||
const GITHUB_429_MAX_DELAY_SEC = 32
|
||||
const CREDENTIAL_POOL_COOLDOWN_MS = 30_000
|
||||
const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 90_000
|
||||
const GEMINI_API_HOST = 'generativelanguage.googleapis.com'
|
||||
function isCopilotTokenExpiredError(text: string): boolean {
|
||||
const lower = text.toLowerCase()
|
||||
|
||||
@@ -158,185 +158,6 @@ function makeStallingResponse(
|
||||
}
|
||||
}
|
||||
|
||||
type ShimStream = AsyncIterable<Record<string, unknown>> & {
|
||||
controller: AbortController
|
||||
}
|
||||
|
||||
type StreamDrainOutcome =
|
||||
| { status: 'completed'; events: Array<Record<string, unknown>> }
|
||||
| {
|
||||
status: 'rejected'
|
||||
events: Array<Record<string, unknown>>
|
||||
error: unknown
|
||||
}
|
||||
|
||||
async function waitForPromise<T>(
|
||||
promise: Promise<T>,
|
||||
timeoutMs: number,
|
||||
message: string,
|
||||
): Promise<T> {
|
||||
let timer: ReturnType<typeof setTimeout> | undefined
|
||||
try {
|
||||
return await Promise.race([
|
||||
promise,
|
||||
new Promise<T>((_, reject) => {
|
||||
timer = setTimeout(() => reject(new Error(message)), timeoutMs)
|
||||
}),
|
||||
])
|
||||
} finally {
|
||||
if (timer !== undefined) {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function expectAbortStopsStream({
|
||||
abort,
|
||||
cancelReasons,
|
||||
expectedEventsBeforeAbort,
|
||||
label,
|
||||
stream,
|
||||
}: {
|
||||
abort: () => void
|
||||
cancelReasons: unknown[]
|
||||
expectedEventsBeforeAbort: number
|
||||
label: string
|
||||
stream: ShimStream
|
||||
}): Promise<StreamDrainOutcome> {
|
||||
const events: Array<Record<string, unknown>> = []
|
||||
let resolveReady!: () => void
|
||||
const ready = new Promise<void>(resolve => {
|
||||
resolveReady = resolve
|
||||
})
|
||||
|
||||
const drain = (async (): Promise<StreamDrainOutcome> => {
|
||||
try {
|
||||
for await (const event of stream) {
|
||||
events.push(event)
|
||||
if (events.length >= expectedEventsBeforeAbort) {
|
||||
resolveReady()
|
||||
}
|
||||
}
|
||||
return { status: 'completed', events }
|
||||
} catch (error) {
|
||||
return { status: 'rejected', events, error }
|
||||
}
|
||||
})()
|
||||
|
||||
await waitForPromise(
|
||||
ready,
|
||||
500,
|
||||
`${label} did not produce initial stream events`,
|
||||
)
|
||||
// Let the for-await loop ask the stream reader for the next chunk, so the
|
||||
// abort has to wake a real pending read rather than only flipping a flag.
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
|
||||
abort()
|
||||
|
||||
const outcome = await waitForPromise(
|
||||
drain,
|
||||
500,
|
||||
`${label} did not stop promptly after abort`,
|
||||
)
|
||||
expect(cancelReasons).toHaveLength(1)
|
||||
expect(outcome.status).toBe('rejected')
|
||||
if (outcome.status === 'rejected') {
|
||||
expect((outcome.error as { name?: unknown }).name).toBe('AbortError')
|
||||
}
|
||||
return outcome
|
||||
}
|
||||
|
||||
async function expectPausedAbortCancelsStream({
|
||||
cancelReasons,
|
||||
label,
|
||||
stream,
|
||||
}: {
|
||||
cancelReasons: unknown[]
|
||||
label: string
|
||||
stream: ShimStream
|
||||
}): Promise<IteratorResult<Record<string, unknown>>> {
|
||||
const iterator = stream[Symbol.asyncIterator]()
|
||||
const first = await waitForPromise(
|
||||
iterator.next(),
|
||||
500,
|
||||
`${label} did not produce first stream event`,
|
||||
)
|
||||
expect(first.done).toBe(false)
|
||||
|
||||
stream.controller.abort()
|
||||
await waitForPromise(
|
||||
(async () => {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
if (cancelReasons.length > 0) return
|
||||
await Promise.resolve()
|
||||
}
|
||||
throw new Error(`${label} did not cancel source on controller abort`)
|
||||
})(),
|
||||
500,
|
||||
`${label} did not cancel source on controller abort`,
|
||||
)
|
||||
|
||||
const returned = await waitForPromise(
|
||||
Promise.resolve(iterator.return?.()),
|
||||
500,
|
||||
`${label} did not return promptly after abort while paused`,
|
||||
)
|
||||
expect(cancelReasons).toHaveLength(1)
|
||||
return returned as IteratorResult<Record<string, unknown>>
|
||||
}
|
||||
|
||||
async function expectBufferedAbortRejectsNext({
|
||||
expectedText,
|
||||
label,
|
||||
stream,
|
||||
}: {
|
||||
expectedText?: string
|
||||
label: string
|
||||
stream: ShimStream
|
||||
}): Promise<void> {
|
||||
const iterator = stream[Symbol.asyncIterator]()
|
||||
|
||||
try {
|
||||
let firstDelta: Record<string, unknown> | undefined
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const next = await waitForPromise(
|
||||
iterator.next(),
|
||||
500,
|
||||
`${label} did not produce expected pre-abort events`,
|
||||
)
|
||||
expect(next.done).toBe(false)
|
||||
if (next.value?.type === 'content_block_delta') {
|
||||
firstDelta = next.value
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
expect(firstDelta).toBeDefined()
|
||||
if (expectedText !== undefined) {
|
||||
expect((firstDelta as { delta?: { text?: string } }).delta?.text).toBe(expectedText)
|
||||
}
|
||||
|
||||
stream.controller.abort()
|
||||
const afterAbort = await waitForPromise(
|
||||
iterator.next().then(
|
||||
value => ({ status: 'resolved' as const, value }),
|
||||
error => ({ status: 'rejected' as const, error }),
|
||||
),
|
||||
500,
|
||||
`${label} did not stop after abort`,
|
||||
)
|
||||
|
||||
if (afterAbort.status !== 'rejected') {
|
||||
throw new Error(`${label} yielded after abort: ${JSON.stringify(afterAbort.value)}`)
|
||||
}
|
||||
expect((afterAbort.error as { name?: unknown }).name).toBe('AbortError')
|
||||
} finally {
|
||||
await Promise.resolve(iterator.return?.()).catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
function makeOpenAIStreamFrame(
|
||||
delta: Record<string, unknown>,
|
||||
finishReason: string | null = null,
|
||||
|
||||
@@ -5,15 +5,13 @@ import { CannotRetryError, withRetry } from '../withRetry.js'
|
||||
import { afterEach, beforeEach, expect, jest, mock, test } from 'bun:test'
|
||||
import { acquireSharedMutationLock, releaseSharedMutationLock } from '../../../test/sharedMutationLock.js'
|
||||
import { asMockFetch } from '../../../test/typedMocks.js'
|
||||
import { _clearRegistryForTesting, ensureIntegrationsLoaded, registerGateway } from '../../../integrations/index.ts'
|
||||
import { _clearRegistryForTesting, ensureIntegrationsLoaded } from '../../../integrations/index.ts'
|
||||
import { applyProviderFlag } from '../../../utils/providerFlag.ts'
|
||||
import { applyProviderProfileToProcessEnv } from '../../../utils/providerProfiles.ts'
|
||||
import {
|
||||
categorizeRetryableAPIError,
|
||||
getAssistantMessageFromError,
|
||||
OPENCODE_GO_FREE_LIMIT_ERROR_MESSAGE,
|
||||
} from '../errors.ts'
|
||||
import { createOpenAIShimClient, hasMistralApiHost } from '../openaiShim.ts'
|
||||
import { createOpenAIShimClient } from '../openaiShim.ts'
|
||||
import { formatRetryAfterHint, sleepMs } from './requestExecutor.js'
|
||||
import * as realGithubModelsCredentials from '../../../utils/githubModelsCredentials.js'
|
||||
|
||||
@@ -112,255 +110,6 @@ function makeSseResponse(lines: string[]): Response {
|
||||
)
|
||||
}
|
||||
|
||||
function withResponseUrl(response: Response, url: string): Response {
|
||||
Object.defineProperty(response, 'url', {
|
||||
value: url,
|
||||
configurable: true,
|
||||
})
|
||||
return response
|
||||
}
|
||||
|
||||
type StallingResponse = {
|
||||
response: Response
|
||||
cancelReasons: unknown[]
|
||||
close: () => void
|
||||
}
|
||||
|
||||
function makeStallingResponse(
|
||||
firstChunk: string,
|
||||
url = 'https://api.example.test/v1/chat/completions',
|
||||
contentType = 'text/event-stream',
|
||||
): StallingResponse {
|
||||
const encoder = new TextEncoder()
|
||||
const cancelReasons: unknown[] = []
|
||||
let streamController: ReadableStreamDefaultController<Uint8Array> | undefined
|
||||
let closed = false
|
||||
|
||||
const response = new Response(
|
||||
new ReadableStream<Uint8Array>({
|
||||
start(controller) {
|
||||
streamController = controller
|
||||
controller.enqueue(encoder.encode(firstChunk))
|
||||
},
|
||||
cancel(reason) {
|
||||
closed = true
|
||||
cancelReasons.push(reason)
|
||||
},
|
||||
}),
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': contentType,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
return {
|
||||
response: withResponseUrl(response, url),
|
||||
cancelReasons,
|
||||
close: () => {
|
||||
if (closed) return
|
||||
closed = true
|
||||
try {
|
||||
streamController?.close()
|
||||
} catch {
|
||||
// The test may already have cancelled the stream.
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type ShimStream = AsyncIterable<Record<string, unknown>> & {
|
||||
controller: AbortController
|
||||
}
|
||||
|
||||
type StreamDrainOutcome =
|
||||
| { status: 'completed'; events: Array<Record<string, unknown>> }
|
||||
| {
|
||||
status: 'rejected'
|
||||
events: Array<Record<string, unknown>>
|
||||
error: unknown
|
||||
}
|
||||
|
||||
async function waitForPromise<T>(
|
||||
promise: Promise<T>,
|
||||
timeoutMs: number,
|
||||
message: string,
|
||||
): Promise<T> {
|
||||
let timer: ReturnType<typeof setTimeout> | undefined
|
||||
try {
|
||||
return await Promise.race([
|
||||
promise,
|
||||
new Promise<T>((_, reject) => {
|
||||
timer = setTimeout(() => reject(new Error(message)), timeoutMs)
|
||||
}),
|
||||
])
|
||||
} finally {
|
||||
if (timer !== undefined) {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function expectAbortStopsStream({
|
||||
abort,
|
||||
cancelReasons,
|
||||
expectedEventsBeforeAbort,
|
||||
label,
|
||||
stream,
|
||||
}: {
|
||||
abort: () => void
|
||||
cancelReasons: unknown[]
|
||||
expectedEventsBeforeAbort: number
|
||||
label: string
|
||||
stream: ShimStream
|
||||
}): Promise<StreamDrainOutcome> {
|
||||
const events: Array<Record<string, unknown>> = []
|
||||
let resolveReady!: () => void
|
||||
const ready = new Promise<void>(resolve => {
|
||||
resolveReady = resolve
|
||||
})
|
||||
|
||||
const drain = (async (): Promise<StreamDrainOutcome> => {
|
||||
try {
|
||||
for await (const event of stream) {
|
||||
events.push(event)
|
||||
if (events.length >= expectedEventsBeforeAbort) {
|
||||
resolveReady()
|
||||
}
|
||||
}
|
||||
return { status: 'completed', events }
|
||||
} catch (error) {
|
||||
return { status: 'rejected', events, error }
|
||||
}
|
||||
})()
|
||||
|
||||
await waitForPromise(
|
||||
ready,
|
||||
500,
|
||||
`${label} did not produce initial stream events`,
|
||||
)
|
||||
// Let the for-await loop ask the stream reader for the next chunk, so the
|
||||
// abort has to wake a real pending read rather than only flipping a flag.
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
|
||||
abort()
|
||||
|
||||
const outcome = await waitForPromise(
|
||||
drain,
|
||||
500,
|
||||
`${label} did not stop promptly after abort`,
|
||||
)
|
||||
expect(cancelReasons).toHaveLength(1)
|
||||
expect(outcome.status).toBe('rejected')
|
||||
if (outcome.status === 'rejected') {
|
||||
expect((outcome.error as { name?: unknown }).name).toBe('AbortError')
|
||||
}
|
||||
return outcome
|
||||
}
|
||||
|
||||
async function expectPausedAbortCancelsStream({
|
||||
cancelReasons,
|
||||
label,
|
||||
stream,
|
||||
}: {
|
||||
cancelReasons: unknown[]
|
||||
label: string
|
||||
stream: ShimStream
|
||||
}): Promise<IteratorResult<Record<string, unknown>>> {
|
||||
const iterator = stream[Symbol.asyncIterator]()
|
||||
const first = await waitForPromise(
|
||||
iterator.next(),
|
||||
500,
|
||||
`${label} did not produce first stream event`,
|
||||
)
|
||||
expect(first.done).toBe(false)
|
||||
|
||||
stream.controller.abort()
|
||||
await waitForPromise(
|
||||
(async () => {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
if (cancelReasons.length > 0) return
|
||||
await Promise.resolve()
|
||||
}
|
||||
throw new Error(`${label} did not cancel source on controller abort`)
|
||||
})(),
|
||||
500,
|
||||
`${label} did not cancel source on controller abort`,
|
||||
)
|
||||
|
||||
const returned = await waitForPromise(
|
||||
Promise.resolve(iterator.return?.()),
|
||||
500,
|
||||
`${label} did not return promptly after abort while paused`,
|
||||
)
|
||||
expect(cancelReasons).toHaveLength(1)
|
||||
return returned as IteratorResult<Record<string, unknown>>
|
||||
}
|
||||
|
||||
async function expectBufferedAbortRejectsNext({
|
||||
expectedText,
|
||||
label,
|
||||
stream,
|
||||
}: {
|
||||
expectedText?: string
|
||||
label: string
|
||||
stream: ShimStream
|
||||
}): Promise<void> {
|
||||
const iterator = stream[Symbol.asyncIterator]()
|
||||
|
||||
try {
|
||||
let firstDelta: Record<string, unknown> | undefined
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const next = await waitForPromise(
|
||||
iterator.next(),
|
||||
500,
|
||||
`${label} did not produce expected pre-abort events`,
|
||||
)
|
||||
expect(next.done).toBe(false)
|
||||
if (next.value?.type === 'content_block_delta') {
|
||||
firstDelta = next.value
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
expect(firstDelta).toBeDefined()
|
||||
if (expectedText !== undefined) {
|
||||
expect((firstDelta as { delta?: { text?: string } }).delta?.text).toBe(expectedText)
|
||||
}
|
||||
|
||||
stream.controller.abort()
|
||||
const afterAbort = await waitForPromise(
|
||||
iterator.next().then(
|
||||
value => ({ status: 'resolved' as const, value }),
|
||||
error => ({ status: 'rejected' as const, error }),
|
||||
),
|
||||
500,
|
||||
`${label} did not stop after abort`,
|
||||
)
|
||||
|
||||
if (afterAbort.status !== 'rejected') {
|
||||
throw new Error(`${label} yielded after abort: ${JSON.stringify(afterAbort.value)}`)
|
||||
}
|
||||
expect((afterAbort.error as { name?: unknown }).name).toBe('AbortError')
|
||||
} finally {
|
||||
await Promise.resolve(iterator.return?.()).catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
function makeOpenAIStreamFrame(
|
||||
delta: Record<string, unknown>,
|
||||
finishReason: string | null = null,
|
||||
): string {
|
||||
return `data: ${JSON.stringify({
|
||||
id: 'chatcmpl-abort-test',
|
||||
object: 'chat.completion.chunk',
|
||||
created: 1_780_000_000,
|
||||
model: 'test-model',
|
||||
choices: [{ index: 0, delta, finish_reason: finishReason }],
|
||||
})}\n\n`
|
||||
}
|
||||
|
||||
function makeStreamChunks(chunks: unknown[]): string[] {
|
||||
return [
|
||||
...chunks.map(chunk => `data: ${JSON.stringify(chunk)}\n\n`),
|
||||
@@ -374,25 +123,6 @@ function importFreshOpenAIShim(
|
||||
return import(`../openaiShim.ts?${cacheKey}`)
|
||||
}
|
||||
|
||||
type StreamIdleTestApi = {
|
||||
StreamIdleTimeoutError: new (timeoutMs: number) => Error
|
||||
getStreamIdleTimeoutMs: () => number
|
||||
readWithIdleTimeout: (
|
||||
reader: ReadableStreamDefaultReader<Uint8Array>,
|
||||
timeoutMs: number,
|
||||
options?: { signal?: AbortSignal; onTimeout?: () => void },
|
||||
) => Promise<Awaited<ReturnType<ReadableStreamDefaultReader<Uint8Array>['read']>>>
|
||||
}
|
||||
|
||||
async function getStreamIdleTestApi(cacheKey: string): Promise<StreamIdleTestApi> {
|
||||
const mod = await importFreshOpenAIShim(cacheKey)
|
||||
const testApi = mod.__test as unknown as Partial<StreamIdleTestApi>
|
||||
expect(typeof testApi.StreamIdleTimeoutError).toBe('function')
|
||||
expect(typeof testApi.getStreamIdleTimeoutMs).toBe('function')
|
||||
expect(typeof testApi.readWithIdleTimeout).toBe('function')
|
||||
return testApi as StreamIdleTestApi
|
||||
}
|
||||
|
||||
function makeChatCompletionResponse(model: string): Response {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
|
||||
@@ -229,7 +229,6 @@ export async function executeOpenAIRequest(
|
||||
params,
|
||||
options,
|
||||
requestProcessEnv,
|
||||
fastPath,
|
||||
shimConfig,
|
||||
runtimeShimContext,
|
||||
body,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
afterAll,
|
||||
afterEach,
|
||||
beforeAll,
|
||||
beforeEach,
|
||||
describe,
|
||||
expect,
|
||||
@@ -20,7 +19,6 @@ import {
|
||||
releaseSharedMutationLock,
|
||||
} from '../../test/sharedMutationLock.js'
|
||||
import type { Message } from '../../types/message.js'
|
||||
import * as realConfig from '../../utils/config.js'
|
||||
import {
|
||||
__getInterruptionTraceSnapshotForTests,
|
||||
__resetInterruptionTraceForTests,
|
||||
|
||||
@@ -67,20 +67,6 @@ afterAll(async () => {
|
||||
}
|
||||
})
|
||||
|
||||
function makeCtx() {
|
||||
const toolPermissionContext = getEmptyToolPermissionContext()
|
||||
return {
|
||||
abortController: new AbortController(),
|
||||
agentId: undefined,
|
||||
options: { isNonInteractiveSession: false, tools: [] },
|
||||
getAppState: () => ({ toolPermissionContext } as never),
|
||||
setAppState: () => undefined,
|
||||
setToolJSX: undefined,
|
||||
toolUseId: 'test-exit-plan-mode',
|
||||
addNotification: undefined,
|
||||
} as never
|
||||
}
|
||||
|
||||
test('surfaces write error when plan file write fails and asserts no side effects (standard)', async () => {
|
||||
const persistFileSnapshotIfRemoteMock = mock(() => Promise.resolve())
|
||||
const unwritablePlanPath = makeUnwritablePlanPath()
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
import { isEnvDefinedFalsy } from '../../../utils/envUtils.js'
|
||||
import type { SearchInput, SearchProvider } from './types.js'
|
||||
import { applyDomainFilters, safeHostname, type ProviderOutput } from './types.js'
|
||||
import { safeHostname, type ProviderOutput } from './types.js'
|
||||
import { fetchJsonWithWebSearchTimeout } from './timeout.js'
|
||||
|
||||
export const DEFAULT_EXA_NUM_RESULTS = 15
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
rebuildIndex,
|
||||
clearIndex,
|
||||
} from '../memdir/vectorIndex.js'
|
||||
import { extractKeywords } from './knowledgeGraph.js'
|
||||
import { isMemoryWriteApprovalRequired } from './governancePolicy.js'
|
||||
import { sanitizeMemoryIdentifier, sanitizeMemoryText } from '../memdir/memorySecurity.js'
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import { readFileSync, existsSync, readdirSync, rmSync, mkdirSync, writeFileSync, statSync } from 'fs'
|
||||
import { join, basename } from 'path'
|
||||
import { join } from 'path'
|
||||
import { getAutoMemPath } from '../memdir/paths.js'
|
||||
import { searchMemdirIndex, clearIndex, getIndexPath, getIndexMetaPath } from '../memdir/vectorIndex.js'
|
||||
import { parseFrontmatter } from './frontmatterParser.js'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterEach, beforeAll, beforeEach, expect, test } from 'bun:test'
|
||||
import { applyAgentProviderOverrideToEnv } from '../services/api/agentRouting.js'
|
||||
import { ensureIntegrationsLoaded, getAllGateways } from '../integrations/index.js'
|
||||
import { ensureIntegrationsLoaded } from '../integrations/index.js'
|
||||
import {
|
||||
acquireSharedMutationLock,
|
||||
releaseSharedMutationLock,
|
||||
|
||||
@@ -79,7 +79,7 @@ import { updateSessionName } from './concurrentSessions.js'
|
||||
import { getCwd } from './cwd.js'
|
||||
import { logForDebugging } from './debug.js'
|
||||
import { logForDiagnosticsNoPII } from './diagLogs.js'
|
||||
import { getClaudeConfigHomeDir, getProjectsDir, isEnvTruthy } from './envUtils.js'
|
||||
import { getProjectsDir, isEnvTruthy } from './envUtils.js'
|
||||
import { getErrnoCode, isFsInaccessible } from './errors.js'
|
||||
import { getTaskOutputPath } from './task/diskOutput.js'
|
||||
import type { FileHistorySnapshot } from './fileHistory.js'
|
||||
@@ -88,7 +88,6 @@ import { getFsImplementation } from './fsOperations.js'
|
||||
import { getWorktreePaths } from './getWorktreePaths.js'
|
||||
import { getBranch } from './git.js'
|
||||
import { gracefulShutdownSync, isShuttingDown } from './gracefulShutdown.js'
|
||||
import { parseJSONL } from './json.js'
|
||||
import { logError } from './log.js'
|
||||
import {
|
||||
extractTag,
|
||||
|
||||
Reference in New Issue
Block a user