diff --git a/packages/server/src/server/agent/provider-registry.ts b/packages/server/src/server/agent/provider-registry.ts index 6bbeb1fb6..941156aa6 100644 --- a/packages/server/src/server/agent/provider-registry.ts +++ b/packages/server/src/server/agent/provider-registry.ts @@ -36,6 +36,10 @@ import { type AgentProviderDefinition, } from "./provider-manifest.js"; +function isNonEmptyStringArray(value: string[]): value is [string, ...string[]] { + return value.length > 0; +} + export type { AgentProviderDefinition }; export { AGENT_PROVIDER_DEFINITIONS, getAgentProviderDefinition }; @@ -461,9 +465,11 @@ function addDerivedProviders( } if (override.extends === "acp") { - if (!override.command) { + if (!override.command || !isNonEmptyStringArray(override.command)) { throw new Error(`ACP provider '${providerId}' requires a command`); } + // Capture command in const for closure - TypeScript can't track type refinement inside closures + const command = override.command; resolvedProviders.set(providerId, { definition: createDerivedDefinition( @@ -484,7 +490,7 @@ function addDerivedProviders( createBaseClient: (logger) => new GenericACPAgentClient({ logger, - command: override.command!, + command, env: override.env, }), }); diff --git a/packages/server/src/server/agent/providers/diagnostic-utils.ts b/packages/server/src/server/agent/providers/diagnostic-utils.ts index 248cbe9bf..81778f86b 100644 --- a/packages/server/src/server/agent/providers/diagnostic-utils.ts +++ b/packages/server/src/server/agent/providers/diagnostic-utils.ts @@ -43,8 +43,9 @@ function truncateForDiagnostic(value: string): string { return `${trimmed.slice(0, DIAGNOSTIC_OUTPUT_CAP)}…(truncated)`; } -function readStringProperty(error: object, key: string): string | undefined { - const value = (error as Record)[key]; +function readStringProperty(error: Error, key: string): string | undefined { + if (!(key in error)) return undefined; + const value = (error as Error & Record)[key]; if (typeof value === "string") { return value; } @@ -54,8 +55,9 @@ function readStringProperty(error: object, key: string): string | undefined { return undefined; } -function readUnknownProperty(error: object, key: string): unknown { - return (error as Record)[key]; +function readUnknownProperty(error: Error, key: string): unknown { + if (!(key in error)) return undefined; + return (error as Error & Record)[key]; } function pushIfNonEmpty(sections: string[], label: string, value: string | undefined): void { diff --git a/packages/server/src/server/agent/providers/generic-acp-agent.ts b/packages/server/src/server/agent/providers/generic-acp-agent.ts index 14ef3de73..682b16f61 100644 --- a/packages/server/src/server/agent/providers/generic-acp-agent.ts +++ b/packages/server/src/server/agent/providers/generic-acp-agent.ts @@ -5,7 +5,7 @@ import { ACPAgentClient } from "./acp-agent.js"; interface GenericACPAgentClientOptions { logger: Logger; - command: string[]; + command: [string, ...string[]]; env?: Record; } @@ -13,20 +13,16 @@ export class GenericACPAgentClient extends ACPAgentClient { private readonly command: [string, ...string[]]; constructor(options: GenericACPAgentClientOptions) { - if (options.command.length === 0) { - throw new Error("Generic ACP provider requires a non-empty command"); - } - super({ provider: "acp", logger: options.logger, runtimeSettings: { env: options.env, }, - defaultCommand: options.command as [string, ...string[]], + defaultCommand: options.command, }); - this.command = options.command as [string, ...string[]]; + this.command = options.command; } protected override async resolveLaunchCommand(): Promise<{ command: string; args: string[] }> { diff --git a/packages/server/src/server/agent/providers/mock-load-test-agent.ts b/packages/server/src/server/agent/providers/mock-load-test-agent.ts index d1c708aac..a99c4ed0e 100644 --- a/packages/server/src/server/agent/providers/mock-load-test-agent.ts +++ b/packages/server/src/server/agent/providers/mock-load-test-agent.ts @@ -166,9 +166,13 @@ function parseLargeAgentStreamPayloadPrompt( if (!Number.isFinite(bytes) || bytes <= 0) { return null; } + const kindValue = match[2]?.toLowerCase(); + if (kindValue !== "diff" && kindValue !== "file" && kindValue !== "image") { + return null; + } return { bytes: Math.min(bytes, 1_000_000), - kind: match[2]?.toLowerCase() as LargeAgentStreamPayloadRequest["kind"], + kind: kindValue, }; } diff --git a/packages/server/src/server/agent/providers/tool-call-detail-primitives.ts b/packages/server/src/server/agent/providers/tool-call-detail-primitives.ts index ab4dce148..b2adcabb1 100644 --- a/packages/server/src/server/agent/providers/tool-call-detail-primitives.ts +++ b/packages/server/src/server/agent/providers/tool-call-detail-primitives.ts @@ -859,19 +859,14 @@ export function toolDetailBranchByName< output: z.infer | null, ) => ToolCallDetail | undefined, ) { - return z - .object({ - name: z.literal(name), - input: inputSchema.nullable(), - output: outputSchema.nullable(), - }) - .transform((value) => { - const parsed = value as unknown as { - input: z.infer | null; - output: z.infer | null; - }; - return mapper(parsed.input, parsed.output); - }); + const schema = z.object({ + name: z.literal(name), + input: inputSchema.nullable(), + output: outputSchema.nullable(), + }); + return schema.transform((value: z.infer) => { + return mapper(value.input, value.output); + }); } export function toolDetailBranchByToolName< @@ -887,19 +882,14 @@ export function toolDetailBranchByToolName< output: z.infer | null, ) => ToolCallDetail | undefined, ) { - return z - .object({ - toolName: z.literal(toolName), - input: inputSchema.nullable(), - output: outputSchema.nullable(), - }) - .transform((value) => { - const parsed = value as unknown as { - input: z.infer | null; - output: z.infer | null; - }; - return mapper(parsed.input, parsed.output); - }); + const schema = z.object({ + toolName: z.literal(toolName), + input: inputSchema.nullable(), + output: outputSchema.nullable(), + }); + return schema.transform((value: z.infer) => { + return mapper(value.input, value.output); + }); } export function toolDetailBranchByNameWithCwd< @@ -916,19 +906,13 @@ export function toolDetailBranchByNameWithCwd< cwd: string | null, ) => ToolCallDetail | undefined, ) { - return z - .object({ - name: z.literal(name), - input: inputSchema.nullable(), - output: outputSchema.nullable(), - cwd: z.string().optional().nullable(), - }) - .transform((value) => { - const parsed = value as unknown as { - input: z.infer | null; - output: z.infer | null; - cwd?: string | null; - }; - return mapper(parsed.input, parsed.output, parsed.cwd ?? null); - }); + const schema = z.object({ + name: z.literal(name), + input: inputSchema.nullable(), + output: outputSchema.nullable(), + cwd: z.string().optional().nullable(), + }); + return schema.transform((value: z.infer) => { + return mapper(value.input, value.output, value.cwd ?? null); + }); }