Skip to content
Closed
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
6 changes: 4 additions & 2 deletions open-sse/config/providerHeaderProfiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export const QWEN_STAINLESS_LANG = "js";

export const QODER_DEFAULT_USER_AGENT = "Qoder-Cli";

export const KIRO_SDK_USER_AGENT = "AWS-SDK-JS/3.0.0 kiro-ide/1.0.0";
export const KIRO_AMZ_USER_AGENT = "aws-sdk-js/3.0.0 kiro-ide/1.0.0";
export const KIRO_SDK_USER_AGENT = "aws-sdk-js/1.0.0 KiroIDE-1.0.116";
export const KIRO_AMZ_USER_AGENT = "aws-sdk-js/1.0.0 KiroIDE-1.0.116";
export const KIRO_STREAMING_TARGET =
"AmazonCodeWhispererStreamingService.GenerateAssistantResponse";

Expand Down Expand Up @@ -145,6 +145,8 @@ export function getKiroServiceHeaders(
"X-Amz-Target": KIRO_STREAMING_TARGET,
"User-Agent": KIRO_SDK_USER_AGENT,
"X-Amz-User-Agent": KIRO_AMZ_USER_AGENT,
"x-amzn-kiro-agent-mode": "vibe",
"x-amzn-codewhisperer-optout": "true",
};
}

Expand Down
31 changes: 23 additions & 8 deletions open-sse/executors/kiro.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { v4 as uuidv4 } from "uuid";

import {
BaseExecutor,
mergeUpstreamExtraHeaders,
Expand All @@ -6,7 +8,9 @@ import {
type ProviderCredentials,
} from "./base.ts";
import { PROVIDERS } from "../config/constants.ts";
import { v4 as uuidv4 } from "uuid";
import { KIRO_STREAMING_TARGET } from "../config/providerHeaderProfiles.ts";
import { buildKiroClientHeaders } from "../services/kiroClientProfile.ts";
import { kiroRuntimeHost, resolveKiroRuntimeRegion } from "../services/kiroRegion.ts";
import { refreshKiroToken } from "../services/tokenRefresh.ts";
import {
isExternalIdpAuthMethod,
Expand All @@ -19,7 +23,6 @@ import {
type KiroThinkingState,
} from "./kiroThinking.ts";
import { ByteQueue, TEXT_ENCODER, parseEventFrame } from "./kiro/eventstream.ts";
import { kiroRuntimeHost, resolveKiroRuntimeRegion } from "../services/kiroRegion.ts";

type JsonRecord = Record<string, unknown>;

Expand Down Expand Up @@ -189,10 +192,13 @@ export class KiroExecutor extends BaseExecutor {
void stream;
const headers = {
...this.config.headers,
...buildKiroClientHeaders(
credentials.providerSpecificData,
credentials.accessToken || credentials.apiKey || "",
"runtime"
),
"Amz-Sdk-Request": "attempt=1; max=3",
"Amz-Sdk-Invocation-Id": uuidv4(),
"x-amzn-bedrock-cache-control": "enable",
"anthropic-beta": "prompt-caching-2024-07-31",
};

const authMethod =
Expand Down Expand Up @@ -263,12 +269,21 @@ export class KiroExecutor extends BaseExecutor {
log,
upstreamExtraHeaders,
}: ExecuteInput) {
// Route to the region-specific CodeWhisperer/Amazon Q endpoint. Enterprise IAM Identity
// Center accounts (e.g. eu-central-1) are rejected by the default us-east-1 host; only the
// regional endpoint accepts the region-bound token + profileArn.
// Current Kiro IDE accounts use the regional native runtime. Keep the legacy
// CodeWhisperer/Amazon Q host for Amazon Q and long-lived Kiro API keys.
const region = resolveKiroRegion(credentials);
const url = `${kiroRuntimeHost(region)}/generateAssistantResponse`;
const authMethod = credentials.providerSpecificData?.authMethod;
const useLegacyRuntime = this.provider === "amazon-q" || authMethod === "api_key";
const baseUrl = useLegacyRuntime
? kiroRuntimeHost(region)
: `https://runtime.${region}.kiro.dev`;
const url = `${baseUrl}/generateAssistantResponse`;
const headers = this.buildHeaders(credentials, stream);
if (useLegacyRuntime) {
headers["X-Amz-Target"] = KIRO_STREAMING_TARGET;
} else {
delete headers["X-Amz-Target"];
}
mergeUpstreamExtraHeaders(headers, upstreamExtraHeaders);
const transformedBody = await this.transformRequest(model, body, stream, credentials);

Expand Down
71 changes: 71 additions & 0 deletions open-sse/services/kiroClientProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { createHash } from "node:crypto";
import { createRequire } from "node:module";
import { platform, release } from "node:os";

type JsonRecord = Record<string, unknown>;

export const KIRO_IDE_VERSION = "1.0.116";
export const KIRO_SDK_VERSION = "1.0.0";

const nodeRequire = createRequire(import.meta.url);
let nativeMachineIdSync: (() => string) | null = null;
try {
const module = nodeRequire("node-machine-id") as {
machineIdSync?: () => string;
default?: { machineIdSync?: () => string };
};
nativeMachineIdSync = module.machineIdSync || module.default?.machineIdSync || null;
} catch {
nativeMachineIdSync = null;
}

function asRecord(value: unknown): JsonRecord {
return value && typeof value === "object" && !Array.isArray(value) ? (value as JsonRecord) : {};
}

function nonEmptyString(value: unknown): string | null {
return typeof value === "string" && value.trim() ? value.trim() : null;
}

function kiroMachineId(providerSpecificData: unknown, accessToken: string): string {
const data = asRecord(providerSpecificData);
try {
const native = nonEmptyString(nativeMachineIdSync?.());
if (native && /^[a-f0-9]{64}$/i.test(native)) return native.toLowerCase();
} catch {
// Fall through to a deterministic connection-derived id.
}

const persisted = nonEmptyString(data.kiroMachineId) || nonEmptyString(data.machineId);
if (persisted && /^[a-f0-9]{64}$/i.test(persisted)) return persisted.toLowerCase();

const seed =
persisted ||
nonEmptyString(data.clientId) ||
nonEmptyString(data.profileArn) ||
accessToken ||
"kiro-anonymous";
return createHash("sha256").update(seed).digest("hex");
}

export function buildKiroClientHeaders(
providerSpecificData: unknown,
accessToken: string,
service: "runtime" | "control-plane"
): Record<string, string> {
const machineId = kiroMachineId(providerSpecificData, accessToken);
const customUserAgent = `KiroIDE-${KIRO_IDE_VERSION}-${machineId}`;
const serviceId = service === "runtime" ? "kiroruntime" : "kirocontrolplanebearer";
const nodeVersion = process.versions?.node || process.version?.replace(/^v/, "") || "unknown";
const userAgent =
`aws-sdk-js/${KIRO_SDK_VERSION} ua/2.1 ` +
`os/${platform()}#${release()} lang/js md/nodejs#${nodeVersion} ` +
`api/${serviceId}#${KIRO_SDK_VERSION} m/N,E ${customUserAgent}`;

return {
"User-Agent": userAgent,
"X-Amz-User-Agent": `aws-sdk-js/${KIRO_SDK_VERSION} ${customUserAgent}`,
"x-amzn-codewhisperer-optout": "true",
...(service === "runtime" ? { "x-amzn-kiro-agent-mode": "vibe" } : {}),
};
}
Loading
Loading