Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 81 additions & 1 deletion server/claude-agent-sdk-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ export class ClaudeAgentSDKManager extends BaseProcessManager {
private buildSDKOptions(codon: Codon, previousSessionId: string | null): Options {
// Model override is already applied in loadCodonSequence(), so just use codon.model
const modelInfo = codon.model;
const isBedrock = modelInfo.providerId.toLowerCase() === "amazon-bedrock";

const options: Options = {
model: modelInfo.modelId,
Expand All @@ -286,6 +287,13 @@ export class ClaudeAgentSDKManager extends BaseProcessManager {
settingSources: ["user"],
};

// Enable Bedrock mode if using Bedrock model
if (isBedrock) {
if (!options.env) options.env = {};
options.env.CLAUDE_CODE_USE_BEDROCK = "1";
this.logger.log("Enabling Bedrock mode for Claude SDK");
}

// Use custom Claude Code executable path if provided
if (process.env.CLAUDE_PATH_TO_CLAUDE_EXECUTABLE) {
options.pathToClaudeCodeExecutable = process.env.CLAUDE_PATH_TO_CLAUDE_EXECUTABLE;
Expand Down Expand Up @@ -370,6 +378,29 @@ export class ClaudeAgentSDKManager extends BaseProcessManager {
this.logger.log(`Using custom Anthropic base URL: ${this.anthropicBaseUrl}`);
}

// Pass through AWS credentials for Bedrock
if (isBedrock) {
const awsKeys = [
"AWS_PROFILE",
"AWS_REGION",
"AWS_DEFAULT_REGION",
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY",
"AWS_SESSION_TOKEN",
"AWS_BEARER_TOKEN_BEDROCK",
"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI",
"AWS_CONTAINER_CREDENTIALS_FULL_URI",
"AWS_WEB_IDENTITY_TOKEN_FILE",
];

for (const key of awsKeys) {
if (process.env[key]) {
options.env[key] = process.env[key];
this.logger.log(`Passing through AWS credential: ${key}`);
}
}
}

// Add codon-specific environment variables from config
// These will override any existing variables with the same name
if (codon.env) {
Expand Down Expand Up @@ -708,9 +739,10 @@ export class ClaudeAgentSDKManager extends BaseProcessManager {
* Run self-test to verify Claude Agent SDK environment setup.
* Checks for API authentication (API key or OAuth token) and SDK availability.
*
* @param modelInfo - Optional model info to check for Bedrock-specific requirements
* @returns Promise resolving to self-test results
*/
async runSelfTest(): Promise<ShimSelfTestResult> {
async runSelfTest(modelInfo?: import("./llm/models-dev-schema.js").ModelInfo): Promise<ShimSelfTestResult> {
this.logger.log("Running Claude Agent SDK self-test...");

const checks: ShimSelfTestResult["checks"] = [];
Expand Down Expand Up @@ -808,6 +840,54 @@ export class ClaudeAgentSDKManager extends BaseProcessManager {
});
}

// Check 5: Bedrock authentication (if Bedrock model)
if (modelInfo?.providerId.toLowerCase() === "amazon-bedrock") {
const hasAwsProfile = !!process.env.AWS_PROFILE;
const hasAwsKeys = !!(
process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY
);
const hasAwsBearerToken = !!process.env.AWS_BEARER_TOKEN_BEDROCK;
const hasAwsContainer = !!(
process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ||
process.env.AWS_CONTAINER_CREDENTIALS_FULL_URI
);
const hasAwsWebIdentity = !!process.env.AWS_WEB_IDENTITY_TOKEN_FILE;

const hasAwsAuth =
hasAwsProfile || hasAwsKeys || hasAwsBearerToken || hasAwsContainer || hasAwsWebIdentity;

if (!hasAwsAuth) {
checks.push({
name: "aws_credentials",
passed: false,
message: "No AWS credentials found (AWS_PROFILE, AWS_ACCESS_KEY_ID, etc.)",
});
} else {
const authMethods = [];
if (hasAwsProfile) authMethods.push(`profile: ${process.env.AWS_PROFILE}`);
if (hasAwsKeys) authMethods.push("access keys");
if (hasAwsBearerToken) authMethods.push("bearer token");
if (hasAwsContainer) authMethods.push("container credentials");
if (hasAwsWebIdentity) authMethods.push("web identity");

checks.push({
name: "aws_credentials",
passed: true,
message: `AWS credentials detected (${authMethods.join(", ")})`,
});
}

// Check AWS region
const hasRegion = !!(process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION);
checks.push({
name: "aws_region",
passed: hasRegion,
message: hasRegion
? `Region: ${process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION}`
: "No AWS_REGION or AWS_DEFAULT_REGION set",
});
}

// Overall result
const allPassed = checks.every((check) => check.passed);

Expand Down
22 changes: 13 additions & 9 deletions server/codon-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,9 @@ export class CodonRunner extends TypedEventEmitter<CodonRunnerEvents> {
logger: Logger,
anthropicBaseUrl?: string,
): Promise<ShimSelfTestResult> {
const isAnthropicModel = modelInfo.providerId.toLowerCase() === "anthropic";
const isClaudeModel = ["anthropic", "amazon-bedrock"].includes(
modelInfo.providerId.toLowerCase(),
);

// Create temporary log parser (required by managers)
const tempLogParserPath = path.join(os.tmpdir(), `self-test-parser-${Date.now()}.jsonl`);
Expand All @@ -433,8 +435,8 @@ export class CodonRunner extends TypedEventEmitter<CodonRunnerEvents> {
try {
let result: ShimSelfTestResult;

if (isAnthropicModel) {
// Use Claude Agent SDK Manager for Anthropic models
if (isClaudeModel) {
// Use Claude Agent SDK Manager for Claude models (Anthropic or Bedrock)
logger.log(
`Testing Claude Agent SDK for model: ${modelInfo.name} (${modelInfo.providerId}/${modelInfo.modelId})`,
"info",
Expand All @@ -449,7 +451,7 @@ export class CodonRunner extends TypedEventEmitter<CodonRunnerEvents> {
anthropicBaseUrl,
);

result = await manager.runSelfTest();
result = await manager.runSelfTest(modelInfo);
} else {
// Use Shim Process Manager for non-Anthropic models
logger.log(
Expand Down Expand Up @@ -580,12 +582,14 @@ export class CodonRunner extends TypedEventEmitter<CodonRunnerEvents> {
);
} else {
const modelInfo = this.config.codon.model;
const isAnthropicModel = modelInfo.providerId.toLowerCase() === "anthropic";
const isClaudeModel = ["anthropic", "amazon-bedrock"].includes(
modelInfo.providerId.toLowerCase(),
);

if (isAnthropicModel) {
// Use Claude Agent SDK for Anthropic models
if (isClaudeModel) {
// Use Claude Agent SDK for Claude models (Anthropic or Bedrock)
this.config.logger.log(
`Using Claude Agent SDK for Anthropic model: ${modelInfo.name} (${modelInfo.providerId}/${modelInfo.modelId})`,
`Using Claude Agent SDK for model: ${modelInfo.name} (${modelInfo.providerId}/${modelInfo.modelId})`,
"info",
);

Expand All @@ -599,7 +603,7 @@ export class CodonRunner extends TypedEventEmitter<CodonRunnerEvents> {
this.config.shimIdleTimeout,
);
} else {
// Use Shim for non-Anthropic models (e.g., Gemini)
// Use Shim for non-Claude models (e.g., Gemini, OpenAI)
this.config.logger.log(
`Using shim for model: ${modelInfo.name} (${modelInfo.providerId}/${modelInfo.modelId})`,
"info",
Expand Down