Disallow unsafe type assertions (#18688)

This commit is contained in:
Christian Gunderman
2026-02-10 00:10:15 +00:00
committed by GitHub
parent bce1caefd0
commit fd65416a2f
188 changed files with 592 additions and 47 deletions
+4
View File
@@ -185,6 +185,7 @@ export async function parseAgentMarkdown(
} catch (error) {
throw new AgentLoadError(
filePath,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
`YAML frontmatter parsing failed: ${(error as Error).message}`,
);
}
@@ -328,12 +329,14 @@ export async function loadAgentsFromDirectory(
dirEntries = await fs.readdir(dir, { withFileTypes: true });
} catch (error) {
// If directory doesn't exist, just return empty
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
return result;
}
result.errors.push(
new AgentLoadError(
dir,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
`Could not list directory: ${(error as Error).message}`,
),
);
@@ -364,6 +367,7 @@ export async function loadAgentsFromDirectory(
result.errors.push(
new AgentLoadError(
filePath,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
`Unexpected error: ${(error as Error).message}`,
),
);
@@ -822,6 +822,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
for (const [index, functionCall] of functionCalls.entries()) {
const callId = functionCall.id ?? `${promptId}-${index}`;
const args = functionCall.args ?? {};
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const toolName = functionCall.name as string;
this.emitActivity('TOOL_CALL_START', {
@@ -1107,6 +1108,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
...schema
} = jsonSchema;
completeTool.parameters!.properties![outputConfig.outputName] =
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
schema as Schema;
completeTool.parameters!.required!.push(outputConfig.outputName);
} else {
@@ -26,5 +26,6 @@ export function createAvailabilityServiceMock(
selectFirstAvailable: vi.fn().mockReturnValue(selection),
};
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return service as unknown as ModelAvailabilityService;
}
@@ -208,6 +208,7 @@ function toContent(content: ContentUnion): Content {
// it's a Part
return {
role: 'user',
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
parts: [toPart(content as Part)],
};
}
@@ -44,6 +44,7 @@ export async function getExperiments(
'Invalid format for experiments file: `flags` and `experimentIds` must be arrays if present.',
);
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return parseExperiments(response as ListExperimentsResponse);
} catch (e) {
debugLogger.debug('Failed to read experiments from GEMINI_EXP', e);
@@ -125,6 +125,7 @@ export class OAuthCredentialStorage {
throw error;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const credentials = JSON.parse(credsJson) as Credentials;
// Save to new storage
+2
View File
@@ -115,6 +115,7 @@ async function initOauthClient(
if (
credentials &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(credentials as { type?: string }).type ===
'external_account_authorized_user'
) {
@@ -602,6 +603,7 @@ export function getAvailablePort(): Promise<number> {
}
const server = net.createServer();
server.listen(0, () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const address = server.address()! as net.AddressInfo;
port = address.port;
});
+6
View File
@@ -301,6 +301,7 @@ export class CodeAssistServer implements ContentGenerator {
body: JSON.stringify(req),
signal,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return res.data as T;
}
@@ -318,6 +319,7 @@ export class CodeAssistServer implements ContentGenerator {
responseType: 'json',
signal,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return res.data as T;
}
@@ -351,6 +353,7 @@ export class CodeAssistServer implements ContentGenerator {
return (async function* (): AsyncGenerator<T> {
const rl = readline.createInterface({
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
input: res.data as NodeJS.ReadableStream,
crlfDelay: Infinity, // Recognizes '\r\n' and '\n' as line breaks
});
@@ -363,6 +366,7 @@ export class CodeAssistServer implements ContentGenerator {
if (bufferedLines.length === 0) {
continue; // no data to yield
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
yield JSON.parse(bufferedLines.join('\n')) as T;
bufferedLines = []; // Reset the buffer after yielding
}
@@ -390,11 +394,13 @@ export class CodeAssistServer implements ContentGenerator {
function isVpcScAffectedUser(error: unknown): boolean {
if (error && typeof error === 'object' && 'response' in error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const gaxiosError = error as {
response?: {
data?: unknown;
};
};
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const response = gaxiosError.response?.data as
| GoogleRpcResponse
| undefined;
+1
View File
@@ -42,6 +42,7 @@ export async function* performRestore<
content: 'Restored project to the state before the tool call.',
};
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const error = e as Error;
if (error.message.includes('unable to read tree')) {
yield {
@@ -146,7 +146,7 @@ export class MessageBus extends EventEmitter {
this.subscribe<TResponse>(responseType, responseHandler);
// Publish the request with correlation ID
// eslint-disable-next-line @typescript-eslint/no-floating-promises
// eslint-disable-next-line @typescript-eslint/no-floating-promises, @typescript-eslint/no-unsafe-type-assertion
this.publish({ ...request, correlationId } as TRequest);
});
}
@@ -73,6 +73,7 @@ export async function executeToolWithHooks(
setPidCallback?: (pid: number) => void,
config?: Config,
): Promise<ToolResult> {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const toolInput = (invocation.params || {}) as Record<string, unknown>;
let inputWasModified = false;
let modifiedKeys: string[] = [];
@@ -224,6 +224,7 @@ export class CoreToolScheduler {
tool: toolInstance,
invocation,
status: 'success',
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
response: auxiliaryData as ToolCallResponseInfo,
durationMs,
outcome,
@@ -237,6 +238,7 @@ export class CoreToolScheduler {
request: currentCall.request,
status: 'error',
tool: toolInstance,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
response: auxiliaryData as ToolCallResponseInfo,
durationMs,
outcome,
@@ -247,6 +249,7 @@ export class CoreToolScheduler {
request: currentCall.request,
tool: toolInstance,
status: 'awaiting_approval',
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
confirmationDetails: auxiliaryData as ToolCallConfirmationDetails,
startTime: existingStartTime,
outcome,
@@ -347,6 +350,7 @@ export class CoreToolScheduler {
const invocationOrError = this.buildInvocation(
call.tool,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
args as Record<string, unknown>,
);
if (invocationOrError instanceof Error) {
@@ -356,6 +360,7 @@ export class CoreToolScheduler {
ToolErrorType.INVALID_TOOL_PARAMS,
);
return {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
request: { ...call.request, args: args as Record<string, unknown> },
status: 'error',
tool: call.tool,
@@ -365,6 +370,7 @@ export class CoreToolScheduler {
return {
...call,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
request: { ...call.request, args: args as Record<string, unknown> },
invocation: invocationOrError,
};
@@ -749,6 +755,7 @@ export class CoreToolScheduler {
this.cancelAll(signal);
return; // `cancelAll` calls `checkAndNotifyCompletion`, so we can exit here.
} else if (outcome === ToolConfirmationOutcome.ModifyWithEditor) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const waitingToolCall = toolCall as WaitingToolCall;
const editorType = this.getPreferredEditor();
@@ -756,6 +763,7 @@ export class CoreToolScheduler {
return;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
this.setStatusInternal(callId, 'awaiting_approval', signal, {
...waitingToolCall.confirmationDetails,
isModifying: true,
@@ -770,12 +778,14 @@ export class CoreToolScheduler {
// Restore status (isModifying: false) and update diff if result exists
if (result) {
this.setArgsInternal(callId, result.updatedParams);
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
this.setStatusInternal(callId, 'awaiting_approval', signal, {
...waitingToolCall.confirmationDetails,
fileDiff: result.updatedDiff,
isModifying: false,
} as ToolCallConfirmationDetails);
} else {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
this.setStatusInternal(callId, 'awaiting_approval', signal, {
...waitingToolCall.confirmationDetails,
isModifying: false,
@@ -786,13 +796,16 @@ export class CoreToolScheduler {
// re-confirmation.
if (payload && 'newContent' in payload && toolCall) {
const result = await this.toolModifier.applyInlineModify(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
toolCall as WaitingToolCall,
payload,
signal,
);
if (result) {
this.setArgsInternal(callId, result.updatedParams);
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
this.setStatusInternal(callId, 'awaiting_approval', signal, {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
...(toolCall as WaitingToolCall).confirmationDetails,
fileDiff: result.updatedDiff,
} as ToolCallConfirmationDetails);
@@ -51,6 +51,7 @@ export class FakeContentGenerator implements ContentGenerator {
const responses = fileContent
.split('\n')
.filter((line) => line.trim() !== '')
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
.map((line) => JSON.parse(line) as FakeResponse);
return new FakeContentGenerator(responses);
}
@@ -71,6 +72,7 @@ export class FakeContentGenerator implements ContentGenerator {
`Unexpected response type, next response was for ${response.method} but expected ${method}`,
);
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return response.response as R;
}
+3
View File
@@ -560,6 +560,7 @@ export class GeminiChat {
beforeModelResult.modifiedContents &&
Array.isArray(beforeModelResult.modifiedContents)
) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
contentsToUse = beforeModelResult.modifiedContents as Content[];
}
@@ -577,6 +578,7 @@ export class GeminiChat {
toolSelectionResult.tools &&
Array.isArray(toolSelectionResult.tools)
) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
config.tools = toolSelectionResult.tools as Tool[];
}
}
@@ -820,6 +822,7 @@ export class GeminiChat {
(candidate) => candidate.finishReason,
);
if (candidateWithReason) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
finishReason = candidateWithReason.finishReason as FinishReason;
}
+10
View File
@@ -96,6 +96,7 @@ export class Logger {
await this._backupCorruptedLogFile('malformed_array');
return [];
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return parsedLogs.filter(
(entry) =>
typeof entry.sessionId === 'string' &&
@@ -105,6 +106,7 @@ export class Logger {
typeof entry.message === 'string',
) as LogEntry[];
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const nodeError = error as NodeJS.ErrnoException;
if (nodeError.code === 'ENOENT') {
return [];
@@ -298,6 +300,7 @@ export class Logger {
await fs.access(newPath);
return newPath; // Found it, use the new path.
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const nodeError = error as NodeJS.ErrnoException;
if (nodeError.code !== 'ENOENT') {
throw error; // A real error occurred, rethrow it.
@@ -311,6 +314,7 @@ export class Logger {
await fs.access(oldPath);
return oldPath; // Found it, use the old path.
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const nodeError = error as NodeJS.ErrnoException;
if (nodeError.code !== 'ENOENT') {
throw error; // A real error occurred, rethrow it.
@@ -352,6 +356,7 @@ export class Logger {
// Handle legacy format (just an array of Content)
if (Array.isArray(parsedContent)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return { history: parsedContent as Content[] };
}
@@ -360,6 +365,7 @@ export class Logger {
parsedContent !== null &&
'history' in parsedContent
) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return parsedContent as Checkpoint;
}
@@ -368,6 +374,7 @@ export class Logger {
);
return { history: [] };
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const nodeError = error as NodeJS.ErrnoException;
if (nodeError.code === 'ENOENT') {
// This is okay, it just means the checkpoint doesn't exist in either format.
@@ -397,6 +404,7 @@ export class Logger {
await fs.unlink(newPath);
deletedSomething = true;
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const nodeError = error as NodeJS.ErrnoException;
if (nodeError.code !== 'ENOENT') {
debugLogger.error(
@@ -415,6 +423,7 @@ export class Logger {
await fs.unlink(oldPath);
deletedSomething = true;
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const nodeError = error as NodeJS.ErrnoException;
if (nodeError.code !== 'ENOENT') {
debugLogger.error(
@@ -444,6 +453,7 @@ export class Logger {
await fs.access(filePath);
return true;
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const nodeError = error as NodeJS.ErrnoException;
if (nodeError.code === 'ENOENT') {
return false; // It truly doesn't exist in either format.
@@ -177,7 +177,8 @@ export class LoggingContentGenerator implements ContentGenerator {
this.config.getContentGeneratorConfig()?.authType,
errorType,
isStructuredError(error)
? (error as StructuredError).status
? // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(error as StructuredError).status
: undefined,
),
);
@@ -48,6 +48,7 @@ export class RecordingContentGenerator implements ContentGenerator {
);
const recordedResponse: FakeResponse = {
method: 'generateContent',
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
response: {
candidates: response.candidates,
usageMetadata: response.usageMetadata,
@@ -73,6 +74,7 @@ export class RecordingContentGenerator implements ContentGenerator {
async function* stream(filePath: string) {
for await (const response of realResponses) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(recordedResponse.response as GenerateContentResponse[]).push({
candidates: response.candidates,
usageMetadata: response.usageMetadata,
+2 -1
View File
@@ -384,7 +384,8 @@ export class Turn {
error !== null &&
'status' in error &&
typeof (error as { status: unknown }).status === 'number'
? (error as { status: number }).status
? // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(error as { status: number }).status
: undefined;
const structuredError: StructuredError = {
message: getErrorMessage(error),
@@ -102,6 +102,7 @@ export class HookAggregator {
case HookEventName.BeforeToolSelection:
return this.mergeToolSelectionOutputs(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
outputs as BeforeToolSelectionOutput[],
);
+2
View File
@@ -226,6 +226,7 @@ please review the project settings (.gemini/settings.json) and remove them.`;
this.validateHookConfig(hookConfig, eventName, source)
) {
// Check if this hook is in the disabled list
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const hookName = this.getHookName({
config: hookConfig,
} as HookRegistryEntry);
@@ -282,6 +283,7 @@ please review the project settings (.gemini/settings.json) and remove them.`;
*/
private isValidEventName(eventName: string): eventName is HookEventName {
const validEventNames = Object.values(HookEventName);
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return validEventNames.includes(eventName as HookEventName);
}
+8
View File
@@ -174,6 +174,7 @@ export class HookRunner {
typeof additionalContext === 'string' &&
'prompt' in modifiedInput
) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(modifiedInput as BeforeAgentInput).prompt +=
'\n\n' + additionalContext;
}
@@ -183,16 +184,19 @@ export class HookRunner {
case HookEventName.BeforeModel:
if ('llm_request' in hookOutput.hookSpecificOutput) {
// For BeforeModel, we update the LLM request
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const hookBeforeModelOutput = hookOutput as BeforeModelOutput;
if (
hookBeforeModelOutput.hookSpecificOutput?.llm_request &&
'llm_request' in modifiedInput
) {
// Merge the partial request with the existing request
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const currentRequest = (modifiedInput as BeforeModelInput)
.llm_request;
const partialRequest =
hookBeforeModelOutput.hookSpecificOutput.llm_request;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(modifiedInput as BeforeModelInput).llm_request = {
...currentRequest,
...partialRequest,
@@ -203,11 +207,14 @@ export class HookRunner {
case HookEventName.BeforeTool:
if ('tool_input' in hookOutput.hookSpecificOutput) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const newToolInput = hookOutput.hookSpecificOutput[
'tool_input'
] as Record<string, unknown>;
if (newToolInput && 'tool_input' in modifiedInput) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(modifiedInput as BeforeToolInput).tool_input = {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
...(modifiedInput as BeforeToolInput).tool_input,
...newToolInput,
};
@@ -355,6 +362,7 @@ export class HookRunner {
parsed = JSON.parse(parsed);
}
if (parsed) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
output = parsed as HookOutput;
}
} catch {
+3
View File
@@ -262,6 +262,7 @@ export class HookSystem {
const blockingError = hookOutput?.getBlockingError();
if (blockingError?.blocked) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const beforeModelOutput = hookOutput as BeforeModelHookOutput;
const syntheticResponse = beforeModelOutput.getSyntheticResponse();
return {
@@ -273,6 +274,7 @@ export class HookSystem {
}
if (hookOutput) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const beforeModelOutput = hookOutput as BeforeModelHookOutput;
const modifiedRequest =
beforeModelOutput.applyLLMRequestModifications(llmRequest);
@@ -319,6 +321,7 @@ export class HookSystem {
}
if (hookOutput) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const afterModelOutput = hookOutput as AfterModelHookOutput;
const modifiedResponse = afterModelOutput.getModifiedResponse();
if (modifiedResponse) {
+6 -1
View File
@@ -282,6 +282,7 @@ export class HookTranslatorGenAIv1 extends HookTranslator {
parts: textParts,
},
finishReason:
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
candidate.finishReason as LLMResponse['candidates'][0]['finishReason'],
index: candidate.index,
safetyRatings: candidate.safetyRatings?.map((rating) => ({
@@ -306,6 +307,7 @@ export class HookTranslatorGenAIv1 extends HookTranslator {
*/
fromHookLLMResponse(hookResponse: LLMResponse): GenerateContentResponse {
// Build response object with proper structure
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const response: GenerateContentResponse = {
text: hookResponse.text,
candidates: hookResponse.candidates.map((candidate) => ({
@@ -315,6 +317,7 @@ export class HookTranslatorGenAIv1 extends HookTranslator {
text: part,
})),
},
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
finishReason: candidate.finishReason as FinishReason,
index: candidate.index,
safetyRatings: candidate.safetyRatings,
@@ -330,6 +333,7 @@ export class HookTranslatorGenAIv1 extends HookTranslator {
*/
toHookToolConfig(sdkToolConfig: ToolConfig): HookToolConfig {
return {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
mode: sdkToolConfig.functionCallingConfig?.mode as HookToolConfig['mode'],
allowedFunctionNames:
sdkToolConfig.functionCallingConfig?.allowedFunctionNames,
@@ -342,7 +346,8 @@ export class HookTranslatorGenAIv1 extends HookTranslator {
fromHookToolConfig(hookToolConfig: HookToolConfig): ToolConfig {
const functionCallingConfig: FunctionCallingConfig | undefined =
hookToolConfig.mode || hookToolConfig.allowedFunctionNames
? ({
? // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
({
mode: hookToolConfig.mode,
allowedFunctionNames: hookToolConfig.allowedFunctionNames,
} as FunctionCallingConfig)
+2
View File
@@ -71,6 +71,7 @@ export class TrustedHooksManager {
const untrusted: string[] = [];
for (const eventName of Object.keys(hooks)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const definitions = hooks[eventName as HookEventName];
if (!Array.isArray(definitions)) continue;
@@ -99,6 +100,7 @@ export class TrustedHooksManager {
const currentTrusted = new Set(this.trustedHooks[projectPath] || []);
for (const eventName of Object.keys(hooks)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const definitions = hooks[eventName as HookEventName];
if (!Array.isArray(definitions)) continue;
+7
View File
@@ -270,6 +270,7 @@ export class BeforeToolHookOutput extends DefaultHookOutput {
input !== null &&
!Array.isArray(input)
) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return input as Record<string, unknown>;
}
}
@@ -286,6 +287,7 @@ export class BeforeModelHookOutput extends DefaultHookOutput {
*/
getSyntheticResponse(): GenerateContentResponse | undefined {
if (this.hookSpecificOutput && 'llm_response' in this.hookSpecificOutput) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const hookResponse = this.hookSpecificOutput[
'llm_response'
] as LLMResponse;
@@ -304,12 +306,14 @@ export class BeforeModelHookOutput extends DefaultHookOutput {
target: GenerateContentParameters,
): GenerateContentParameters {
if (this.hookSpecificOutput && 'llm_request' in this.hookSpecificOutput) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const hookRequest = this.hookSpecificOutput[
'llm_request'
] as Partial<LLMRequest>;
if (hookRequest) {
// Convert hook format to SDK format
const sdkRequest = defaultHookTranslator.fromHookLLMRequest(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
hookRequest as LLMRequest,
target,
);
@@ -335,6 +339,7 @@ export class BeforeToolSelectionHookOutput extends DefaultHookOutput {
tools?: ToolListUnion;
}): { toolConfig?: GenAIToolConfig; tools?: ToolListUnion } {
if (this.hookSpecificOutput && 'toolConfig' in this.hookSpecificOutput) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const hookToolConfig = this.hookSpecificOutput[
'toolConfig'
] as HookToolConfig;
@@ -362,12 +367,14 @@ export class AfterModelHookOutput extends DefaultHookOutput {
*/
getModifiedResponse(): GenerateContentResponse | undefined {
if (this.hookSpecificOutput && 'llm_response' in this.hookSpecificOutput) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const hookResponse = this.hookSpecificOutput[
'llm_response'
] as Partial<LLMResponse>;
if (hookResponse?.candidates?.[0]?.content?.parts?.length) {
// Convert hook format to SDK format
return defaultHookTranslator.fromHookLLMResponse(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
hookResponse as LLMResponse,
);
}
@@ -213,8 +213,10 @@ export async function createProxyAwareFetch(ideServerHost: string) {
...init,
dispatcher: agent,
};
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const options = fetchOptions as unknown as import('undici').RequestInit;
const response = await fetchFn(url, options);
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return new Response(response.body as ReadableStream<unknown> | null, {
status: response.status,
statusText: response.statusText,
+4
View File
@@ -143,6 +143,7 @@ export class MCPOAuthProvider {
);
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return (await response.json()) as OAuthClientRegistrationResponse;
}
@@ -377,6 +378,7 @@ export class MCPOAuthProvider {
}
server.listen(listenPort, () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const address = server.address() as net.AddressInfo;
serverPort = address.port;
debugLogger.log(
@@ -580,6 +582,7 @@ export class MCPOAuthProvider {
// Try to parse as JSON first, fall back to form-urlencoded
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return JSON.parse(responseText) as OAuthTokenResponse;
} catch {
// Parse form-urlencoded response
@@ -702,6 +705,7 @@ export class MCPOAuthProvider {
// Try to parse as JSON first, fall back to form-urlencoded
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return JSON.parse(responseText) as OAuthTokenResponse;
} catch {
// Parse form-urlencoded response
@@ -61,6 +61,7 @@ export class MCPOAuthTokenStorage implements TokenStorage {
try {
const tokenFile = this.getTokenFilePath();
const data = await fs.readFile(tokenFile, 'utf-8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const tokens = JSON.parse(data) as OAuthCredentials[];
for (const credential of tokens) {
@@ -68,6 +69,7 @@ export class MCPOAuthTokenStorage implements TokenStorage {
}
} catch (error) {
// File doesn't exist or is invalid, return empty map
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
coreEvents.emitFeedback(
'error',
@@ -222,6 +224,7 @@ export class MCPOAuthTokenStorage implements TokenStorage {
const tokenFile = this.getTokenFilePath();
await fs.unlink(tokenFile);
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
coreEvents.emitFeedback(
'error',
+2
View File
@@ -101,6 +101,7 @@ export class OAuthUtils {
if (!response.ok) {
return null;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return (await response.json()) as OAuthProtectedResourceMetadata;
} catch (error) {
debugLogger.debug(
@@ -124,6 +125,7 @@ export class OAuthUtils {
if (!response.ok) {
return null;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return (await response.json()) as OAuthAuthorizationServerMetadata;
} catch (error) {
debugLogger.debug(
@@ -114,6 +114,7 @@ export class ServiceAccountImpersonationProvider implements McpAuthProvider {
coreEvents.emitFeedback(
'error',
'Failed to obtain authentication token.',
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
e as Error,
);
return undefined;
@@ -72,9 +72,11 @@ export class FileTokenStorage extends BaseTokenStorage {
try {
const data = await fs.readFile(this.tokenFilePath, 'utf-8');
const decrypted = this.decrypt(data);
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const tokens = JSON.parse(decrypted) as Record<string, OAuthCredentials>;
return new Map(Object.entries(tokens));
} catch (error: unknown) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const err = error as NodeJS.ErrnoException & { message?: string };
if (err.code === 'ENOENT') {
return new Map();
@@ -144,6 +146,7 @@ export class FileTokenStorage extends BaseTokenStorage {
try {
await fs.unlink(this.tokenFilePath);
} catch (error: unknown) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const err = error as NodeJS.ErrnoException;
if (err.code !== 'ENOENT') {
throw error;
@@ -176,6 +179,7 @@ export class FileTokenStorage extends BaseTokenStorage {
try {
await fs.unlink(this.tokenFilePath);
} catch (error: unknown) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const err = error as NodeJS.ErrnoException;
if (err.code !== 'ENOENT') {
throw error;
@@ -70,6 +70,7 @@ export class KeychainTokenStorage
return null;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const credentials = JSON.parse(data) as OAuthCredentials;
if (this.isTokenExpired(credentials)) {
@@ -179,6 +180,7 @@ export class KeychainTokenStorage
for (const cred of credentials) {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const data = JSON.parse(cred.password) as OAuthCredentials;
if (!this.isTokenExpired(data)) {
result.set(cred.account, data);
@@ -223,6 +225,7 @@ export class KeychainTokenStorage
try {
await this.deleteCredentials(server);
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
errors.push(error as Error);
}
}
+2
View File
@@ -382,6 +382,7 @@ export function createPolicyUpdater(
const fileContent = await fs.readFile(policyFile, 'utf-8');
existingData = toml.parse(fileContent) as { rule?: TomlRule[] };
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
debugLogger.warn(
`Failed to parse ${policyFile}, overwriting with new policy.`,
@@ -424,6 +425,7 @@ export function createPolicyUpdater(
// Serialize back to TOML
// @iarna/toml stringify might not produce beautiful output but it handles escaping correctly
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const newContent = toml.stringify(existingData as toml.JsonMap);
// Atomic write: write to tmp then rename
@@ -312,6 +312,7 @@ export class PolicyEngine {
if (toolName && SHELL_TOOL_NAMES.includes(toolName)) {
isShellCommand = true;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const args = toolCall.args as { command?: string; dir_path?: string };
command = args?.command;
shellDirPath = args?.dir_path;
@@ -111,6 +111,7 @@ export function stableStringify(obj: unknown): string {
const pairs: string[] = [];
for (const key of sortedKeys) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const value = (currentObj as Record<string, unknown>)[key];
// Skip undefined and function values in objects (per JSON spec)
if (value !== undefined && typeof value !== 'function') {
+6
View File
@@ -234,6 +234,7 @@ export async function loadPoliciesFromToml(
.filter((entry) => entry.isFile() && entry.name.endsWith('.toml'))
.map((entry) => entry.name);
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const error = e as NodeJS.ErrnoException;
if (error.code === 'ENOENT') {
// Directory doesn't exist, skip it (not an error)
@@ -262,6 +263,7 @@ export async function loadPoliciesFromToml(
try {
parsed = toml.parse(fileContent);
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const error = e as Error;
errors.push({
filePath,
@@ -356,6 +358,7 @@ export async function loadPoliciesFromToml(
try {
policyRule.argsPattern = new RegExp(argsPattern);
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const error = e as Error;
errors.push({
filePath,
@@ -411,6 +414,7 @@ export async function loadPoliciesFromToml(
const safetyCheckerRule: SafetyCheckerRule = {
toolName: effectiveToolName,
priority: checker.priority,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
checker: checker.checker as SafetyCheckerConfig,
modes: checker.modes,
};
@@ -419,6 +423,7 @@ export async function loadPoliciesFromToml(
try {
safetyCheckerRule.argsPattern = new RegExp(argsPattern);
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const error = e as Error;
errors.push({
filePath,
@@ -440,6 +445,7 @@ export async function loadPoliciesFromToml(
checkers.push(...parsedCheckers);
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const error = e as NodeJS.ErrnoException;
// Catch-all for unexpected errors
if (error.code !== 'ENOENT') {
+2
View File
@@ -35,8 +35,10 @@ export function getHookSource(input: Record<string, unknown>): HookSource {
const source = input['hook_source'];
if (
typeof source === 'string' &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
VALID_HOOK_SOURCES.includes(source as HookSource)
) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return source as HookSource;
}
return 'project';
+5 -5
View File
@@ -183,11 +183,11 @@ export class PromptProvider {
})),
} as snippets.SystemPromptOptions;
basePrompt = (
activeSnippets.getCoreSystemPrompt as (
options: snippets.SystemPromptOptions,
) => string
)(options);
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const getCoreSystemPrompt = activeSnippets.getCoreSystemPrompt as (
options: snippets.SystemPromptOptions,
) => string;
basePrompt = getCoreSystemPrompt(options);
}
// --- Finalization (Shell) ---
@@ -49,6 +49,7 @@ export class CompositeStrategy implements TerminalStrategy {
0,
-1,
) as RoutingStrategy[];
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const terminalStrategy = this.strategies[
this.strategies.length - 1
] as TerminalStrategy;
+1
View File
@@ -23,6 +23,7 @@ export interface InProcessChecker {
export class AllowedPathChecker implements InProcessChecker {
async check(input: SafetyCheckInput): Promise<SafetyCheckResult> {
const { toolCall, context } = input;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const config = input.config as AllowedPathConfig | undefined;
// Build list of allowed directories
+3 -1
View File
@@ -23,6 +23,7 @@ export class ContextBuilder {
return {
environment: {
cwd: process.cwd(),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
workspaces: this.config
.getWorkspaceContext()
.getDirectories() as string[],
@@ -44,11 +45,12 @@ export class ContextBuilder {
for (const key of requiredKeys) {
if (key in fullContext) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
(minimalContext as any)[key] = fullContext[key];
}
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return minimalContext as SafetyCheckInput['context'];
}
}
@@ -70,6 +70,7 @@ export async function awaitConfirmation(
MessageBusType.TOOL_CONFIRMATION_RESPONSE,
{ signal },
)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const response = msg as ToolConfirmationResponse;
if (response.correlationId === correlationId) {
return {
@@ -84,6 +85,7 @@ export async function awaitConfirmation(
}
}
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
if (signal.aborted || (error as Error).name === 'AbortError') {
throw new Error('Operation cancelled');
}
@@ -232,6 +234,7 @@ async function handleExternalModification(
}
const result = await modifier.handleModifyWithEditor(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
state.firstActiveCall as WaitingToolCall,
editor,
signal,
@@ -258,6 +261,7 @@ async function handleInlineModification(
): Promise<void> {
const { state, modifier } = deps;
const result = await modifier.applyInlineModify(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
state.firstActiveCall as WaitingToolCall,
payload,
signal,
+1
View File
@@ -476,6 +476,7 @@ export class Scheduler {
if (signal.aborted) throw new Error('Operation cancelled');
this.state.updateStatus(callId, 'executing');
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const activeCall = this.state.firstActiveCall as ExecutingToolCall;
const result = await runWithToolCallContext(
@@ -370,6 +370,7 @@ export class SchedulerStateManager {
confirmationDetails = data.confirmationDetails;
} else {
// TODO: Remove legacy callback shape once event-driven migration is complete
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
confirmationDetails = data as ToolCallConfirmationDetails;
}
@@ -489,6 +490,7 @@ export class SchedulerStateManager {
private toExecuting(call: ToolCall, data?: unknown): ExecutingToolCall {
this.validateHasToolAndInvocation(call, 'executing');
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const execData = data as Partial<ExecutingToolCall> | undefined;
const liveOutput =
execData?.liveOutput ??
@@ -48,6 +48,7 @@ export class ToolModificationHandler {
typeof toolCall.request.args
>(
toolCall.request.args,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
modifyContext as ModifyContext<typeof toolCall.request.args>,
editorType,
signal,
@@ -76,6 +77,7 @@ export class ToolModificationHandler {
return undefined;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const modifyContext = toolCall.tool.getModifyContext(
signal,
) as ModifyContext<typeof toolCall.request.args>;
@@ -191,6 +191,7 @@ export class ChatRecordingService {
if (
error instanceof Error &&
'code' in error &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(error as NodeJS.ErrnoException).code === 'ENOSPC'
) {
this.conversationFile = null;
@@ -420,6 +421,7 @@ export class ChatRecordingService {
this.cachedLastConvData = fs.readFileSync(this.conversationFile!, 'utf8');
return JSON.parse(this.cachedLastConvData);
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
debugLogger.error('Error reading conversation file.', error);
throw error;
@@ -460,6 +462,7 @@ export class ChatRecordingService {
if (
error instanceof Error &&
'code' in error &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(error as NodeJS.ErrnoException).code === 'ENOSPC'
) {
this.conversationFile = null;
@@ -449,6 +449,7 @@ export class LoopDetectionService {
return false;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const flashConfidence = flashResult[
'unproductive_state_confidence'
] as number;
@@ -490,7 +491,8 @@ export class LoopDetectionService {
);
const mainModelConfidence = mainModelResult
? (mainModelResult['unproductive_state_confidence'] as number)
? // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(mainModelResult['unproductive_state_confidence'] as number)
: 0;
logLlmLoopCheck(
@@ -245,6 +245,7 @@ export class ModelConfigService {
let matchedLevel = 0; // Default to Global
const isMatch = matchEntries.every(([key, value]) => {
if (key === 'model') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const level = modelToLevel.get(value as string);
if (level === undefined) return false;
matchedLevel = level;
@@ -253,6 +254,7 @@ export class ModelConfigService {
if (key === 'overrideScope' && value === 'core') {
return context.overrideScope === 'core' || !context.overrideScope;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return context[key as keyof ModelConfigKey] === value;
});
@@ -291,6 +293,7 @@ export class ModelConfigService {
);
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return {
model: resolved.model,
generateContentConfig: resolved.generateContentConfig,
@@ -321,7 +324,9 @@ export class ModelConfigService {
config2: GenerateContentConfig | undefined,
): GenerateContentConfig {
return ModelConfigService.genericDeepMerge(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
config1 as Record<string, unknown> | undefined,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
config2 as Record<string, unknown> | undefined,
) as GenerateContentConfig;
}
@@ -13,6 +13,7 @@ export const makeResolvedModelConfig = (
model: string,
overrides: Partial<ResolvedModelConfig['generateContentConfig']> = {},
): ResolvedModelConfig =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
({
model,
generateContentConfig: {
@@ -510,6 +510,7 @@ export class ShellExecutionService {
return { pid: child.pid, result };
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const error = e as Error;
return {
pid: undefined,
@@ -778,6 +779,7 @@ export class ShellExecutionService {
this.activePtys.delete(ptyProcess.pid);
// Attempt to destroy the PTY to ensure FD is closed
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(ptyProcess as IPty & { destroy?: () => void }).destroy?.();
} catch {
// Ignore errors during cleanup
@@ -860,6 +862,7 @@ export class ShellExecutionService {
return { pid: ptyProcess.pid, result };
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const error = e as Error;
if (error.message.includes('posix_spawnp failed')) {
onOutputEvent({
@@ -1105,6 +1108,7 @@ export class ShellExecutionService {
} catch (e) {
// Ignore errors if the pty has already exited, which can happen
// due to a race condition between the exit event and this call.
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const err = e as { code?: string; message?: string };
const isEsrch = err.code === 'ESRCH';
const isWindowsPtyError = err.message?.includes(
@@ -189,6 +189,7 @@ export class ToolOutputMaskingService {
await fsPromises.writeFile(filePath, content, 'utf-8');
const originalResponse =
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(part.functionResponse.response as Record<string, unknown>) || {};
const totalLines = content.split('\n').length;
@@ -268,6 +269,7 @@ export class ToolOutputMaskingService {
private getToolOutputContent(part: Part): string | null {
if (!part.functionResponse) return null;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const response = part.functionResponse.response as Record<string, unknown>;
if (!response) return null;
@@ -286,6 +288,7 @@ export class ToolOutputMaskingService {
}
private formatShellPreview(response: Record<string, unknown>): string {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const content = (response['output'] || response['stdout'] || '') as string;
if (typeof content !== 'string') {
return typeof content === 'object'
+1
View File
@@ -42,6 +42,7 @@ function parseFrontmatter(
try {
const parsed = yaml.load(content);
if (parsed && typeof parsed === 'object') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const { name, description } = parsed as Record<string, unknown>;
if (typeof name === 'string' && typeof description === 'string') {
return { name, description };
@@ -174,6 +174,7 @@ export class ActivityMonitor {
eventTypes: Record<ActivityType, number>;
timeRange: { start: number; end: number } | null;
} {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const eventTypes = {} as Record<ActivityType, number>;
let start = Number.MAX_SAFE_INTEGER;
let end = 0;
@@ -450,6 +450,7 @@ export class ClearcutLogger {
if (this.config?.getDebugMode()) {
debugLogger.log('Flushing log events to Clearcut.');
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const eventsToSend = this.events.toArray() as LogEventEntry[][];
this.events.clear();
@@ -493,6 +494,7 @@ export class ClearcutLogger {
}
} catch (e: unknown) {
if (this.config?.getDebugMode()) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
debugLogger.warn('Error flushing log events:', e as Error);
}
@@ -104,6 +104,7 @@ export class GcpLogExporter implements LogRecordExporter {
} catch (error) {
resultCallback({
code: ExportResultCode.FAILED,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
error: error as Error,
});
}
@@ -15,6 +15,7 @@ import type { Config } from '../config/config.js';
describe('Circular Reference Integration Test', () => {
it('should handle HttpsProxyAgent-like circular references in clearcut logging', () => {
// Create a mock config with proxy
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const mockConfig = {
getTelemetryEnabled: () => true,
getUsageStatisticsEnabled: () => true,
@@ -56,7 +57,7 @@ describe('Circular Reference Integration Test', () => {
const logger = ClearcutLogger.getInstance(mockConfig);
expect(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
logger?.enqueueLogEvent(problematicEvent as any);
}).not.toThrow();
});
@@ -22,6 +22,7 @@ import { MockTool } from '../test-utils/mock-tool.js';
describe('Circular Reference Handling', () => {
it('should handle circular references in tool function arguments', () => {
// Create a mock config
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const mockConfig = {
getTelemetryEnabled: () => true,
getUsageStatisticsEnabled: () => true,
@@ -78,6 +79,7 @@ describe('Circular Reference Handling', () => {
});
it('should handle normal objects without circular references', () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const mockConfig = {
getTelemetryEnabled: () => true,
getUsageStatisticsEnabled: () => true,
+4
View File
@@ -111,6 +111,7 @@ export function logUserPrompt(config: Config, event: UserPromptEvent): void {
}
export function logToolCall(config: Config, event: ToolCallEvent): void {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const uiEvent = {
...event,
'event.name': EVENT_TOOL_CALL,
@@ -242,6 +243,7 @@ export function logRipgrepFallback(
}
export function logApiError(config: Config, event: ApiErrorEvent): void {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const uiEvent = {
...event,
'event.name': EVENT_API_ERROR,
@@ -273,6 +275,7 @@ export function logApiError(config: Config, event: ApiErrorEvent): void {
}
export function logApiResponse(config: Config, event: ApiResponseEvent): void {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const uiEvent = {
...event,
'event.name': EVENT_API_RESPONSE,
@@ -372,6 +375,7 @@ export function logSlashCommand(
}
export function logRewind(config: Config, event: RewindEvent): void {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const uiEvent = {
...event,
'event.name': EVENT_REWIND,
+30
View File
@@ -77,6 +77,7 @@ const COUNTER_DEFINITIONS = {
description: 'Counts tool calls, tagged by function name and success.',
valueType: ValueType.INT,
assign: (c: Counter) => (toolCallCounter = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
function_name: string;
success: boolean;
@@ -88,6 +89,7 @@ const COUNTER_DEFINITIONS = {
description: 'Counts API requests, tagged by model and status.',
valueType: ValueType.INT,
assign: (c: Counter) => (apiRequestCounter = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
model: string;
status_code?: number | string;
@@ -98,6 +100,7 @@ const COUNTER_DEFINITIONS = {
description: 'Counts the total number of tokens used.',
valueType: ValueType.INT,
assign: (c: Counter) => (tokenUsageCounter = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
model: string;
type: 'input' | 'output' | 'thought' | 'cache' | 'tool';
@@ -113,6 +116,7 @@ const COUNTER_DEFINITIONS = {
description: 'Counts file operations (create, read, update).',
valueType: ValueType.INT,
assign: (c: Counter) => (fileOperationCounter = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
operation: FileOperation;
lines?: number;
@@ -125,6 +129,7 @@ const COUNTER_DEFINITIONS = {
description: 'Number of lines changed (from file diffs).',
valueType: ValueType.INT,
assign: (c: Counter) => (linesChangedCounter = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
function_name?: string;
type: 'added' | 'removed';
@@ -152,6 +157,7 @@ const COUNTER_DEFINITIONS = {
description: 'Counts model routing failures.',
valueType: ValueType.INT,
assign: (c: Counter) => (modelRoutingFailureCounter = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
'routing.decision_source': string;
'routing.error_message': string;
@@ -161,6 +167,7 @@ const COUNTER_DEFINITIONS = {
description: 'Counts model slash command calls.',
valueType: ValueType.INT,
assign: (c: Counter) => (modelSlashCommandCallCounter = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
'slash_command.model.model_name': string;
},
@@ -169,6 +176,7 @@ const COUNTER_DEFINITIONS = {
description: 'Counts chat compression events.',
valueType: ValueType.INT,
assign: (c: Counter) => (chatCompressionCounter = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
tokens_before: number;
tokens_after: number;
@@ -178,6 +186,7 @@ const COUNTER_DEFINITIONS = {
description: 'Counts agent runs, tagged by name and termination reason.',
valueType: ValueType.INT,
assign: (c: Counter) => (agentRunCounter = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
agent_name: string;
terminate_reason: string;
@@ -187,6 +196,7 @@ const COUNTER_DEFINITIONS = {
description: 'Counts agent recovery attempts.',
valueType: ValueType.INT,
assign: (c: Counter) => (agentRecoveryAttemptCounter = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
agent_name: string;
reason: string;
@@ -210,6 +220,7 @@ const COUNTER_DEFINITIONS = {
description: 'Counts plan executions (switching from Plan Mode).',
valueType: ValueType.INT,
assign: (c: Counter) => (planExecutionCounter = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
approval_mode: string;
},
@@ -218,6 +229,7 @@ const COUNTER_DEFINITIONS = {
description: 'Counts hook calls, tagged by hook event name and success.',
valueType: ValueType.INT,
assign: (c: Counter) => (hookCallCounter = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
hook_event_name: string;
hook_name: string;
@@ -232,6 +244,7 @@ const HISTOGRAM_DEFINITIONS = {
unit: 'ms',
valueType: ValueType.INT,
assign: (h: Histogram) => (toolCallLatencyHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
function_name: string;
},
@@ -241,6 +254,7 @@ const HISTOGRAM_DEFINITIONS = {
unit: 'ms',
valueType: ValueType.INT,
assign: (h: Histogram) => (apiRequestLatencyHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
model: string;
},
@@ -250,6 +264,7 @@ const HISTOGRAM_DEFINITIONS = {
unit: 'ms',
valueType: ValueType.INT,
assign: (h: Histogram) => (modelRoutingLatencyHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
'routing.decision_model': string;
'routing.decision_source': string;
@@ -260,6 +275,7 @@ const HISTOGRAM_DEFINITIONS = {
unit: 'ms',
valueType: ValueType.INT,
assign: (h: Histogram) => (agentDurationHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
agent_name: string;
},
@@ -276,6 +292,7 @@ const HISTOGRAM_DEFINITIONS = {
unit: 'turns',
valueType: ValueType.INT,
assign: (h: Histogram) => (agentTurnsHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
agent_name: string;
},
@@ -285,6 +302,7 @@ const HISTOGRAM_DEFINITIONS = {
unit: 'ms',
valueType: ValueType.INT,
assign: (h: Histogram) => (agentRecoveryAttemptDurationHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
agent_name: string;
},
@@ -294,6 +312,7 @@ const HISTOGRAM_DEFINITIONS = {
unit: 'token',
valueType: ValueType.INT,
assign: (h: Histogram) => (genAiClientTokenUsageHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
'gen_ai.operation.name': string;
'gen_ai.provider.name': string;
@@ -309,6 +328,7 @@ const HISTOGRAM_DEFINITIONS = {
unit: 's',
valueType: ValueType.DOUBLE,
assign: (h: Histogram) => (genAiClientOperationDurationHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
'gen_ai.operation.name': string;
'gen_ai.provider.name': string;
@@ -324,6 +344,7 @@ const HISTOGRAM_DEFINITIONS = {
unit: 'ms',
valueType: ValueType.INT,
assign: (c: Histogram) => (hookCallLatencyHistogram = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
hook_event_name: string;
hook_name: string;
@@ -337,6 +358,7 @@ const PERFORMANCE_COUNTER_DEFINITIONS = {
description: 'Performance regression detection events.',
valueType: ValueType.INT,
assign: (c: Counter) => (regressionDetectionCounter = c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
metric: string;
severity: 'low' | 'medium' | 'high';
@@ -353,6 +375,7 @@ const PERFORMANCE_HISTOGRAM_DEFINITIONS = {
unit: 'ms',
valueType: ValueType.DOUBLE,
assign: (h: Histogram) => (startupTimeHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
phase: string;
details?: Record<string, string | number | boolean>;
@@ -363,6 +386,7 @@ const PERFORMANCE_HISTOGRAM_DEFINITIONS = {
unit: 'bytes',
valueType: ValueType.INT,
assign: (h: Histogram) => (memoryUsageGauge = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
memory_type: MemoryMetricType;
component?: string;
@@ -389,6 +413,7 @@ const PERFORMANCE_HISTOGRAM_DEFINITIONS = {
unit: 'ms',
valueType: ValueType.INT,
assign: (h: Histogram) => (toolExecutionBreakdownHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
function_name: string;
phase: ToolExecutionPhase;
@@ -400,6 +425,7 @@ const PERFORMANCE_HISTOGRAM_DEFINITIONS = {
unit: 'ratio',
valueType: ValueType.DOUBLE,
assign: (h: Histogram) => (tokenEfficiencyHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
model: string;
metric: string;
@@ -411,6 +437,7 @@ const PERFORMANCE_HISTOGRAM_DEFINITIONS = {
unit: 'ms',
valueType: ValueType.INT,
assign: (h: Histogram) => (apiRequestBreakdownHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
model: string;
phase: ApiRequestPhase;
@@ -421,6 +448,7 @@ const PERFORMANCE_HISTOGRAM_DEFINITIONS = {
unit: 'score',
valueType: ValueType.DOUBLE,
assign: (h: Histogram) => (performanceScoreGauge = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
category: string;
baseline?: number;
@@ -432,6 +460,7 @@ const PERFORMANCE_HISTOGRAM_DEFINITIONS = {
unit: 'percent',
valueType: ValueType.DOUBLE,
assign: (h: Histogram) => (regressionPercentageChangeHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
metric: string;
severity: 'low' | 'medium' | 'high';
@@ -445,6 +474,7 @@ const PERFORMANCE_HISTOGRAM_DEFINITIONS = {
unit: 'percent',
valueType: ValueType.DOUBLE,
assign: (h: Histogram) => (baselineComparisonHistogram = h),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
attributes: {} as {
metric: string;
category: string;
+4
View File
@@ -65,8 +65,10 @@ function getStringReferences(parts: AnyPart[]): StringReference[] {
} else if (part instanceof GenericPart) {
if (part.type === 'executableCode' && typeof part['code'] === 'string') {
refs.push({
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
get: () => part['code'] as string,
set: (val: string) => (part['code'] = val),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
len: () => (part['code'] as string).length,
});
} else if (
@@ -74,8 +76,10 @@ function getStringReferences(parts: AnyPart[]): StringReference[] {
typeof part['output'] === 'string'
) {
refs.push({
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
get: () => part['output'] as string,
set: (val: string) => (part['output'] = val),
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
len: () => (part['output'] as string).length,
});
}
+1
View File
@@ -316,6 +316,7 @@ export class ToolCallEvent implements BaseTelemetryEvent {
}
}
} else {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
this.function_name = function_name as string;
this.function_args = function_args!;
this.duration_ms = duration_ms!;
@@ -62,6 +62,7 @@ export class MockMessageBus {
if (!this.subscriptions.has(type)) {
this.subscriptions.set(type, new Set());
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
this.subscriptions.get(type)!.add(listener as (message: Message) => void);
},
);
@@ -73,6 +74,7 @@ export class MockMessageBus {
<T extends Message>(type: T['type'], listener: (message: T) => void) => {
const listeners = this.subscriptions.get(type);
if (listeners) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
listeners.delete(listener as (message: Message) => void);
}
},
@@ -101,6 +103,7 @@ export class MockMessageBus {
* Create a mock MessageBus for testing
*/
export function createMockMessageBus(): MessageBus {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return new MockMessageBus() as unknown as MessageBus;
}
@@ -110,5 +113,6 @@ export function createMockMessageBus(): MessageBus {
export function getMockMessageBusInstance(
messageBus: MessageBus,
): MockMessageBus {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return messageBus as unknown as MockMessageBus;
}
@@ -19,6 +19,7 @@ export function createMockWorkspaceContext(
): WorkspaceContext {
const allDirs = [rootDir, ...additionalDirs];
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const mockWorkspaceContext = {
addDirectory: vi.fn(),
getDirectories: vi.fn().mockReturnValue(allDirs),
@@ -175,6 +175,7 @@ export class ActivateSkillTool extends BaseDeclarativeTool<
} else {
schema = z.object({
name: z
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
.enum(skillNames as [string, ...string[]])
.describe('The name of the skill to activate.'),
});
+8 -1
View File
@@ -875,6 +875,7 @@ class LenientJsonSchemaValidator implements jsonSchemaValidator {
);
return (input: unknown) => ({
valid: true as const,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
data: input as T,
errorMessage: undefined,
});
@@ -889,6 +890,7 @@ export function populateMcpServerCommand(
): Record<string, MCPServerConfig> {
if (mcpServerCommand) {
const cmd = mcpServerCommand;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const args = parse(cmd, process.env) as string[];
if (args.some((arg) => typeof arg !== 'string')) {
throw new Error('failed to parse mcpServerCommand: ' + cmd);
@@ -1068,6 +1070,7 @@ export async function discoverTools(
'error',
`Error discovering tool: '${
toolDef.name
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
}' from MCP server '${mcpServerName}': ${(error as Error).message}`,
error,
);
@@ -1121,6 +1124,7 @@ class McpCallableTool implements CallableTool {
const result = await this.client.callTool(
{
name: call.name!,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
arguments: call.args as Record<string, unknown>,
},
undefined,
@@ -1550,6 +1554,7 @@ export async function connectToMcpServer(
return { client: mcpClient, transport };
} catch (error) {
await transport.close();
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
firstAttemptError = error as Error;
throw error;
}
@@ -1589,6 +1594,7 @@ export async function connectToMcpServer(
);
return { client: mcpClient, transport: sseTransport };
} catch (sseFallbackError) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
sseError = sseFallbackError as Error;
// If SSE also returned 401, handle OAuth below
@@ -1929,6 +1935,7 @@ export async function createTransport(
let transport: Transport = new StdioClientTransport({
command: mcpServerConfig.command,
args: mcpServerConfig.args || [],
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
env: sanitizeEnvironment(
{
...process.env,
@@ -1965,7 +1972,7 @@ export async function createTransport(
const underlyingTransport =
transport instanceof XcodeMcpBridgeFixTransport
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
? // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
(transport as any).transport
: transport;
+2
View File
@@ -373,6 +373,7 @@ function transformResourceLinkBlock(block: McpResourceLinkBlock): Part {
*/
function transformMcpContentToParts(sdkResponse: Part[]): Part[] {
const funcResponse = sdkResponse?.[0]?.functionResponse;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const mcpContent = funcResponse?.response?.['content'] as McpContentBlock[];
const toolName = funcResponse?.name || 'unknown tool';
@@ -410,6 +411,7 @@ function transformMcpContentToParts(sdkResponse: Part[]): Part[] {
* @returns A formatted string representing the tool's output.
*/
function getStringifiedResultForDisplay(rawResponse: Part[]): string {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const mcpContent = rawResponse?.[0]?.functionResponse?.response?.[
'content'
] as McpContentBlock[];
+1
View File
@@ -94,6 +94,7 @@ async function readMemoryFileContent(): Promise<string> {
try {
return await fs.readFile(getGlobalMemoryFilePath(), 'utf-8');
} catch (err) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const error = err as Error & { code?: string };
if (!(error instanceof Error) || error.code !== 'ENOENT') throw err;
return '';
+6
View File
@@ -265,7 +265,9 @@ export class ToolRegistry {
}
if (priorityA === 2) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const serverA = (toolA as DiscoveredMCPTool).serverName;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const serverB = (toolB as DiscoveredMCPTool).serverName;
return serverA.localeCompare(serverB);
}
@@ -319,6 +321,7 @@ export class ToolRegistry {
'Tool discovery command is empty or contains only whitespace.',
);
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const proc = spawn(cmdParts[0] as string, cmdParts.slice(1) as string[]);
let stdout = '';
const stdoutDecoder = new StringDecoder('utf8');
@@ -398,6 +401,7 @@ export class ToolRegistry {
} else if (Array.isArray(tool['functionDeclarations'])) {
functions.push(...tool['functionDeclarations']);
} else if (tool['name']) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
functions.push(tool as FunctionDeclaration);
}
}
@@ -420,6 +424,7 @@ export class ToolRegistry {
func.name,
DISCOVERED_TOOL_PREFIX + func.name,
func.description ?? '',
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
parameters as Record<string, unknown>,
this.messageBus,
),
@@ -552,6 +557,7 @@ export class ToolRegistry {
getToolsByServer(serverName: string): AnyDeclarativeTool[] {
const serverTools: AnyDeclarativeTool[] = [];
for (const tool of this.getActiveTools()) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
if ((tool as DiscoveredMCPTool)?.serverName === serverName) {
serverTools.push(tool);
}
+5
View File
@@ -195,6 +195,7 @@ export abstract class BaseToolInvocation<
correlationId,
toolCall: {
name: this._toolName,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
args: this.params as Record<string, unknown>,
},
serverName: this._serverName,
@@ -536,6 +537,7 @@ export function isTool(obj: unknown): obj is AnyDeclarativeTool {
obj !== null &&
'name' in obj &&
'build' in obj &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
typeof (obj as AnyDeclarativeTool).build === 'function'
);
}
@@ -590,8 +592,10 @@ export function hasCycleInSchema(schema: object): boolean {
) {
return null;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
current = (current as Record<string, unknown>)[segment];
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return current as object;
}
@@ -639,6 +643,7 @@ export function hasCycleInSchema(schema: object): boolean {
if (Object.prototype.hasOwnProperty.call(node, key)) {
if (
traverse(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(node as Record<string, unknown>)[key],
visitedRefs,
pathRefs,
+2
View File
@@ -194,6 +194,7 @@ ${textContent}
returnDisplay: `Content for ${url} processed using fallback fetch.`,
};
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const error = e as Error;
const errorMessage = `Error during fallback fetch for ${url}: ${error.message}`;
return {
@@ -291,6 +292,7 @@ ${textContent}
const sources = groundingMetadata?.groundingChunks as
| GroundingChunkItem[]
| undefined;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const groundingSupports = groundingMetadata?.groundingSupports as
| GroundingSupportItem[]
| undefined;
+1
View File
@@ -91,6 +91,7 @@ class WebSearchToolInvocation extends BaseToolInvocation<
const sources = groundingMetadata?.groundingChunks as
| GroundingChunkItem[]
| undefined;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const groundingSupports = groundingMetadata?.groundingSupports as
| GroundingSupportItem[]
| undefined;
@@ -75,7 +75,7 @@ export class XcodeMcpBridgeFixTransport
// We can cast because we verified 'result' is in response,
// but TS might still be picky if the type is a strict union.
// Let's treat it safely.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
const result = response.result as any;
// Check if we have content but missing structuredContent
+2
View File
@@ -80,6 +80,7 @@ export async function bfsFileSearch(
return { currentDir, entries };
} catch (error) {
// Warn user that a directory could not be read, as this affects search results.
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const message = (error as Error)?.message ?? 'Unknown error';
debugLogger.warn(
`[WARN] Skipping unreadable directory: ${currentDir} (${message})`,
@@ -153,6 +154,7 @@ export function bfsFileSearchSync(
foundFiles,
);
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const message = (error as Error)?.message ?? 'Unknown error';
debugLogger.warn(
`[WARN] Skipping unreadable directory: ${currentDir} (${message})`,
@@ -49,6 +49,7 @@ export function generateCheckpointFileName(
toolCall: ToolCallRequestInfo,
): string | null {
const toolArgs = toolCall.args;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const toolFilePath = toolArgs['file_path'] as string;
if (!toolFilePath) {
@@ -167,6 +168,7 @@ export function getCheckpointInfoList(
for (const [file, content] of checkpointFiles) {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const toolCallData = JSON.parse(content) as ToolCallData;
if (toolCallData.messageId) {
checkpointInfoList.push({
+6 -3
View File
@@ -208,9 +208,12 @@ export async function resolveEditorAsync(
coreEvents.emit(CoreEvent.RequestEditorSelection);
return once(coreEvents, CoreEvent.EditorSelected, { signal })
.then(([payload]) => (payload as EditorSelectedPayload).editor)
.catch(() => undefined);
return (
once(coreEvents, CoreEvent.EditorSelected, { signal })
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
.then(([payload]) => (payload as EditorSelectedPayload).editor)
.catch(() => undefined)
);
}
/**
+3
View File
@@ -98,6 +98,7 @@ interface ResponseData {
export function toFriendlyError(error: unknown): unknown {
if (error && typeof error === 'object' && 'response' in error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const gaxiosError = error as GaxiosError;
const data = parseResponseData(gaxiosError);
if (data && data.error && data.error.message && data.error.code) {
@@ -122,11 +123,13 @@ function parseResponseData(error: GaxiosError): ResponseData | undefined {
// Inexplicably, Gaxios sometimes doesn't JSONify the response data.
if (typeof error.response?.data === 'string') {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return JSON.parse(error.response?.data) as ResponseData;
} catch {
return undefined;
}
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return error.response?.data as ResponseData | undefined;
}
+11 -12
View File
@@ -199,14 +199,14 @@ export class CoreEventEmitter extends EventEmitter<CoreEvents> {
if (this._eventBacklog.length >= CoreEventEmitter.MAX_BACKLOG_SIZE) {
this._eventBacklog.shift();
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
this._eventBacklog.push({ event, args } as EventBacklogItem);
} else {
(
this.emit as <K extends keyof CoreEvents>(
event: K,
...args: CoreEvents[K]
) => boolean
)(event, ...args);
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(this.emit as (event: K, ...args: CoreEvents[K]) => boolean)(
event,
...args,
);
}
}
@@ -319,12 +319,11 @@ export class CoreEventEmitter extends EventEmitter<CoreEvents> {
const backlog = [...this._eventBacklog];
this._eventBacklog.length = 0; // Clear in-place
for (const item of backlog) {
(
this.emit as <K extends keyof CoreEvents>(
event: K,
...args: CoreEvents[K]
) => boolean
)(item.event, ...item.args);
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(this.emit as (event: keyof CoreEvents, ...args: unknown[]) => boolean)(
item.event,
...item.args,
);
}
}
}
@@ -102,6 +102,7 @@ export function convertToFunctionResponse(
if (inlineDataParts.length > 0) {
if (isMultimodalFRSupported) {
// Nest inlineData if supported by the model
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(part.functionResponse as unknown as { parts: Part[] }).parts =
inlineDataParts;
} else {
@@ -151,6 +152,7 @@ export function getFunctionCalls(
}
const functionCallParts = parts
.filter((part) => !!part.functionCall)
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
.map((part) => part.functionCall as FunctionCall);
return functionCallParts.length > 0 ? functionCallParts : undefined;
}
@@ -163,6 +165,7 @@ export function getFunctionCallsFromParts(
}
const functionCallParts = parts
.filter((part) => !!part.functionCall)
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
.map((part) => part.functionCall as FunctionCall);
return functionCallParts.length > 0 ? functionCallParts : undefined;
}
+4
View File
@@ -195,6 +195,7 @@ export function parseGoogleApiError(error: unknown): GoogleApiError | null {
if (Array.isArray(errorDetails)) {
for (const detail of errorDetails) {
if (detail && typeof detail === 'object') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const detailObj = detail as Record<string, unknown>;
const typeKey = Object.keys(detailObj).find(
(key) => key.trim() === '@type',
@@ -205,6 +206,7 @@ export function parseGoogleApiError(error: unknown): GoogleApiError | null {
delete detailObj[typeKey];
}
// We can just cast it; the consumer will have to switch on @type
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
details.push(detailObj as unknown as GoogleApiErrorDetail);
}
}
@@ -253,6 +255,7 @@ function fromGaxiosError(errorObj: object): ErrorShape | undefined {
if (typeof data === 'object' && data !== null) {
if ('error' in data) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
outerError = (data as { error: ErrorShape }).error;
}
}
@@ -309,6 +312,7 @@ function fromApiError(errorObj: object): ErrorShape | undefined {
if (typeof data === 'object' && data !== null) {
if ('error' in data) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
outerError = (data as { error: ErrorShape }).error;
}
}
+4 -3
View File
@@ -24,9 +24,10 @@ export function getErrorStatus(error: unknown): number | undefined {
typeof (error as { response?: unknown }).response === 'object' &&
(error as { response?: unknown }).response !== null
) {
const response = (
error as { response: { status?: unknown; headers?: unknown } }
).response;
const response =
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(error as { response: { status?: unknown; headers?: unknown } })
.response;
if ('status' in response && typeof response.status === 'number') {
return response.status;
}
@@ -107,6 +107,7 @@ async function generateJsonWithTimeout<T>(
timeoutSignal,
]),
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return result as T;
} catch (err) {
debugLogger.debug(
@@ -54,6 +54,7 @@ async function findProjectRoot(startDir: string): Promise<string | null> {
typeof error === 'object' &&
error !== null &&
'code' in error &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(error as { code: string }).code === 'ENOENT';
// Only log unexpected errors in non-test environments
@@ -63,6 +64,7 @@ async function findProjectRoot(startDir: string): Promise<string | null> {
if (!isENOENT && !isTestEnv) {
if (typeof error === 'object' && error !== null && 'code' in error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const fsError = error as { code: string; message: string };
logger.warn(
`Error checking for .git directory at ${gitPath}: ${fsError.message}`,
@@ -311,6 +313,7 @@ export function concatenateInstructions(
return instructionContents
.filter((item) => typeof item.content === 'string')
.map((item) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const trimmedContent = (item.content as string).trim();
if (trimmedContent.length === 0) {
return null;
@@ -359,6 +362,7 @@ export async function loadGlobalMemory(
.filter((item) => item.content !== null)
.map((item) => ({
path: item.filePath,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
content: item.content as string,
})),
};
@@ -456,6 +460,7 @@ export async function loadEnvironmentMemory(
.filter((item) => item.content !== null)
.map((item) => ({
path: item.filePath,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
content: item.content as string,
})),
};
@@ -640,6 +645,7 @@ export async function loadJitSubdirectoryMemory(
.filter((item) => item.content !== null)
.map((item) => ({
path: item.filePath,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
content: item.content as string,
})),
};
@@ -109,6 +109,7 @@ export async function checkNextSpeaker(
];
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const parsedResponse = (await baseLlmClient.generateJson({
modelConfigKey: { model: 'next-speaker-checker' },
contents,
+1
View File
@@ -30,6 +30,7 @@ export function partToString(
}
// Cast to Part, assuming it might contain project-specific fields
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const part = value as Part & {
videoMetadata?: unknown;
thought?: string;
@@ -20,7 +20,9 @@ export function isApiError(error: unknown): error is ApiError {
typeof error === 'object' &&
error !== null &&
'error' in error &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
typeof (error as ApiError).error === 'object' &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
'message' in (error as ApiError).error
);
}
@@ -30,6 +32,7 @@ export function isStructuredError(error: unknown): error is StructuredError {
typeof error === 'object' &&
error !== null &&
'message' in error &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
typeof (error as StructuredError).message === 'string'
);
}
+3
View File
@@ -68,6 +68,7 @@ function getNetworkErrorCode(error: unknown): string | undefined {
return undefined;
}
if ('code' in obj && typeof (obj as { code: unknown }).code === 'string') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return (obj as { code: string }).code;
}
return undefined;
@@ -196,6 +197,7 @@ export async function retryWithBackoff<T>(
if (
shouldRetryOnContent &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
shouldRetryOnContent(result as GenerateContentResponse)
) {
const jitter = currentDelay * 0.3 * (Math.random() * 2 - 1);
@@ -327,6 +329,7 @@ export async function retryWithBackoff<T>(
// Generic retry logic for other errors
if (
attempt >= maxAttempts ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
!shouldRetryOnError(error as Error, retryFetchErrors)
) {
throw error;
@@ -56,6 +56,7 @@ function removeEmptyObjects(data: any): object {
export function safeJsonStringifyBooleanValuesOnly(obj: any): string {
let configSeen = false;
return JSON.stringify(removeEmptyObjects(obj), (key, value) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
if ((value as Config) !== null && !configSeen) {
configSeen = true;
return value;
+5 -3
View File
@@ -12,9 +12,9 @@ import * as addFormats from 'ajv-formats';
import { debugLogger } from './debugLogger.js';
// Ajv's ESM/CJS interop: use 'any' for compatibility as recommended by Ajv docs
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
const AjvClass = (AjvPkg as any).default || AjvPkg;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
const Ajv2020Class = (Ajv2020Pkg as any).default || Ajv2020Pkg;
const ajvOptions = {
@@ -34,7 +34,7 @@ const ajvDefault: Ajv = new AjvClass(ajvOptions);
// Draft-2020-12 validator for MCP servers using rmcp
const ajv2020: Ajv = new Ajv2020Class(ajvOptions);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
const addFormatsFunc = (addFormats as any).default || addFormats;
addFormatsFunc(ajvDefault);
addFormatsFunc(ajv2020);
@@ -90,6 +90,7 @@ export class SchemaValidator {
// This matches LenientJsonSchemaValidator behavior in mcp-client.ts.
debugLogger.warn(
`Failed to compile schema (${
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(schema as Record<string, unknown>)?.['$schema'] ?? '<no $schema>'
}): ${error instanceof Error ? error.message : String(error)}. ` +
'Skipping parameter validation.',
@@ -121,6 +122,7 @@ export class SchemaValidator {
// Skip validation rather than blocking tool usage.
debugLogger.warn(
`Failed to validate schema (${
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
(schema as Record<string, unknown>)?.['$schema'] ?? '<no $schema>'
}): ${error instanceof Error ? error.message : String(error)}. ` +
'Skipping schema validation.',
+3
View File
@@ -66,6 +66,7 @@ export async function isDirectorySecure(
} catch (error) {
return {
secure: false,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
reason: `A security check for the system policy directory '${dirPath}' failed and could not be completed. Please file a bug report. Original error: ${(error as Error).message}`,
};
}
@@ -93,11 +94,13 @@ export async function isDirectorySecure(
return { secure: true };
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
return { secure: true };
}
return {
secure: false,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
reason: `Failed to access directory: ${(error as Error).message}`,
};
}
+1
View File
@@ -237,6 +237,7 @@ function parseCommandTree(
progressCallback: () => {
if (performance.now() > deadline) {
timedOut = true;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return true as unknown as void; // Returning true cancels parsing, but type says void
}
},
+20
View File
@@ -52,6 +52,26 @@ export function disableSimulationAfterFallback(): void {
fallbackOccurred = true;
}
/**
* Create a simulated 429 error response
*/
export function createSimulated429Error(): Error {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const error = new Error('Rate limit exceeded (simulated)') as Error & {
status: number;
};
error.status = 429;
return error;
}
/**
* Reset simulation state when switching auth methods
*/
export function resetSimulationState(): void {
fallbackOccurred = false;
resetRequestCounter();
}
/**
* Enable/disable 429 simulation programmatically (for tests)
*/
@@ -88,6 +88,7 @@ function estimateFunctionResponseTokens(part: Part, depth: number): number {
}
// Gemini 3: Handle nested multimodal parts recursively.
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const nestedParts = (fr as unknown as { parts?: Part[] }).parts;
if (nestedParts && nestedParts.length > 0) {
totalTokens += estimateTokenCountSync(nestedParts, depth + 1);
+1
View File
@@ -104,6 +104,7 @@ export function doesToolInvocationMatch(
// This invocation has no command - nothing to check.
continue;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
command = String((invocation.params as { command: string }).command);
}
@@ -37,6 +37,7 @@ export class UserAccountManager {
debugLogger.log('Invalid accounts file schema, starting fresh.');
return defaultState;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const { active, old } = parsed as Partial<UserAccounts>;
const isValid =
(active === undefined || active === null || typeof active === 'string') &&