ns-omp-provider-kiro 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1483 -1050
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -245,6 +245,12 @@ function getKiroEndpoints(region) {
245
245
  runtime: `https://runtime.${region}.kiro.dev/`
246
246
  };
247
247
  }
248
+ function getKiroRegionFromProfileArn(profileArn) {
249
+ if (!profileArn)
250
+ return void 0;
251
+ const region = profileArn.split(":")[3];
252
+ return region && /^[a-z]{2}(?:-[a-z]+)+-\d$/.test(region) ? region : void 0;
253
+ }
248
254
  function getKiroRegionFromEndpoint(endpoint) {
249
255
  try {
250
256
  const [service, region, ...suffix] = new URL(endpoint).hostname.split(".");
@@ -267,6 +273,7 @@ var init_endpoints = __esm({
267
273
  "ap-southeast-1": "us-east-1",
268
274
  "ap-southeast-2": "us-east-1",
269
275
  "ap-northeast-1": "us-east-1",
276
+ "ap-northeast-2": "us-east-1",
270
277
  "ap-south-1": "us-east-1",
271
278
  "eu-west-1": "eu-central-1",
272
279
  "eu-west-2": "eu-central-1",
@@ -642,803 +649,337 @@ var init_kiro_ide = __esm({
642
649
  }
643
650
  });
644
651
 
645
- // ../kiro-core/dist/models.js
646
- var models_exports = {};
647
- __export(models_exports, {
648
- KIRO_MANAGEMENT_CACHE_PATH: () => KIRO_MANAGEMENT_CACHE_PATH,
649
- KIRO_MANAGEMENT_CACHE_SOURCE: () => KIRO_MANAGEMENT_CACHE_SOURCE,
650
- KIRO_MANAGEMENT_CACHE_VERSION: () => KIRO_MANAGEMENT_CACHE_VERSION,
651
- KIRO_MODEL_IDS: () => KIRO_MODEL_IDS,
652
- applyEffortLadder: () => applyEffortLadder,
653
- getCachedModels: () => getCachedModels,
654
- isCacheStale: () => isCacheStale,
655
- kiroModels: () => kiroModels,
656
- loadCachedModelIds: () => loadCachedModelIds,
657
- mapKiroCatalogModels: () => mapKiroCatalogModels,
658
- resolveApiRegion: () => resolveApiRegion,
659
- resolveKiroModel: () => resolveKiroModel,
660
- updateKiroModelsCache: () => updateKiroModelsCache
661
- });
662
- import { randomUUID } from "node:crypto";
663
- import { existsSync as existsSync3, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync } from "node:fs";
664
- import { homedir as homedir4 } from "node:os";
665
- import { join as join4 } from "node:path";
666
- function isRecord2(value) {
667
- return typeof value === "object" && value !== null && !Array.isArray(value);
668
- }
669
- function isPositiveNumber(value) {
670
- return typeof value === "number" && Number.isFinite(value) && value > 0;
671
- }
672
- function isEffortList(value) {
673
- return Array.isArray(value) && value.length > 0 && value.every((effort) => KIRO_EFFORT_ORDER.includes(effort));
652
+ // ../kiro-core/dist/oauth.js
653
+ function kiroUserAgent(service, sdkVersion) {
654
+ return {
655
+ "User-Agent": `aws-sdk-js/3.714.0 os/macos/24.3.0 lang/js md/nodejs/22.14.0 api/${service}/3.714.0 exec-env/kiro-cli/2.7.0 m/E`,
656
+ "amz-sdk-invocation-id": "00000000-0000-0000-0000-000000000000",
657
+ "amz-sdk-request": `attempt=1; max=${sdkVersion}`
658
+ };
674
659
  }
675
- function isEffortMap(value) {
676
- return isRecord2(value) && Object.values(value).every((mapped) => typeof mapped === "string");
660
+ function kiroAuthHeaders(token) {
661
+ const headers = { Authorization: `Bearer ${token}` };
662
+ if (isApiKey(token))
663
+ headers.tokentype = "API_KEY";
664
+ return headers;
677
665
  }
678
- function isCachedKiroModel(value) {
679
- if (!isRecord2(value))
680
- return false;
681
- const cost = value.cost;
682
- const input = value.input;
683
- const schema = value.additionalModelRequestFieldsSchema;
684
- const tokenLimits = value.tokenLimits;
685
- return typeof value.id === "string" && value.id.length > 0 && typeof value.kiroModelId === "string" && value.kiroModelId.length > 0 && typeof value.name === "string" && value.name.length > 0 && typeof value.reasoning === "boolean" && Array.isArray(input) && input.length > 0 && input.every((modality) => modality === "text" || modality === "image") && isRecord2(cost) && typeof cost.input === "number" && typeof cost.output === "number" && typeof cost.cacheRead === "number" && typeof cost.cacheWrite === "number" && isPositiveNumber(value.contextWindow) && isPositiveNumber(value.maxTokens) && (value.efforts === void 0 || isEffortList(value.efforts)) && (value.effortMap === void 0 || isEffortMap(value.effortMap)) && (schema === void 0 || isRecord2(schema)) && (tokenLimits === void 0 || isRecord2(tokenLimits)) && (value.firstTokenTimeout === void 0 || isPositiveNumber(value.firstTokenTimeout)) && (value.rateMultiplier === void 0 || isPositiveNumber(value.rateMultiplier)) && (value.rateUnit === void 0 || typeof value.rateUnit === "string");
666
+ function isApiKey(token) {
667
+ return token.startsWith("ksk_");
686
668
  }
687
- function parseManagementCache(raw) {
688
- let value;
689
- try {
690
- value = JSON.parse(raw);
691
- } catch {
692
- return void 0;
693
- }
694
- if (!isRecord2(value) || value.version !== KIRO_MANAGEMENT_CACHE_VERSION || value.source !== KIRO_MANAGEMENT_CACHE_SOURCE || !isRecord2(value.regions)) {
695
- return void 0;
669
+ async function loginKiroWithApiKey(apiKey, onProgress) {
670
+ if (!apiKey.startsWith("ksk_")) {
671
+ throw new Error("Invalid API key format. Kiro API keys start with 'ksk_'.");
696
672
  }
697
- const regions = {};
698
- for (const [region, rawEntry] of Object.entries(value.regions)) {
699
- if (!isRecord2(rawEntry) || rawEntry.region !== region || !isPositiveNumber(rawEntry.fetchedAt) || !Array.isArray(rawEntry.models) || rawEntry.models.length === 0 || !rawEntry.models.every(isCachedKiroModel)) {
700
- return void 0;
673
+ onProgress?.("Validating API key...");
674
+ const region = "us-east-1";
675
+ const managementUrl = `https://management.${resolveApiRegion(region)}.kiro.dev/`;
676
+ const response = await fetch(managementUrl, {
677
+ method: "POST",
678
+ headers: {
679
+ "Content-Type": "application/x-amz-json-1.0",
680
+ "X-Amz-Target": "AmazonCodeWhispererService.GetProfile",
681
+ ...kiroAuthHeaders(apiKey),
682
+ ...kiroUserAgent("codewhispererruntime", "F,C")
683
+ },
684
+ body: "{}"
685
+ });
686
+ if (!response.ok) {
687
+ let detail = "";
688
+ try {
689
+ detail = await response.text();
690
+ } catch {
691
+ detail = "";
701
692
  }
702
- const modelIds = /* @__PURE__ */ new Set();
703
- for (const model of rawEntry.models) {
704
- if (modelIds.has(model.id))
705
- return void 0;
706
- modelIds.add(model.id);
693
+ if (response.status === 401 || response.status === 403 || /Invalid token/i.test(detail)) {
694
+ throw new Error("API key was rejected by Kiro. Check that the key is valid and not expired.");
707
695
  }
708
- regions[region] = rawEntry;
696
+ throw new Error(`Kiro GetProfile failed: ${response.status} ${response.statusText} ${detail}`.trim());
709
697
  }
698
+ const data = await response.json();
699
+ const profileArn = data.profile?.arn;
710
700
  return {
711
- version: KIRO_MANAGEMENT_CACHE_VERSION,
712
- source: KIRO_MANAGEMENT_CACHE_SOURCE,
713
- regions
701
+ access: apiKey,
702
+ refresh: `${apiKey}|apikey`,
703
+ expires: Date.now() + 365 * 24 * 60 * 60 * 1e3,
704
+ clientId: "",
705
+ clientSecret: "",
706
+ region,
707
+ authMethod: "apikey",
708
+ ...profileArn ? { profileArn } : {}
714
709
  };
715
710
  }
716
- function readManagementCache() {
717
- if (!existsSync3(KIRO_MANAGEMENT_CACHE_PATH))
718
- return void 0;
719
- try {
720
- return parseManagementCache(readFileSync2(KIRO_MANAGEMENT_CACHE_PATH, "utf-8"));
721
- } catch {
722
- return void 0;
711
+ async function refreshKiroToken(credentials) {
712
+ const refreshed = await refreshKiroTokenInternal(credentials);
713
+ if (!process.env.VITEST) {
714
+ try {
715
+ const { updateKiroModelsCache: updateKiroModelsCache2 } = await Promise.resolve().then(() => (init_models(), models_exports));
716
+ const region = resolveApiRegion(refreshed.region);
717
+ updateKiroModelsCache2(refreshed.access, region, refreshed.profileArn).catch((error2) => {
718
+ console.warn(`[kiro-core] Failed to refresh Kiro model catalog in ${region}: ${formatSafeError(error2)}`);
719
+ });
720
+ } catch (error2) {
721
+ console.warn(`[kiro-core] Failed to start Kiro model catalog refresh: ${formatSafeError(error2)}`);
722
+ }
723
723
  }
724
+ return refreshed;
724
725
  }
725
- function writeManagementCache(cache) {
726
- const temporaryPath = `${KIRO_MANAGEMENT_CACHE_PATH}.${process.pid}.${randomUUID()}.tmp`;
726
+ async function refreshKiroTokenInternal(credentials) {
727
+ const { getKiroCliCredentials: getKiroCliCredentials2, getKiroCliCredentialsAllowExpired: getKiroCliCredentialsAllowExpired2, saveKiroCliCredentials: saveKiroCliCredentials2, getKiroCliSocialToken: getKiroCliSocialToken2, getKiroCliSocialTokenAllowExpired: getKiroCliSocialTokenAllowExpired2 } = await Promise.resolve().then(() => (init_kiro_cli(), kiro_cli_exports));
728
+ const credentialAuthMethod = credentials.authMethod ?? (credentials.refresh.split("|").at(-1) === "desktop" ? "desktop" : "idc");
729
+ const getValidCliCredentials = () => {
730
+ if (credentialAuthMethod === "desktop")
731
+ return getKiroCliSocialToken2();
732
+ const cliCreds = getKiroCliCredentials2();
733
+ return cliCreds?.authMethod === "idc" ? cliCreds : void 0;
734
+ };
735
+ const getExpiredCliCredentials = () => {
736
+ if (credentialAuthMethod === "desktop")
737
+ return getKiroCliSocialTokenAllowExpired2();
738
+ const cliCreds = getKiroCliCredentialsAllowExpired2();
739
+ return cliCreds?.authMethod === "idc" ? cliCreds : void 0;
740
+ };
741
+ if (credentials.authMethod === "apikey" || isApiKey(credentials.access))
742
+ return credentials;
743
+ if (credentialAuthMethod === "idc") {
744
+ const ideCreds = getKiroIdeCredentials();
745
+ if (ideCreds)
746
+ return ideCreds;
747
+ }
748
+ const preCheckCreds = getValidCliCredentials();
749
+ if (preCheckCreds)
750
+ return preCheckCreds;
727
751
  try {
728
- writeFileSync(temporaryPath, JSON.stringify(cache, null, 2), "utf-8");
729
- renameSync(temporaryPath, KIRO_MANAGEMENT_CACHE_PATH);
730
- } finally {
731
- rmSync(temporaryPath, { force: true });
752
+ const refreshed = await refreshKiroTokenDirect(credentials);
753
+ saveKiroCliCredentials2(refreshed);
754
+ return refreshed;
755
+ } catch (refreshError) {
756
+ const retryCreds = getValidCliCredentials();
757
+ if (retryCreds)
758
+ return retryCreds;
759
+ const expiredCliCreds = getExpiredCliCredentials();
760
+ if (expiredCliCreds && expiredCliCreds.refresh !== credentials.refresh) {
761
+ try {
762
+ const refreshedFromCli = await refreshKiroTokenDirect(expiredCliCreds);
763
+ saveKiroCliCredentials2(refreshedFromCli);
764
+ return refreshedFromCli;
765
+ } catch {
766
+ }
767
+ }
768
+ const actualExpiry = credentials.expires + EXPIRES_BUFFER_MS;
769
+ if (credentials.access && Date.now() < actualExpiry) {
770
+ return { ...credentials, expires: actualExpiry };
771
+ }
772
+ throw refreshError;
732
773
  }
733
774
  }
734
- function toLocalModelId(kiroModelId) {
735
- return kiroModelId.replace(/(\d)\.(\d)/g, "$1-$2");
736
- }
737
- function humanizeModelId(modelId) {
738
- return modelId.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
739
- }
740
- function applyEffortLadder(config) {
741
- if (!config || config.values.length === 0)
742
- return {};
743
- const efforts = KIRO_EFFORT_ORDER.filter((effort) => config.values.includes(effort));
744
- if (efforts.length === 0)
745
- return {};
746
- return { efforts, ...config.summarizedThinking ? { supportsSummarizedThinking: true } : {} };
747
- }
748
- function hasReasoningFamilyFallback(modelId) {
749
- const normalizedId = modelId.toLowerCase();
750
- return normalizedId === "auto" || REASONING_FAMILY_MARKERS.some((marker) => normalizedId.includes(marker));
751
- }
752
- function hasVerifiedImageInput(kiroModelId) {
753
- return kiroModelId.startsWith("claude-") || VERIFIED_IMAGE_MODEL_IDS.has(kiroModelId.toLowerCase());
754
- }
755
- function applyVerifiedCapabilities(model) {
756
- if (!hasVerifiedImageInput(model.kiroModelId) || model.input.includes("image"))
757
- return model;
758
- return { ...model, input: ["text", "image"] };
759
- }
760
- function validateCatalogMetadata(model) {
761
- const rawSchema = model.additionalModelRequestFieldsSchema;
762
- const schema = rawSchema ?? void 0;
763
- if (schema !== void 0 && !isRecord2(schema)) {
764
- throw new Error(`Kiro management catalog model ${model.modelId} has an invalid request-fields schema`);
765
- }
766
- const tokenLimits = model.tokenLimits;
767
- if (tokenLimits !== void 0 && !isRecord2(tokenLimits)) {
768
- throw new Error(`Kiro management catalog model ${model.modelId} has invalid token limits`);
769
- }
770
- if (tokenLimits && (tokenLimits.maxInputTokens !== void 0 && !isPositiveNumber(tokenLimits.maxInputTokens) || tokenLimits.maxOutputTokens !== void 0 && !isPositiveNumber(tokenLimits.maxOutputTokens))) {
771
- throw new Error(`Kiro management catalog model ${model.modelId} has invalid token limits`);
775
+ async function refreshKiroTokenDirect(credentials) {
776
+ const parts = credentials.refresh.split("|");
777
+ const refreshToken = parts[0] ?? "";
778
+ const authMethod = parts[parts.length - 1] ?? "idc";
779
+ const region = credentials.region || "us-east-1";
780
+ if (authMethod === "apikey")
781
+ return credentials;
782
+ if (authMethod === "desktop") {
783
+ const url = KIRO_DESKTOP_REFRESH_URL.replace("{region}", region);
784
+ const response2 = await fetch(url, {
785
+ method: "POST",
786
+ headers: { "Content-Type": "application/json", "User-Agent": KIRO_DESKTOP_USER_AGENT },
787
+ body: JSON.stringify({ refreshToken })
788
+ });
789
+ if (!response2.ok)
790
+ throw new Error(`Desktop token refresh failed: ${response2.status}`);
791
+ const data2 = await response2.json();
792
+ if (!data2.accessToken)
793
+ throw new Error("Desktop token refresh: missing accessToken");
794
+ return {
795
+ refresh: `${data2.refreshToken || refreshToken}|desktop`,
796
+ access: data2.accessToken,
797
+ expires: Date.now() + data2.expiresIn * 1e3 - EXPIRES_BUFFER_MS,
798
+ clientId: "",
799
+ clientSecret: "",
800
+ region,
801
+ authMethod: "desktop",
802
+ profileArn: data2.profileArn || credentials.profileArn,
803
+ startUrl: credentials.startUrl
804
+ };
772
805
  }
773
- return { schema, tokenLimits };
774
- }
775
- function mapKiroCatalogModels(catalogModels, region, rates) {
776
- if (catalogModels.length === 0) {
777
- throw new Error(`Kiro management catalog returned no models in ${region}`);
778
- }
779
- const seenIds = /* @__PURE__ */ new Set();
780
- return catalogModels.map((catalogModel) => {
781
- const kiroModelId = catalogModel.modelId;
782
- if (!kiroModelId || kiroModelId.trim() !== kiroModelId) {
783
- throw new Error(`Kiro management catalog returned an invalid model ID in ${region}`);
784
- }
785
- const id = toLocalModelId(kiroModelId);
786
- if (seenIds.has(id)) {
787
- throw new Error(`Kiro management catalog contains conflicting model ID ${id} in ${region}`);
788
- }
789
- seenIds.add(id);
790
- const existing = kiroModels.find((model) => model.id === id);
791
- const rate = rates?.get(kiroModelId);
792
- const { schema, tokenLimits } = validateCatalogMetadata(catalogModel);
793
- const effortConfig = getKiroEffortConfig(schema, kiroModelId);
794
- const catalogName = typeof catalogModel.displayName === "string" && catalogModel.displayName.length > 0 ? catalogModel.displayName : void 0;
806
+ if (authMethod === "external-idp") {
807
+ const idpClientId = parts[1] ?? "";
808
+ const tokenEndpoint = parts[2] ?? "";
809
+ if (!tokenEndpoint)
810
+ throw new Error("External IdP token refresh: missing token endpoint");
811
+ const response2 = await fetch(tokenEndpoint, {
812
+ method: "POST",
813
+ headers: {
814
+ "Content-Type": "application/x-www-form-urlencoded",
815
+ Accept: "application/json",
816
+ "User-Agent": "kiro-core"
817
+ },
818
+ body: new URLSearchParams({
819
+ grant_type: "refresh_token",
820
+ client_id: idpClientId,
821
+ refresh_token: refreshToken
822
+ }).toString()
823
+ });
824
+ if (!response2.ok)
825
+ throw new Error(`External IdP token refresh failed: ${response2.status}`);
826
+ const data2 = await response2.json();
827
+ if (!data2.access_token)
828
+ throw new Error("External IdP token refresh: missing access_token");
829
+ const expiresIn = typeof data2.expires_in === "number" ? data2.expires_in : 3600;
795
830
  return {
796
- id,
797
- kiroModelId,
798
- name: catalogName ?? existing?.name ?? humanizeModelId(id),
831
+ refresh: `${data2.refresh_token || refreshToken}|${idpClientId}|${tokenEndpoint}|external-idp`,
832
+ access: data2.access_token,
833
+ expires: Date.now() + expiresIn * 1e3 - EXPIRES_BUFFER_MS,
834
+ clientId: idpClientId,
835
+ clientSecret: "",
799
836
  region,
800
- // Deliberately schema-only: when a schema exists the resolver returns
801
- // `deriveKiroEffort(schema)`, and when it does not the family-marker guess
802
- // decides. The fallback tier feeds the ladders, not this flag.
803
- reasoning: schema !== void 0 && effortConfig !== void 0 || schema === void 0 && hasReasoningFamilyFallback(id),
804
- ...applyEffortLadder(effortConfig),
805
- input: existing ? [...existing.input] : hasVerifiedImageInput(kiroModelId) ? ["text", "image"] : ["text"],
806
- ...id.startsWith("claude-") ? { recoverTextToolCalls: false } : {},
807
- cost: ZERO_COST,
808
- ...rate ? { rateMultiplier: rate.multiplier, ...rate.unit ? { rateUnit: rate.unit } : {} } : {},
809
- contextWindow: tokenLimits?.maxInputTokens ?? DEFAULT_CONTEXT_WINDOW,
810
- maxTokens: tokenLimits?.maxOutputTokens ?? DEFAULT_MAX_TOKENS,
811
- ...existing?.firstTokenTimeout ? { firstTokenTimeout: existing.firstTokenTimeout } : {},
812
- ...schema ? { additionalModelRequestFieldsSchema: schema } : {},
813
- ...tokenLimits ? { tokenLimits } : {}
837
+ authMethod: "external-idp",
838
+ profileArn: credentials.profileArn
814
839
  };
840
+ }
841
+ const clientId = parts[1] ?? "";
842
+ const clientSecret = parts[2] ?? "";
843
+ const ssoEndpoint = `https://oidc.${region}.amazonaws.com`;
844
+ const response = await fetch(`${ssoEndpoint}/token`, {
845
+ method: "POST",
846
+ headers: { "Content-Type": "application/json", ...kiroUserAgent("ssooidc", "E") },
847
+ body: JSON.stringify({ clientId, clientSecret, refreshToken, grantType: "refresh_token" })
815
848
  });
849
+ if (!response.ok)
850
+ throw new Error(`Token refresh failed: ${response.status}`);
851
+ const data = await response.json();
852
+ return {
853
+ refresh: `${data.refreshToken}|${clientId}|${clientSecret}|idc`,
854
+ access: data.accessToken,
855
+ expires: Date.now() + data.expiresIn * 1e3 - EXPIRES_BUFFER_MS,
856
+ clientId,
857
+ clientSecret,
858
+ region,
859
+ authMethod: "idc",
860
+ profileArn: credentials.profileArn,
861
+ startUrl: credentials.startUrl,
862
+ isEnterprise: credentials.isEnterprise
863
+ };
816
864
  }
817
- function refreshKnownModelIds(cache) {
818
- KIRO_MODEL_IDS.clear();
819
- for (const modelId of BOOTSTRAP_KIRO_MODEL_IDS)
820
- KIRO_MODEL_IDS.add(modelId);
821
- if (!cache)
822
- return;
823
- for (const entry of Object.values(cache.regions)) {
824
- for (const model of entry.models)
825
- KIRO_MODEL_IDS.add(model.kiroModelId);
826
- }
865
+ async function resolveKiroCredentials() {
866
+ const { getKiroCliCredentials: getKiroCliCredentials2, getKiroCliCredentialsAllowExpired: getKiroCliCredentialsAllowExpired2 } = await Promise.resolve().then(() => (init_kiro_cli(), kiro_cli_exports));
867
+ const fresh = getKiroCliCredentials2() ?? getKiroIdeCredentials();
868
+ if (fresh)
869
+ return fresh;
870
+ const stale = getKiroCliCredentialsAllowExpired2();
871
+ if (!stale)
872
+ return void 0;
873
+ return refreshKiroToken(stale);
827
874
  }
828
- function loadCachedModelIds() {
829
- refreshKnownModelIds(readManagementCache());
875
+ var KIRO_DESKTOP_REFRESH_URL, EXPIRES_BUFFER_MS, KIRO_DESKTOP_USER_AGENT;
876
+ var init_oauth = __esm({
877
+ "../kiro-core/dist/oauth.js"() {
878
+ "use strict";
879
+ init_debug();
880
+ init_endpoints();
881
+ init_kiro_ide();
882
+ KIRO_DESKTOP_REFRESH_URL = "https://prod.{region}.auth.desktop.kiro.dev/refreshToken";
883
+ EXPIRES_BUFFER_MS = 5 * 60 * 1e3;
884
+ KIRO_DESKTOP_USER_AGENT = "Kiro-Desktop/0.2.13 (darwin; arm64)";
885
+ }
886
+ });
887
+
888
+ // ../kiro-core/dist/token-type.js
889
+ function isExternalIdpAccessToken(accessToken) {
890
+ if (typeof accessToken !== "string")
891
+ return false;
892
+ const segments = accessToken.split(".");
893
+ if (segments.length !== 3)
894
+ return false;
895
+ const payloadSegment = segments[1];
896
+ if (!payloadSegment)
897
+ return false;
898
+ try {
899
+ const payload = JSON.parse(Buffer.from(payloadSegment.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf-8"));
900
+ return payload.aud === EXTERNAL_IDP_AUDIENCE;
901
+ } catch {
902
+ return false;
903
+ }
830
904
  }
831
- function getCachedModels(region) {
832
- const cache = readManagementCache();
833
- refreshKnownModelIds(cache);
834
- const models = cache?.regions[region]?.models ?? kiroModels;
835
- let changed = false;
836
- const corrected = models.map((model) => {
837
- const result = applyVerifiedCapabilities(model);
838
- if (result !== model)
839
- changed = true;
840
- return result;
841
- });
842
- return changed ? corrected : models;
905
+ function kiroTokenTypeHeaders(accessToken) {
906
+ return isExternalIdpAccessToken(accessToken) ? { tokentype: "EXTERNAL_IDP" } : {};
843
907
  }
844
- function isCacheStale(region) {
845
- const entry = readManagementCache()?.regions[region];
846
- return !entry || Date.now() - entry.fetchedAt > CACHE_MAX_AGE_MS;
908
+ var EXTERNAL_IDP_AUDIENCE;
909
+ var init_token_type = __esm({
910
+ "../kiro-core/dist/token-type.js"() {
911
+ "use strict";
912
+ EXTERNAL_IDP_AUDIENCE = "api://kiro";
913
+ }
914
+ });
915
+
916
+ // ../kiro-core/dist/management.js
917
+ import { createHash } from "node:crypto";
918
+ function candidateManagementRegions(primary) {
919
+ const seen = /* @__PURE__ */ new Set([primary]);
920
+ const candidates = [primary];
921
+ for (const region of CANONICAL_MANAGEMENT_REGIONS) {
922
+ if (!seen.has(region)) {
923
+ seen.add(region);
924
+ candidates.push(region);
925
+ }
926
+ }
927
+ return candidates;
847
928
  }
848
- async function updateKiroModelsCache(accessToken, region, profileArn) {
849
- const response = await fetchKiroModelCatalog({ accessToken, region }, profileArn);
850
- const models = mapKiroCatalogModels(response.models, region, getKiroCliModelRates());
851
- const cache = readManagementCache() ?? {
852
- version: KIRO_MANAGEMENT_CACHE_VERSION,
853
- source: KIRO_MANAGEMENT_CACHE_SOURCE,
854
- regions: {}
929
+ async function requestManagement(auth, operation, path, method, body) {
930
+ const url = new URL(path, getKiroEndpoints(auth.region).management);
931
+ const request = {
932
+ method,
933
+ headers: {
934
+ Accept: "application/json",
935
+ ...kiroAuthHeaders(auth.accessToken),
936
+ ...kiroTokenTypeHeaders(auth.accessToken)
937
+ }
855
938
  };
856
- cache.regions[region] = { region, fetchedAt: Date.now(), models };
857
- writeManagementCache(cache);
858
- refreshKnownModelIds(cache);
939
+ if (method === "GET") {
940
+ for (const [name, value] of Object.entries(body)) {
941
+ if (value !== void 0)
942
+ url.searchParams.set(name, String(value));
943
+ }
944
+ } else {
945
+ request.headers = { ...request.headers, "Content-Type": "application/json" };
946
+ request.body = JSON.stringify(body);
947
+ }
948
+ let response;
949
+ try {
950
+ response = await fetch(url.toString(), request);
951
+ } catch (error2) {
952
+ throw new Error(`Kiro management ${operation} request failed in ${auth.region}`, { cause: error2 });
953
+ }
954
+ return parseManagementResponse(response, operation, auth.region);
859
955
  }
860
- function resolveKiroModel(modelId, exactKiroModelId) {
861
- if (exactKiroModelId)
862
- return exactKiroModelId;
863
- const cachedModel = Object.values(readManagementCache()?.regions ?? {}).flatMap((entry) => entry.models).find((model) => model.id === modelId);
864
- if (cachedModel) {
865
- KIRO_MODEL_IDS.add(cachedModel.kiroModelId);
866
- return cachedModel.kiroModelId;
956
+ function profileCacheKey(auth) {
957
+ const tokenHash = createHash("sha256").update(auth.accessToken).digest("base64url");
958
+ return `${auth.region}:${tokenHash}`;
959
+ }
960
+ async function getApiKeyProfileArn(accessToken) {
961
+ const operation = "GetProfile";
962
+ let response;
963
+ try {
964
+ response = await fetch(getKiroEndpoints(API_KEY_REGION).management, {
965
+ method: "POST",
966
+ headers: {
967
+ "Content-Type": "application/x-amz-json-1.0",
968
+ "X-Amz-Target": GET_PROFILE_TARGET,
969
+ ...kiroAuthHeaders(accessToken),
970
+ ...kiroUserAgent("codewhispererruntime", "F,C")
971
+ },
972
+ body: "{}"
973
+ });
974
+ } catch (error2) {
975
+ throw new Error(`Kiro management ${operation} request failed in ${API_KEY_REGION}`, { cause: error2 });
867
976
  }
868
- const bootstrapModel = kiroModels.find((model) => model.id === modelId);
869
- if (bootstrapModel)
870
- return bootstrapModel.kiroModelId;
871
- const normalizedId = modelId.replace(/(\d)-(\d)/g, "$1.$2");
872
- loadCachedModelIds();
873
- if (!KIRO_MODEL_IDS.has(normalizedId)) {
874
- throw new Error(`Unknown Kiro model ID: ${modelId}`);
977
+ const parsed = await parseManagementResponse(response, operation, API_KEY_REGION);
978
+ const arn = parsed.profile?.arn;
979
+ if (!arn) {
980
+ throw new Error(`Kiro management ${operation} returned no profile in ${API_KEY_REGION}`);
875
981
  }
876
- return normalizedId;
877
- }
878
- var KIRO_MANAGEMENT_CACHE_VERSION, KIRO_MANAGEMENT_CACHE_SOURCE, KIRO_MANAGEMENT_CACHE_PATH, CACHE_MAX_AGE_MS, DEFAULT_CONTEXT_WINDOW, DEFAULT_MAX_TOKENS, ZERO_COST, REASONING_FAMILY_MARKERS, VERIFIED_IMAGE_MODEL_IDS, bootstrapKiroModels, kiroModels, BOOTSTRAP_KIRO_MODEL_IDS, KIRO_MODEL_IDS;
879
- var init_models = __esm({
880
- "../kiro-core/dist/models.js"() {
881
- "use strict";
882
- init_effort();
883
- init_kiro_cli();
884
- init_management();
885
- init_types();
886
- init_endpoints();
887
- KIRO_MANAGEMENT_CACHE_VERSION = 1;
888
- KIRO_MANAGEMENT_CACHE_SOURCE = "ns-kiro-provider-management";
889
- KIRO_MANAGEMENT_CACHE_PATH = join4(homedir4(), ".ns-kiro-provider-models-cache.json");
890
- CACHE_MAX_AGE_MS = 36e5;
891
- DEFAULT_CONTEXT_WINDOW = 2e5;
892
- DEFAULT_MAX_TOKENS = 8192;
893
- ZERO_COST = Object.freeze({ input: 0, output: 0, cacheRead: 0, cacheWrite: 0 });
894
- REASONING_FAMILY_MARKERS = ["opus", "sonnet", "fable", "coder", "deepseek", "gpt", "glm", "qwen"];
895
- VERIFIED_IMAGE_MODEL_IDS = /* @__PURE__ */ new Set(["gpt-5.6-luna"]);
896
- bootstrapKiroModels = [
897
- {
898
- id: "claude-opus-5",
899
- kiroModelId: "claude-opus-5",
900
- name: "Claude Opus 5",
901
- reasoning: true,
902
- input: ["text", "image"],
903
- cost: ZERO_COST,
904
- contextWindow: 1e6,
905
- maxTokens: 128e3,
906
- firstTokenTimeout: 18e4
907
- },
908
- {
909
- id: "claude-opus-4-8",
910
- kiroModelId: "claude-opus-4.8",
911
- name: "Claude Opus 4.8",
912
- reasoning: true,
913
- input: ["text", "image"],
914
- cost: ZERO_COST,
915
- contextWindow: 1e6,
916
- maxTokens: 128e3,
917
- firstTokenTimeout: 18e4
918
- },
919
- {
920
- id: "claude-opus-4-7",
921
- kiroModelId: "claude-opus-4.7",
922
- name: "Claude Opus 4.7",
923
- reasoning: true,
924
- input: ["text", "image"],
925
- cost: ZERO_COST,
926
- contextWindow: 1e6,
927
- maxTokens: 128e3,
928
- firstTokenTimeout: 18e4
929
- },
930
- {
931
- id: "claude-opus-4-6",
932
- kiroModelId: "claude-opus-4.6",
933
- name: "Claude Opus 4.6",
934
- reasoning: true,
935
- input: ["text", "image"],
936
- cost: ZERO_COST,
937
- contextWindow: 1e6,
938
- maxTokens: 32768
939
- },
940
- {
941
- id: "claude-opus-4-5",
942
- kiroModelId: "claude-opus-4.5",
943
- name: "Claude Opus 4.5",
944
- reasoning: true,
945
- input: ["text", "image"],
946
- cost: ZERO_COST,
947
- contextWindow: 2e5,
948
- maxTokens: 64e3
949
- },
950
- {
951
- id: "claude-sonnet-5",
952
- kiroModelId: "claude-sonnet-5",
953
- name: "Claude Sonnet 5",
954
- reasoning: true,
955
- input: ["text", "image"],
956
- cost: ZERO_COST,
957
- contextWindow: 1e6,
958
- maxTokens: 65536
959
- },
960
- {
961
- id: "claude-sonnet-4-6",
962
- kiroModelId: "claude-sonnet-4.6",
963
- name: "Claude Sonnet 4.6",
964
- reasoning: true,
965
- input: ["text", "image"],
966
- cost: ZERO_COST,
967
- contextWindow: 1e6,
968
- maxTokens: 65536
969
- },
970
- {
971
- id: "claude-sonnet-4-5",
972
- kiroModelId: "claude-sonnet-4.5",
973
- name: "Claude Sonnet 4.5",
974
- reasoning: true,
975
- input: ["text", "image"],
976
- cost: ZERO_COST,
977
- contextWindow: 2e5,
978
- maxTokens: 65536
979
- },
980
- {
981
- id: "claude-sonnet-4",
982
- kiroModelId: "claude-sonnet-4",
983
- name: "Claude Sonnet 4",
984
- reasoning: true,
985
- input: ["text", "image"],
986
- cost: ZERO_COST,
987
- contextWindow: 2e5,
988
- maxTokens: 65536
989
- },
990
- {
991
- id: "claude-haiku-4-5",
992
- kiroModelId: "claude-haiku-4.5",
993
- name: "Claude Haiku 4.5",
994
- reasoning: false,
995
- input: ["text", "image"],
996
- cost: ZERO_COST,
997
- contextWindow: 2e5,
998
- maxTokens: 65536
999
- },
1000
- {
1001
- id: "claude-fable-5",
1002
- kiroModelId: "claude-fable-5",
1003
- name: "Claude Fable 5",
1004
- reasoning: true,
1005
- input: ["text", "image"],
1006
- cost: ZERO_COST,
1007
- contextWindow: 1e6,
1008
- maxTokens: 65536
1009
- },
1010
- {
1011
- id: "gpt-5-6-luna",
1012
- kiroModelId: "gpt-5.6-luna",
1013
- name: "GPT-5.6 Luna",
1014
- reasoning: true,
1015
- input: ["text", "image"],
1016
- cost: ZERO_COST,
1017
- contextWindow: 272e3,
1018
- maxTokens: 128e3
1019
- },
1020
- {
1021
- id: "gpt-5-6-sol",
1022
- kiroModelId: "gpt-5.6-sol",
1023
- name: "GPT-5.6 Sol",
1024
- reasoning: true,
1025
- input: ["text"],
1026
- cost: ZERO_COST,
1027
- contextWindow: 272e3,
1028
- maxTokens: 128e3
1029
- },
1030
- {
1031
- id: "gpt-5-6-terra",
1032
- kiroModelId: "gpt-5.6-terra",
1033
- name: "GPT-5.6 Terra",
1034
- reasoning: true,
1035
- input: ["text"],
1036
- cost: ZERO_COST,
1037
- contextWindow: 272e3,
1038
- maxTokens: 128e3
1039
- },
1040
- {
1041
- id: "deepseek-3-2",
1042
- kiroModelId: "deepseek-3.2",
1043
- name: "DeepSeek 3.2",
1044
- reasoning: true,
1045
- input: ["text"],
1046
- cost: ZERO_COST,
1047
- contextWindow: 164e3,
1048
- maxTokens: 8192
1049
- },
1050
- {
1051
- id: "minimax-m2-5",
1052
- kiroModelId: "minimax-m2.5",
1053
- name: "MiniMax M2.5",
1054
- reasoning: false,
1055
- input: ["text"],
1056
- cost: ZERO_COST,
1057
- contextWindow: 196e3,
1058
- maxTokens: 8192
1059
- },
1060
- {
1061
- id: "minimax-m2-1",
1062
- kiroModelId: "minimax-m2.1",
1063
- name: "MiniMax M2.1",
1064
- reasoning: false,
1065
- input: ["text"],
1066
- cost: ZERO_COST,
1067
- contextWindow: 196e3,
1068
- maxTokens: 8192
1069
- },
1070
- {
1071
- id: "glm-5",
1072
- kiroModelId: "glm-5",
1073
- name: "GLM 5",
1074
- reasoning: true,
1075
- input: ["text"],
1076
- cost: ZERO_COST,
1077
- contextWindow: 2e5,
1078
- maxTokens: 8192
1079
- },
1080
- {
1081
- id: "qwen3-coder-next",
1082
- kiroModelId: "qwen3-coder-next",
1083
- name: "Qwen3 Coder Next",
1084
- reasoning: true,
1085
- input: ["text"],
1086
- cost: ZERO_COST,
1087
- contextWindow: 256e3,
1088
- maxTokens: 8192
1089
- },
1090
- {
1091
- id: "auto",
1092
- kiroModelId: "auto",
1093
- name: "Auto",
1094
- reasoning: true,
1095
- input: ["text", "image"],
1096
- cost: ZERO_COST,
1097
- contextWindow: 1e6,
1098
- maxTokens: 65536
1099
- }
1100
- ];
1101
- kiroModels = bootstrapKiroModels.map((model) => ({
1102
- ...model,
1103
- ...applyEffortLadder(model.reasoning ? getKiroEffortConfig(model.additionalModelRequestFieldsSchema, model.kiroModelId) : void 0),
1104
- ...model.id.startsWith("claude-") ? { recoverTextToolCalls: false } : {}
1105
- }));
1106
- BOOTSTRAP_KIRO_MODEL_IDS = kiroModels.map((model) => model.kiroModelId);
1107
- KIRO_MODEL_IDS = new Set(BOOTSTRAP_KIRO_MODEL_IDS);
1108
- }
1109
- });
1110
-
1111
- // ../kiro-core/dist/oauth.js
1112
- function kiroUserAgent(service, sdkVersion) {
1113
- return {
1114
- "User-Agent": `aws-sdk-js/3.714.0 os/macos/24.3.0 lang/js md/nodejs/22.14.0 api/${service}/3.714.0 exec-env/kiro-cli/2.7.0 m/E`,
1115
- "amz-sdk-invocation-id": "00000000-0000-0000-0000-000000000000",
1116
- "amz-sdk-request": `attempt=1; max=${sdkVersion}`
1117
- };
1118
- }
1119
- function kiroAuthHeaders(token) {
1120
- const headers = { Authorization: `Bearer ${token}` };
1121
- if (isApiKey(token))
1122
- headers.tokentype = "API_KEY";
1123
- return headers;
1124
- }
1125
- function isApiKey(token) {
1126
- return token.startsWith("ksk_");
1127
- }
1128
- async function loginKiroWithApiKey(apiKey, onProgress) {
1129
- if (!apiKey.startsWith("ksk_")) {
1130
- throw new Error("Invalid API key format. Kiro API keys start with 'ksk_'.");
1131
- }
1132
- onProgress?.("Validating API key...");
1133
- const region = "us-east-1";
1134
- const managementUrl = `https://management.${resolveApiRegion(region)}.kiro.dev/`;
1135
- const response = await fetch(managementUrl, {
1136
- method: "POST",
1137
- headers: {
1138
- "Content-Type": "application/x-amz-json-1.0",
1139
- "X-Amz-Target": "AmazonCodeWhispererService.GetProfile",
1140
- ...kiroAuthHeaders(apiKey),
1141
- ...kiroUserAgent("codewhispererruntime", "F,C")
1142
- },
1143
- body: "{}"
1144
- });
1145
- if (!response.ok) {
1146
- let detail = "";
1147
- try {
1148
- detail = await response.text();
1149
- } catch {
1150
- detail = "";
1151
- }
1152
- if (response.status === 401 || response.status === 403 || /Invalid token/i.test(detail)) {
1153
- throw new Error("API key was rejected by Kiro. Check that the key is valid and not expired.");
1154
- }
1155
- throw new Error(`Kiro GetProfile failed: ${response.status} ${response.statusText} ${detail}`.trim());
1156
- }
1157
- const data = await response.json();
1158
- const profileArn = data.profile?.arn;
1159
- return {
1160
- access: apiKey,
1161
- refresh: `${apiKey}|apikey`,
1162
- expires: Date.now() + 365 * 24 * 60 * 60 * 1e3,
1163
- clientId: "",
1164
- clientSecret: "",
1165
- region,
1166
- authMethod: "apikey",
1167
- ...profileArn ? { profileArn } : {}
1168
- };
1169
- }
1170
- async function refreshKiroToken(credentials) {
1171
- const refreshed = await refreshKiroTokenInternal(credentials);
1172
- if (!process.env.VITEST) {
1173
- try {
1174
- const { updateKiroModelsCache: updateKiroModelsCache2 } = await Promise.resolve().then(() => (init_models(), models_exports));
1175
- const region = resolveApiRegion(refreshed.region);
1176
- updateKiroModelsCache2(refreshed.access, region, refreshed.profileArn).catch((error2) => {
1177
- console.warn(`[kiro-core] Failed to refresh Kiro model catalog in ${region}: ${formatSafeError(error2)}`);
1178
- });
1179
- } catch (error2) {
1180
- console.warn(`[kiro-core] Failed to start Kiro model catalog refresh: ${formatSafeError(error2)}`);
1181
- }
1182
- }
1183
- return refreshed;
1184
- }
1185
- async function refreshKiroTokenInternal(credentials) {
1186
- const { getKiroCliCredentials: getKiroCliCredentials2, getKiroCliCredentialsAllowExpired: getKiroCliCredentialsAllowExpired2, saveKiroCliCredentials: saveKiroCliCredentials2, getKiroCliSocialToken: getKiroCliSocialToken2, getKiroCliSocialTokenAllowExpired: getKiroCliSocialTokenAllowExpired2 } = await Promise.resolve().then(() => (init_kiro_cli(), kiro_cli_exports));
1187
- const credentialAuthMethod = credentials.authMethod ?? (credentials.refresh.split("|").at(-1) === "desktop" ? "desktop" : "idc");
1188
- const getValidCliCredentials = () => {
1189
- if (credentialAuthMethod === "desktop")
1190
- return getKiroCliSocialToken2();
1191
- const cliCreds = getKiroCliCredentials2();
1192
- return cliCreds?.authMethod === "idc" ? cliCreds : void 0;
1193
- };
1194
- const getExpiredCliCredentials = () => {
1195
- if (credentialAuthMethod === "desktop")
1196
- return getKiroCliSocialTokenAllowExpired2();
1197
- const cliCreds = getKiroCliCredentialsAllowExpired2();
1198
- return cliCreds?.authMethod === "idc" ? cliCreds : void 0;
1199
- };
1200
- if (credentials.authMethod === "apikey" || isApiKey(credentials.access))
1201
- return credentials;
1202
- if (credentialAuthMethod === "idc") {
1203
- const ideCreds = getKiroIdeCredentials();
1204
- if (ideCreds)
1205
- return ideCreds;
1206
- }
1207
- const preCheckCreds = getValidCliCredentials();
1208
- if (preCheckCreds)
1209
- return preCheckCreds;
1210
- try {
1211
- const refreshed = await refreshKiroTokenDirect(credentials);
1212
- saveKiroCliCredentials2(refreshed);
1213
- return refreshed;
1214
- } catch (refreshError) {
1215
- const retryCreds = getValidCliCredentials();
1216
- if (retryCreds)
1217
- return retryCreds;
1218
- const expiredCliCreds = getExpiredCliCredentials();
1219
- if (expiredCliCreds && expiredCliCreds.refresh !== credentials.refresh) {
1220
- try {
1221
- const refreshedFromCli = await refreshKiroTokenDirect(expiredCliCreds);
1222
- saveKiroCliCredentials2(refreshedFromCli);
1223
- return refreshedFromCli;
1224
- } catch {
1225
- }
1226
- }
1227
- const actualExpiry = credentials.expires + EXPIRES_BUFFER_MS;
1228
- if (credentials.access && Date.now() < actualExpiry) {
1229
- return { ...credentials, expires: actualExpiry };
1230
- }
1231
- throw refreshError;
1232
- }
1233
- }
1234
- async function refreshKiroTokenDirect(credentials) {
1235
- const parts = credentials.refresh.split("|");
1236
- const refreshToken = parts[0] ?? "";
1237
- const authMethod = parts[parts.length - 1] ?? "idc";
1238
- const region = credentials.region || "us-east-1";
1239
- if (authMethod === "apikey")
1240
- return credentials;
1241
- if (authMethod === "desktop") {
1242
- const url = KIRO_DESKTOP_REFRESH_URL.replace("{region}", region);
1243
- const response2 = await fetch(url, {
1244
- method: "POST",
1245
- headers: { "Content-Type": "application/json", "User-Agent": KIRO_DESKTOP_USER_AGENT },
1246
- body: JSON.stringify({ refreshToken })
1247
- });
1248
- if (!response2.ok)
1249
- throw new Error(`Desktop token refresh failed: ${response2.status}`);
1250
- const data2 = await response2.json();
1251
- if (!data2.accessToken)
1252
- throw new Error("Desktop token refresh: missing accessToken");
1253
- return {
1254
- refresh: `${data2.refreshToken || refreshToken}|desktop`,
1255
- access: data2.accessToken,
1256
- expires: Date.now() + data2.expiresIn * 1e3 - EXPIRES_BUFFER_MS,
1257
- clientId: "",
1258
- clientSecret: "",
1259
- region,
1260
- authMethod: "desktop",
1261
- profileArn: data2.profileArn || credentials.profileArn,
1262
- startUrl: credentials.startUrl
1263
- };
1264
- }
1265
- if (authMethod === "external-idp") {
1266
- const idpClientId = parts[1] ?? "";
1267
- const tokenEndpoint = parts[2] ?? "";
1268
- if (!tokenEndpoint)
1269
- throw new Error("External IdP token refresh: missing token endpoint");
1270
- const response2 = await fetch(tokenEndpoint, {
1271
- method: "POST",
1272
- headers: {
1273
- "Content-Type": "application/x-www-form-urlencoded",
1274
- Accept: "application/json",
1275
- "User-Agent": "kiro-core"
1276
- },
1277
- body: new URLSearchParams({
1278
- grant_type: "refresh_token",
1279
- client_id: idpClientId,
1280
- refresh_token: refreshToken
1281
- }).toString()
1282
- });
1283
- if (!response2.ok)
1284
- throw new Error(`External IdP token refresh failed: ${response2.status}`);
1285
- const data2 = await response2.json();
1286
- if (!data2.access_token)
1287
- throw new Error("External IdP token refresh: missing access_token");
1288
- const expiresIn = typeof data2.expires_in === "number" ? data2.expires_in : 3600;
1289
- return {
1290
- refresh: `${data2.refresh_token || refreshToken}|${idpClientId}|${tokenEndpoint}|external-idp`,
1291
- access: data2.access_token,
1292
- expires: Date.now() + expiresIn * 1e3 - EXPIRES_BUFFER_MS,
1293
- clientId: idpClientId,
1294
- clientSecret: "",
1295
- region,
1296
- authMethod: "external-idp",
1297
- profileArn: credentials.profileArn
1298
- };
1299
- }
1300
- const clientId = parts[1] ?? "";
1301
- const clientSecret = parts[2] ?? "";
1302
- const ssoEndpoint = `https://oidc.${region}.amazonaws.com`;
1303
- const response = await fetch(`${ssoEndpoint}/token`, {
1304
- method: "POST",
1305
- headers: { "Content-Type": "application/json", ...kiroUserAgent("ssooidc", "E") },
1306
- body: JSON.stringify({ clientId, clientSecret, refreshToken, grantType: "refresh_token" })
1307
- });
1308
- if (!response.ok)
1309
- throw new Error(`Token refresh failed: ${response.status}`);
1310
- const data = await response.json();
1311
- return {
1312
- refresh: `${data.refreshToken}|${clientId}|${clientSecret}|idc`,
1313
- access: data.accessToken,
1314
- expires: Date.now() + data.expiresIn * 1e3 - EXPIRES_BUFFER_MS,
1315
- clientId,
1316
- clientSecret,
1317
- region,
1318
- authMethod: "idc",
1319
- profileArn: credentials.profileArn,
1320
- startUrl: credentials.startUrl,
1321
- isEnterprise: credentials.isEnterprise
1322
- };
1323
- }
1324
- async function resolveKiroCredentials() {
1325
- const { getKiroCliCredentials: getKiroCliCredentials2, getKiroCliCredentialsAllowExpired: getKiroCliCredentialsAllowExpired2 } = await Promise.resolve().then(() => (init_kiro_cli(), kiro_cli_exports));
1326
- const fresh = getKiroCliCredentials2() ?? getKiroIdeCredentials();
1327
- if (fresh)
1328
- return fresh;
1329
- const stale = getKiroCliCredentialsAllowExpired2();
1330
- if (!stale)
1331
- return void 0;
1332
- return refreshKiroToken(stale);
1333
- }
1334
- var KIRO_DESKTOP_REFRESH_URL, EXPIRES_BUFFER_MS, KIRO_DESKTOP_USER_AGENT;
1335
- var init_oauth = __esm({
1336
- "../kiro-core/dist/oauth.js"() {
1337
- "use strict";
1338
- init_debug();
1339
- init_endpoints();
1340
- init_kiro_ide();
1341
- KIRO_DESKTOP_REFRESH_URL = "https://prod.{region}.auth.desktop.kiro.dev/refreshToken";
1342
- EXPIRES_BUFFER_MS = 5 * 60 * 1e3;
1343
- KIRO_DESKTOP_USER_AGENT = "Kiro-Desktop/0.2.13 (darwin; arm64)";
1344
- }
1345
- });
1346
-
1347
- // ../kiro-core/dist/token-type.js
1348
- function isExternalIdpAccessToken(accessToken) {
1349
- if (typeof accessToken !== "string")
1350
- return false;
1351
- const segments = accessToken.split(".");
1352
- if (segments.length !== 3)
1353
- return false;
1354
- const payloadSegment = segments[1];
1355
- if (!payloadSegment)
1356
- return false;
1357
- try {
1358
- const payload = JSON.parse(Buffer.from(payloadSegment.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf-8"));
1359
- return payload.aud === EXTERNAL_IDP_AUDIENCE;
1360
- } catch {
1361
- return false;
1362
- }
1363
- }
1364
- function kiroTokenTypeHeaders(accessToken) {
1365
- return isExternalIdpAccessToken(accessToken) ? { tokentype: "EXTERNAL_IDP" } : {};
1366
- }
1367
- var EXTERNAL_IDP_AUDIENCE;
1368
- var init_token_type = __esm({
1369
- "../kiro-core/dist/token-type.js"() {
1370
- "use strict";
1371
- EXTERNAL_IDP_AUDIENCE = "api://kiro";
1372
- }
1373
- });
1374
-
1375
- // ../kiro-core/dist/management.js
1376
- import { createHash as createHash2 } from "node:crypto";
1377
- function candidateManagementRegions(primary) {
1378
- const seen = /* @__PURE__ */ new Set([primary]);
1379
- const candidates = [primary];
1380
- for (const region of CANONICAL_MANAGEMENT_REGIONS) {
1381
- if (!seen.has(region)) {
1382
- seen.add(region);
1383
- candidates.push(region);
1384
- }
1385
- }
1386
- return candidates;
1387
- }
1388
- async function requestManagement(auth, operation, path, method, body) {
1389
- const url = new URL(path, getKiroEndpoints(auth.region).management);
1390
- const request = {
1391
- method,
1392
- headers: {
1393
- Accept: "application/json",
1394
- ...kiroAuthHeaders(auth.accessToken),
1395
- ...kiroTokenTypeHeaders(auth.accessToken)
1396
- }
1397
- };
1398
- if (method === "GET") {
1399
- for (const [name, value] of Object.entries(body)) {
1400
- if (value !== void 0)
1401
- url.searchParams.set(name, String(value));
1402
- }
1403
- } else {
1404
- request.headers = { ...request.headers, "Content-Type": "application/json" };
1405
- request.body = JSON.stringify(body);
1406
- }
1407
- let response;
1408
- try {
1409
- response = await fetch(url.toString(), request);
1410
- } catch (error2) {
1411
- throw new Error(`Kiro management ${operation} request failed in ${auth.region}`, { cause: error2 });
1412
- }
1413
- return parseManagementResponse(response, operation, auth.region);
1414
- }
1415
- function profileCacheKey(auth) {
1416
- const tokenHash = createHash2("sha256").update(auth.accessToken).digest("base64url");
1417
- return `${auth.region}:${tokenHash}`;
1418
- }
1419
- async function getApiKeyProfileArn(accessToken) {
1420
- const operation = "GetProfile";
1421
- let response;
1422
- try {
1423
- response = await fetch(getKiroEndpoints(API_KEY_REGION).management, {
1424
- method: "POST",
1425
- headers: {
1426
- "Content-Type": "application/x-amz-json-1.0",
1427
- "X-Amz-Target": GET_PROFILE_TARGET,
1428
- ...kiroAuthHeaders(accessToken),
1429
- ...kiroUserAgent("codewhispererruntime", "F,C")
1430
- },
1431
- body: "{}"
1432
- });
1433
- } catch (error2) {
1434
- throw new Error(`Kiro management ${operation} request failed in ${API_KEY_REGION}`, { cause: error2 });
1435
- }
1436
- const parsed = await parseManagementResponse(response, operation, API_KEY_REGION);
1437
- const arn = parsed.profile?.arn;
1438
- if (!arn) {
1439
- throw new Error(`Kiro management ${operation} returned no profile in ${API_KEY_REGION}`);
1440
- }
1441
- return arn;
982
+ return arn;
1442
983
  }
1443
984
  async function parseManagementResponse(response, operation, region) {
1444
985
  if (!response.ok) {
@@ -1517,75 +1058,544 @@ async function resolveKiroProfileArn(auth, providedArn) {
1517
1058
  pendingProfileRequests.delete(key);
1518
1059
  }
1519
1060
  }
1520
- async function listAvailableModels(auth, profileArn) {
1521
- const response = await requestManagement(auth, "ListAvailableModels", LIST_MODELS_PATH, "GET", {
1522
- origin: "KIRO_CLI",
1523
- profileArn
1524
- });
1525
- if (!Array.isArray(response.models) || response.models.length === 0) {
1526
- throw new Error(`Kiro management ListAvailableModels returned no models in ${auth.region}`);
1061
+ async function listAvailableModels(auth, profileArn) {
1062
+ const response = await requestManagement(auth, "ListAvailableModels", LIST_MODELS_PATH, "GET", {
1063
+ origin: "KIRO_CLI",
1064
+ profileArn
1065
+ });
1066
+ if (!Array.isArray(response.models) || response.models.length === 0) {
1067
+ throw new Error(`Kiro management ListAvailableModels returned no models in ${auth.region}`);
1068
+ }
1069
+ if (response.models.some((model) => !model || typeof model.modelId !== "string" || !model.modelId)) {
1070
+ throw new Error(`Kiro management ListAvailableModels returned an invalid catalog in ${auth.region}`);
1071
+ }
1072
+ return response;
1073
+ }
1074
+ async function fetchKiroModelCatalog(auth, providedProfileArn) {
1075
+ const profileArn = await resolveKiroProfileArn(auth, providedProfileArn);
1076
+ const region = isApiKey(auth.accessToken) ? API_KEY_REGION : profileRegionCache.get(profileCacheKey(auth)) ?? auth.region;
1077
+ if (region !== auth.region) {
1078
+ return listAvailableModels({ ...auth, region }, profileArn);
1079
+ }
1080
+ return listAvailableModels(auth, profileArn);
1081
+ }
1082
+ async function getUsageLimits(auth, request) {
1083
+ const operation = "GetUsageLimits";
1084
+ const url = new URL("Get-Usage-Limits", getKiroEndpoints(auth.region).management);
1085
+ for (const [name, value] of Object.entries(request)) {
1086
+ if (value !== void 0)
1087
+ url.searchParams.set(name, String(value));
1088
+ }
1089
+ let response;
1090
+ try {
1091
+ response = await fetch(url.toString(), {
1092
+ method: "GET",
1093
+ headers: {
1094
+ Accept: "application/json",
1095
+ ...kiroAuthHeaders(auth.accessToken),
1096
+ ...kiroTokenTypeHeaders(auth.accessToken),
1097
+ "User-Agent": "ns-kiro-provider"
1098
+ }
1099
+ });
1100
+ } catch (error2) {
1101
+ throw new Error(`Kiro management ${operation} request failed in ${auth.region}`, { cause: error2 });
1102
+ }
1103
+ return parseManagementResponse(response, operation, auth.region);
1104
+ }
1105
+ var LIST_PROFILES_PATH, LIST_MODELS_PATH, GET_PROFILE_TARGET, API_KEY_REGION, profileArnCache, pendingProfileRequests, profileRegionCache, CANONICAL_MANAGEMENT_REGIONS, ENV_PROFILE_ARN, KiroManagementHttpError;
1106
+ var init_management = __esm({
1107
+ "../kiro-core/dist/management.js"() {
1108
+ "use strict";
1109
+ init_debug();
1110
+ init_endpoints();
1111
+ init_oauth();
1112
+ init_token_type();
1113
+ LIST_PROFILES_PATH = "List-Available-Profiles";
1114
+ LIST_MODELS_PATH = "List-Available-Models";
1115
+ GET_PROFILE_TARGET = "AmazonCodeWhispererService.GetProfile";
1116
+ API_KEY_REGION = "us-east-1";
1117
+ profileArnCache = /* @__PURE__ */ new Map();
1118
+ pendingProfileRequests = /* @__PURE__ */ new Map();
1119
+ profileRegionCache = /* @__PURE__ */ new Map();
1120
+ CANONICAL_MANAGEMENT_REGIONS = ["us-east-1", "eu-central-1"];
1121
+ ENV_PROFILE_ARN = "KIRO_PROFILE_ARN";
1122
+ KiroManagementHttpError = class extends Error {
1123
+ status;
1124
+ constructor(message, status) {
1125
+ super(message);
1126
+ this.status = status;
1127
+ this.name = "KiroManagementHttpError";
1128
+ }
1129
+ };
1130
+ }
1131
+ });
1132
+
1133
+ // ../kiro-core/dist/models.js
1134
+ var models_exports = {};
1135
+ __export(models_exports, {
1136
+ KIRO_MANAGEMENT_CACHE_PATH: () => KIRO_MANAGEMENT_CACHE_PATH,
1137
+ KIRO_MANAGEMENT_CACHE_SOURCE: () => KIRO_MANAGEMENT_CACHE_SOURCE,
1138
+ KIRO_MANAGEMENT_CACHE_VERSION: () => KIRO_MANAGEMENT_CACHE_VERSION,
1139
+ KIRO_MODEL_IDS: () => KIRO_MODEL_IDS,
1140
+ applyEffortLadder: () => applyEffortLadder,
1141
+ getCachedModels: () => getCachedModels,
1142
+ isCacheStale: () => isCacheStale,
1143
+ kiroModels: () => kiroModels,
1144
+ loadCachedModelIds: () => loadCachedModelIds,
1145
+ mapKiroCatalogModels: () => mapKiroCatalogModels,
1146
+ resolveApiRegion: () => resolveApiRegion,
1147
+ resolveKiroModel: () => resolveKiroModel,
1148
+ updateKiroModelsCache: () => updateKiroModelsCache
1149
+ });
1150
+ import { randomUUID } from "node:crypto";
1151
+ import { existsSync as existsSync3, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync } from "node:fs";
1152
+ import { homedir as homedir4 } from "node:os";
1153
+ import { join as join4 } from "node:path";
1154
+ function isRecord2(value) {
1155
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1156
+ }
1157
+ function isPositiveNumber(value) {
1158
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
1159
+ }
1160
+ function isEffortList(value) {
1161
+ return Array.isArray(value) && value.length > 0 && value.every((effort) => KIRO_EFFORT_ORDER.includes(effort));
1162
+ }
1163
+ function isEffortMap(value) {
1164
+ return isRecord2(value) && Object.values(value).every((mapped) => typeof mapped === "string");
1165
+ }
1166
+ function isCachedKiroModel(value) {
1167
+ if (!isRecord2(value))
1168
+ return false;
1169
+ const cost = value.cost;
1170
+ const input = value.input;
1171
+ const schema = value.additionalModelRequestFieldsSchema;
1172
+ const tokenLimits = value.tokenLimits;
1173
+ return typeof value.id === "string" && value.id.length > 0 && typeof value.kiroModelId === "string" && value.kiroModelId.length > 0 && typeof value.name === "string" && value.name.length > 0 && typeof value.reasoning === "boolean" && Array.isArray(input) && input.length > 0 && input.every((modality) => modality === "text" || modality === "image") && isRecord2(cost) && typeof cost.input === "number" && typeof cost.output === "number" && typeof cost.cacheRead === "number" && typeof cost.cacheWrite === "number" && isPositiveNumber(value.contextWindow) && isPositiveNumber(value.maxTokens) && (value.efforts === void 0 || isEffortList(value.efforts)) && (value.effortMap === void 0 || isEffortMap(value.effortMap)) && (schema === void 0 || isRecord2(schema)) && (tokenLimits === void 0 || isRecord2(tokenLimits)) && (value.firstTokenTimeout === void 0 || isPositiveNumber(value.firstTokenTimeout)) && (value.rateMultiplier === void 0 || isPositiveNumber(value.rateMultiplier)) && (value.rateUnit === void 0 || typeof value.rateUnit === "string");
1174
+ }
1175
+ function parseManagementCache(raw) {
1176
+ let value;
1177
+ try {
1178
+ value = JSON.parse(raw);
1179
+ } catch {
1180
+ return void 0;
1181
+ }
1182
+ if (!isRecord2(value) || value.version !== KIRO_MANAGEMENT_CACHE_VERSION || value.source !== KIRO_MANAGEMENT_CACHE_SOURCE || !isRecord2(value.regions)) {
1183
+ return void 0;
1184
+ }
1185
+ const regions = {};
1186
+ for (const [region, rawEntry] of Object.entries(value.regions)) {
1187
+ if (!isRecord2(rawEntry) || rawEntry.region !== region || !isPositiveNumber(rawEntry.fetchedAt) || !Array.isArray(rawEntry.models) || rawEntry.models.length === 0 || !rawEntry.models.every(isCachedKiroModel)) {
1188
+ return void 0;
1189
+ }
1190
+ const modelIds = /* @__PURE__ */ new Set();
1191
+ for (const model of rawEntry.models) {
1192
+ if (modelIds.has(model.id))
1193
+ return void 0;
1194
+ modelIds.add(model.id);
1195
+ }
1196
+ regions[region] = rawEntry;
1197
+ }
1198
+ return {
1199
+ version: KIRO_MANAGEMENT_CACHE_VERSION,
1200
+ source: KIRO_MANAGEMENT_CACHE_SOURCE,
1201
+ regions
1202
+ };
1203
+ }
1204
+ function readManagementCache() {
1205
+ if (!existsSync3(KIRO_MANAGEMENT_CACHE_PATH))
1206
+ return void 0;
1207
+ try {
1208
+ return parseManagementCache(readFileSync2(KIRO_MANAGEMENT_CACHE_PATH, "utf-8"));
1209
+ } catch {
1210
+ return void 0;
1211
+ }
1212
+ }
1213
+ function writeManagementCache(cache) {
1214
+ const temporaryPath = `${KIRO_MANAGEMENT_CACHE_PATH}.${process.pid}.${randomUUID()}.tmp`;
1215
+ try {
1216
+ writeFileSync(temporaryPath, JSON.stringify(cache, null, 2), "utf-8");
1217
+ renameSync(temporaryPath, KIRO_MANAGEMENT_CACHE_PATH);
1218
+ } finally {
1219
+ rmSync(temporaryPath, { force: true });
1220
+ }
1221
+ }
1222
+ function toLocalModelId(kiroModelId) {
1223
+ return kiroModelId.replace(/(\d)\.(\d)/g, "$1-$2");
1224
+ }
1225
+ function humanizeModelId(modelId) {
1226
+ return modelId.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
1227
+ }
1228
+ function applyEffortLadder(config) {
1229
+ if (!config || config.values.length === 0)
1230
+ return {};
1231
+ const efforts = KIRO_EFFORT_ORDER.filter((effort) => config.values.includes(effort));
1232
+ if (efforts.length === 0)
1233
+ return {};
1234
+ return { efforts, ...config.summarizedThinking ? { supportsSummarizedThinking: true } : {} };
1235
+ }
1236
+ function hasReasoningFamilyFallback(modelId) {
1237
+ const normalizedId = modelId.toLowerCase();
1238
+ return normalizedId === "auto" || REASONING_FAMILY_MARKERS.some((marker) => normalizedId.includes(marker));
1239
+ }
1240
+ function hasVerifiedImageInput(kiroModelId) {
1241
+ return kiroModelId.startsWith("claude-") || VERIFIED_IMAGE_MODEL_IDS.has(kiroModelId.toLowerCase());
1242
+ }
1243
+ function applyVerifiedCapabilities(model) {
1244
+ if (!hasVerifiedImageInput(model.kiroModelId) || model.input.includes("image"))
1245
+ return model;
1246
+ return { ...model, input: ["text", "image"] };
1247
+ }
1248
+ function validateCatalogMetadata(model) {
1249
+ const rawSchema = model.additionalModelRequestFieldsSchema;
1250
+ const schema = rawSchema ?? void 0;
1251
+ if (schema !== void 0 && !isRecord2(schema)) {
1252
+ throw new Error(`Kiro management catalog model ${model.modelId} has an invalid request-fields schema`);
1527
1253
  }
1528
- if (response.models.some((model) => !model || typeof model.modelId !== "string" || !model.modelId)) {
1529
- throw new Error(`Kiro management ListAvailableModels returned an invalid catalog in ${auth.region}`);
1254
+ const tokenLimits = model.tokenLimits;
1255
+ if (tokenLimits !== void 0 && !isRecord2(tokenLimits)) {
1256
+ throw new Error(`Kiro management catalog model ${model.modelId} has invalid token limits`);
1530
1257
  }
1531
- return response;
1258
+ if (tokenLimits && (tokenLimits.maxInputTokens !== void 0 && !isPositiveNumber(tokenLimits.maxInputTokens) || tokenLimits.maxOutputTokens !== void 0 && !isPositiveNumber(tokenLimits.maxOutputTokens))) {
1259
+ throw new Error(`Kiro management catalog model ${model.modelId} has invalid token limits`);
1260
+ }
1261
+ return { schema, tokenLimits };
1532
1262
  }
1533
- async function fetchKiroModelCatalog(auth, providedProfileArn) {
1534
- const profileArn = await resolveKiroProfileArn(auth, providedProfileArn);
1535
- const region = isApiKey(auth.accessToken) ? API_KEY_REGION : profileRegionCache.get(profileCacheKey(auth)) ?? auth.region;
1536
- if (region !== auth.region) {
1537
- return listAvailableModels({ ...auth, region }, profileArn);
1263
+ function mapKiroCatalogModels(catalogModels, region, rates) {
1264
+ if (catalogModels.length === 0) {
1265
+ throw new Error(`Kiro management catalog returned no models in ${region}`);
1538
1266
  }
1539
- return listAvailableModels(auth, profileArn);
1267
+ const seenIds = /* @__PURE__ */ new Set();
1268
+ return catalogModels.map((catalogModel) => {
1269
+ const kiroModelId = catalogModel.modelId;
1270
+ if (!kiroModelId || kiroModelId.trim() !== kiroModelId) {
1271
+ throw new Error(`Kiro management catalog returned an invalid model ID in ${region}`);
1272
+ }
1273
+ const id = toLocalModelId(kiroModelId);
1274
+ if (seenIds.has(id)) {
1275
+ throw new Error(`Kiro management catalog contains conflicting model ID ${id} in ${region}`);
1276
+ }
1277
+ seenIds.add(id);
1278
+ const existing = kiroModels.find((model) => model.id === id);
1279
+ const rate = rates?.get(kiroModelId);
1280
+ const { schema, tokenLimits } = validateCatalogMetadata(catalogModel);
1281
+ const effortConfig = getKiroEffortConfig(schema, kiroModelId);
1282
+ const catalogName = typeof catalogModel.displayName === "string" && catalogModel.displayName.length > 0 ? catalogModel.displayName : void 0;
1283
+ return {
1284
+ id,
1285
+ kiroModelId,
1286
+ // Humanize the wire id, not the local one: `toLocalModelId` rewrites
1287
+ // `5.1` as `5-1`, and a display name derived from it loses the dot —
1288
+ // "Claude Fable 5 1" instead of "Claude Fable 5.1".
1289
+ name: catalogName ?? existing?.name ?? humanizeModelId(kiroModelId),
1290
+ region,
1291
+ // Deliberately schema-only: when a schema exists the resolver returns
1292
+ // `deriveKiroEffort(schema)`, and when it does not the family-marker guess
1293
+ // decides. The fallback tier feeds the ladders, not this flag.
1294
+ reasoning: schema !== void 0 && effortConfig !== void 0 || schema === void 0 && hasReasoningFamilyFallback(id),
1295
+ ...applyEffortLadder(effortConfig),
1296
+ input: existing ? [...existing.input] : hasVerifiedImageInput(kiroModelId) ? ["text", "image"] : ["text"],
1297
+ ...id.startsWith("claude-") ? { recoverTextToolCalls: false } : {},
1298
+ cost: ZERO_COST,
1299
+ ...rate ? { rateMultiplier: rate.multiplier, ...rate.unit ? { rateUnit: rate.unit } : {} } : {},
1300
+ contextWindow: tokenLimits?.maxInputTokens ?? DEFAULT_CONTEXT_WINDOW,
1301
+ maxTokens: tokenLimits?.maxOutputTokens ?? DEFAULT_MAX_TOKENS,
1302
+ ...existing?.firstTokenTimeout ? { firstTokenTimeout: existing.firstTokenTimeout } : {},
1303
+ ...schema ? { additionalModelRequestFieldsSchema: schema } : {},
1304
+ ...tokenLimits ? { tokenLimits } : {}
1305
+ };
1306
+ });
1540
1307
  }
1541
- async function getUsageLimits(auth, request) {
1542
- const operation = "GetUsageLimits";
1543
- const url = new URL("Get-Usage-Limits", getKiroEndpoints(auth.region).management);
1544
- for (const [name, value] of Object.entries(request)) {
1545
- if (value !== void 0)
1546
- url.searchParams.set(name, String(value));
1308
+ function refreshKnownModelIds(cache) {
1309
+ KIRO_MODEL_IDS.clear();
1310
+ for (const modelId of BOOTSTRAP_KIRO_MODEL_IDS)
1311
+ KIRO_MODEL_IDS.add(modelId);
1312
+ if (!cache)
1313
+ return;
1314
+ for (const entry of Object.values(cache.regions)) {
1315
+ for (const model of entry.models)
1316
+ KIRO_MODEL_IDS.add(model.kiroModelId);
1547
1317
  }
1548
- let response;
1549
- try {
1550
- response = await fetch(url.toString(), {
1551
- method: "GET",
1552
- headers: {
1553
- Accept: "application/json",
1554
- ...kiroAuthHeaders(auth.accessToken),
1555
- ...kiroTokenTypeHeaders(auth.accessToken),
1556
- "User-Agent": "ns-kiro-provider"
1557
- }
1558
- });
1559
- } catch (error2) {
1560
- throw new Error(`Kiro management ${operation} request failed in ${auth.region}`, { cause: error2 });
1318
+ }
1319
+ function loadCachedModelIds() {
1320
+ refreshKnownModelIds(readManagementCache());
1321
+ }
1322
+ function getCachedModels(region) {
1323
+ const cache = readManagementCache();
1324
+ refreshKnownModelIds(cache);
1325
+ const models = cache?.regions[region]?.models ?? kiroModels;
1326
+ let changed = false;
1327
+ const corrected = models.map((model) => {
1328
+ const result = applyVerifiedCapabilities(model);
1329
+ if (result !== model)
1330
+ changed = true;
1331
+ return result;
1332
+ });
1333
+ return changed ? corrected : models;
1334
+ }
1335
+ function isCacheStale(region) {
1336
+ const entry = readManagementCache()?.regions[region];
1337
+ return !entry || Date.now() - entry.fetchedAt > CACHE_MAX_AGE_MS;
1338
+ }
1339
+ async function updateKiroModelsCache(accessToken, region, profileArn) {
1340
+ const response = await fetchKiroModelCatalog({ accessToken, region }, profileArn);
1341
+ const models = mapKiroCatalogModels(response.models, region, getKiroCliModelRates());
1342
+ const cache = readManagementCache() ?? {
1343
+ version: KIRO_MANAGEMENT_CACHE_VERSION,
1344
+ source: KIRO_MANAGEMENT_CACHE_SOURCE,
1345
+ regions: {}
1346
+ };
1347
+ cache.regions[region] = { region, fetchedAt: Date.now(), models };
1348
+ writeManagementCache(cache);
1349
+ refreshKnownModelIds(cache);
1350
+ }
1351
+ function resolveKiroModel(modelId, exactKiroModelId) {
1352
+ if (exactKiroModelId)
1353
+ return exactKiroModelId;
1354
+ const cachedModel = Object.values(readManagementCache()?.regions ?? {}).flatMap((entry) => entry.models).find((model) => model.id === modelId);
1355
+ if (cachedModel) {
1356
+ KIRO_MODEL_IDS.add(cachedModel.kiroModelId);
1357
+ return cachedModel.kiroModelId;
1561
1358
  }
1562
- return parseManagementResponse(response, operation, auth.region);
1359
+ const bootstrapModel = kiroModels.find((model) => model.id === modelId);
1360
+ if (bootstrapModel)
1361
+ return bootstrapModel.kiroModelId;
1362
+ const normalizedId = modelId.replace(/(\d)-(\d)/g, "$1.$2");
1363
+ loadCachedModelIds();
1364
+ if (!KIRO_MODEL_IDS.has(normalizedId)) {
1365
+ throw new Error(`Unknown Kiro model ID: ${modelId}`);
1366
+ }
1367
+ return normalizedId;
1563
1368
  }
1564
- var LIST_PROFILES_PATH, LIST_MODELS_PATH, GET_PROFILE_TARGET, API_KEY_REGION, profileArnCache, pendingProfileRequests, profileRegionCache, CANONICAL_MANAGEMENT_REGIONS, ENV_PROFILE_ARN, KiroManagementHttpError;
1565
- var init_management = __esm({
1566
- "../kiro-core/dist/management.js"() {
1369
+ var KIRO_MANAGEMENT_CACHE_VERSION, KIRO_MANAGEMENT_CACHE_SOURCE, KIRO_MANAGEMENT_CACHE_PATH, CACHE_MAX_AGE_MS, DEFAULT_CONTEXT_WINDOW, DEFAULT_MAX_TOKENS, ZERO_COST, REASONING_FAMILY_MARKERS, VERIFIED_IMAGE_MODEL_IDS, bootstrapKiroModels, kiroModels, BOOTSTRAP_KIRO_MODEL_IDS, KIRO_MODEL_IDS;
1370
+ var init_models = __esm({
1371
+ "../kiro-core/dist/models.js"() {
1567
1372
  "use strict";
1568
- init_debug();
1373
+ init_effort();
1374
+ init_kiro_cli();
1375
+ init_management();
1376
+ init_types();
1569
1377
  init_endpoints();
1570
- init_oauth();
1571
- init_token_type();
1572
- LIST_PROFILES_PATH = "List-Available-Profiles";
1573
- LIST_MODELS_PATH = "List-Available-Models";
1574
- GET_PROFILE_TARGET = "AmazonCodeWhispererService.GetProfile";
1575
- API_KEY_REGION = "us-east-1";
1576
- profileArnCache = /* @__PURE__ */ new Map();
1577
- pendingProfileRequests = /* @__PURE__ */ new Map();
1578
- profileRegionCache = /* @__PURE__ */ new Map();
1579
- CANONICAL_MANAGEMENT_REGIONS = ["us-east-1", "eu-central-1"];
1580
- ENV_PROFILE_ARN = "KIRO_PROFILE_ARN";
1581
- KiroManagementHttpError = class extends Error {
1582
- status;
1583
- constructor(message, status) {
1584
- super(message);
1585
- this.status = status;
1586
- this.name = "KiroManagementHttpError";
1378
+ KIRO_MANAGEMENT_CACHE_VERSION = 1;
1379
+ KIRO_MANAGEMENT_CACHE_SOURCE = "ns-kiro-provider-management";
1380
+ KIRO_MANAGEMENT_CACHE_PATH = join4(homedir4(), ".ns-kiro-provider-models-cache.json");
1381
+ CACHE_MAX_AGE_MS = 36e5;
1382
+ DEFAULT_CONTEXT_WINDOW = 2e5;
1383
+ DEFAULT_MAX_TOKENS = 8192;
1384
+ ZERO_COST = Object.freeze({ input: 0, output: 0, cacheRead: 0, cacheWrite: 0 });
1385
+ REASONING_FAMILY_MARKERS = ["opus", "sonnet", "fable", "coder", "deepseek", "gpt", "glm", "qwen"];
1386
+ VERIFIED_IMAGE_MODEL_IDS = /* @__PURE__ */ new Set(["gpt-5.6-luna"]);
1387
+ bootstrapKiroModels = [
1388
+ {
1389
+ id: "claude-opus-5",
1390
+ kiroModelId: "claude-opus-5",
1391
+ name: "Claude Opus 5",
1392
+ reasoning: true,
1393
+ input: ["text", "image"],
1394
+ cost: ZERO_COST,
1395
+ contextWindow: 1e6,
1396
+ maxTokens: 128e3,
1397
+ firstTokenTimeout: 18e4
1398
+ },
1399
+ {
1400
+ id: "claude-opus-4-8",
1401
+ kiroModelId: "claude-opus-4.8",
1402
+ name: "Claude Opus 4.8",
1403
+ reasoning: true,
1404
+ input: ["text", "image"],
1405
+ cost: ZERO_COST,
1406
+ contextWindow: 1e6,
1407
+ maxTokens: 128e3,
1408
+ firstTokenTimeout: 18e4
1409
+ },
1410
+ {
1411
+ id: "claude-opus-4-7",
1412
+ kiroModelId: "claude-opus-4.7",
1413
+ name: "Claude Opus 4.7",
1414
+ reasoning: true,
1415
+ input: ["text", "image"],
1416
+ cost: ZERO_COST,
1417
+ contextWindow: 1e6,
1418
+ maxTokens: 128e3,
1419
+ firstTokenTimeout: 18e4
1420
+ },
1421
+ {
1422
+ id: "claude-opus-4-6",
1423
+ kiroModelId: "claude-opus-4.6",
1424
+ name: "Claude Opus 4.6",
1425
+ reasoning: true,
1426
+ input: ["text", "image"],
1427
+ cost: ZERO_COST,
1428
+ contextWindow: 1e6,
1429
+ maxTokens: 32768
1430
+ },
1431
+ {
1432
+ id: "claude-opus-4-5",
1433
+ kiroModelId: "claude-opus-4.5",
1434
+ name: "Claude Opus 4.5",
1435
+ reasoning: true,
1436
+ input: ["text", "image"],
1437
+ cost: ZERO_COST,
1438
+ contextWindow: 2e5,
1439
+ maxTokens: 64e3
1440
+ },
1441
+ {
1442
+ id: "claude-sonnet-5",
1443
+ kiroModelId: "claude-sonnet-5",
1444
+ name: "Claude Sonnet 5",
1445
+ reasoning: true,
1446
+ input: ["text", "image"],
1447
+ cost: ZERO_COST,
1448
+ contextWindow: 1e6,
1449
+ maxTokens: 65536
1450
+ },
1451
+ {
1452
+ id: "claude-sonnet-4-6",
1453
+ kiroModelId: "claude-sonnet-4.6",
1454
+ name: "Claude Sonnet 4.6",
1455
+ reasoning: true,
1456
+ input: ["text", "image"],
1457
+ cost: ZERO_COST,
1458
+ contextWindow: 1e6,
1459
+ maxTokens: 65536
1460
+ },
1461
+ {
1462
+ id: "claude-sonnet-4-5",
1463
+ kiroModelId: "claude-sonnet-4.5",
1464
+ name: "Claude Sonnet 4.5",
1465
+ reasoning: true,
1466
+ input: ["text", "image"],
1467
+ cost: ZERO_COST,
1468
+ contextWindow: 2e5,
1469
+ maxTokens: 65536
1470
+ },
1471
+ {
1472
+ id: "claude-sonnet-4",
1473
+ kiroModelId: "claude-sonnet-4",
1474
+ name: "Claude Sonnet 4",
1475
+ reasoning: true,
1476
+ input: ["text", "image"],
1477
+ cost: ZERO_COST,
1478
+ contextWindow: 2e5,
1479
+ maxTokens: 65536
1480
+ },
1481
+ {
1482
+ id: "claude-haiku-4-5",
1483
+ kiroModelId: "claude-haiku-4.5",
1484
+ name: "Claude Haiku 4.5",
1485
+ reasoning: false,
1486
+ input: ["text", "image"],
1487
+ cost: ZERO_COST,
1488
+ contextWindow: 2e5,
1489
+ maxTokens: 65536
1490
+ },
1491
+ {
1492
+ id: "claude-fable-5",
1493
+ kiroModelId: "claude-fable-5",
1494
+ name: "Claude Fable 5",
1495
+ reasoning: true,
1496
+ input: ["text", "image"],
1497
+ cost: ZERO_COST,
1498
+ contextWindow: 1e6,
1499
+ maxTokens: 65536
1500
+ },
1501
+ {
1502
+ id: "gpt-5-6-luna",
1503
+ kiroModelId: "gpt-5.6-luna",
1504
+ name: "GPT-5.6 Luna",
1505
+ reasoning: true,
1506
+ input: ["text", "image"],
1507
+ cost: ZERO_COST,
1508
+ contextWindow: 272e3,
1509
+ maxTokens: 128e3
1510
+ },
1511
+ {
1512
+ id: "gpt-5-6-sol",
1513
+ kiroModelId: "gpt-5.6-sol",
1514
+ name: "GPT-5.6 Sol",
1515
+ reasoning: true,
1516
+ input: ["text"],
1517
+ cost: ZERO_COST,
1518
+ contextWindow: 272e3,
1519
+ maxTokens: 128e3
1520
+ },
1521
+ {
1522
+ id: "gpt-5-6-terra",
1523
+ kiroModelId: "gpt-5.6-terra",
1524
+ name: "GPT-5.6 Terra",
1525
+ reasoning: true,
1526
+ input: ["text"],
1527
+ cost: ZERO_COST,
1528
+ contextWindow: 272e3,
1529
+ maxTokens: 128e3
1530
+ },
1531
+ {
1532
+ id: "deepseek-3-2",
1533
+ kiroModelId: "deepseek-3.2",
1534
+ name: "DeepSeek 3.2",
1535
+ reasoning: true,
1536
+ input: ["text"],
1537
+ cost: ZERO_COST,
1538
+ contextWindow: 164e3,
1539
+ maxTokens: 8192
1540
+ },
1541
+ {
1542
+ id: "minimax-m2-5",
1543
+ kiroModelId: "minimax-m2.5",
1544
+ name: "MiniMax M2.5",
1545
+ reasoning: false,
1546
+ input: ["text"],
1547
+ cost: ZERO_COST,
1548
+ contextWindow: 196e3,
1549
+ maxTokens: 8192
1550
+ },
1551
+ {
1552
+ id: "minimax-m2-1",
1553
+ kiroModelId: "minimax-m2.1",
1554
+ name: "MiniMax M2.1",
1555
+ reasoning: false,
1556
+ input: ["text"],
1557
+ cost: ZERO_COST,
1558
+ contextWindow: 196e3,
1559
+ maxTokens: 8192
1560
+ },
1561
+ {
1562
+ id: "glm-5",
1563
+ kiroModelId: "glm-5",
1564
+ name: "GLM 5",
1565
+ reasoning: true,
1566
+ input: ["text"],
1567
+ cost: ZERO_COST,
1568
+ contextWindow: 2e5,
1569
+ maxTokens: 8192
1570
+ },
1571
+ {
1572
+ id: "qwen3-coder-next",
1573
+ kiroModelId: "qwen3-coder-next",
1574
+ name: "Qwen3 Coder Next",
1575
+ reasoning: true,
1576
+ input: ["text"],
1577
+ cost: ZERO_COST,
1578
+ contextWindow: 256e3,
1579
+ maxTokens: 8192
1580
+ },
1581
+ {
1582
+ id: "auto",
1583
+ kiroModelId: "auto",
1584
+ name: "Auto",
1585
+ reasoning: true,
1586
+ input: ["text", "image"],
1587
+ cost: ZERO_COST,
1588
+ contextWindow: 1e6,
1589
+ maxTokens: 65536
1587
1590
  }
1588
- };
1591
+ ];
1592
+ kiroModels = bootstrapKiroModels.map((model) => ({
1593
+ ...model,
1594
+ ...applyEffortLadder(model.reasoning ? getKiroEffortConfig(model.additionalModelRequestFieldsSchema, model.kiroModelId) : void 0),
1595
+ ...model.id.startsWith("claude-") ? { recoverTextToolCalls: false } : {}
1596
+ }));
1597
+ BOOTSTRAP_KIRO_MODEL_IDS = kiroModels.map((model) => model.kiroModelId);
1598
+ KIRO_MODEL_IDS = new Set(BOOTSTRAP_KIRO_MODEL_IDS);
1589
1599
  }
1590
1600
  });
1591
1601
 
@@ -1653,8 +1663,8 @@ var require_base64_js = __commonJS({
1653
1663
  }
1654
1664
  return arr;
1655
1665
  }
1656
- function tripletToBase64(num) {
1657
- return lookup[num >> 18 & 63] + lookup[num >> 12 & 63] + lookup[num >> 6 & 63] + lookup[num & 63];
1666
+ function tripletToBase64(num2) {
1667
+ return lookup[num2 >> 18 & 63] + lookup[num2 >> 12 & 63] + lookup[num2 >> 6 & 63] + lookup[num2 & 63];
1658
1668
  }
1659
1669
  function encodeChunk(uint8, start, end) {
1660
1670
  var tmp;
@@ -1747,6 +1757,10 @@ var KiroBlockBuffer = class {
1747
1757
  getText(index) {
1748
1758
  return this.blocks.get(index)?.text ?? "";
1749
1759
  }
1760
+ /** Kinds of the live blocks, in allocation order. */
1761
+ kinds() {
1762
+ return [...this.blocks.values()].map((block) => block.kind);
1763
+ }
1750
1764
  /**
1751
1765
  * Rewrite one accumulated block before it is closed. Used by the recovery
1752
1766
  * passes that lift tool calls out of text and by echo-noise stripping, both
@@ -1836,23 +1850,262 @@ function parseBracketToolCalls(text) {
1836
1850
  }
1837
1851
  return { toolCalls, cleanedText };
1838
1852
  }
1839
-
1840
- // ../kiro-core/dist/cost.js
1841
- var PER_MILLION = 1e6;
1842
- function calculateKiroCost(cost, usage) {
1843
- const input = usage.input * cost.input / PER_MILLION;
1844
- const output = usage.output * cost.output / PER_MILLION;
1845
- const cacheRead = (usage.cacheRead ?? 0) * cost.cacheRead / PER_MILLION;
1846
- const cacheWrite = (usage.cacheWrite ?? 0) * cost.cacheWrite / PER_MILLION;
1847
- return { input, output, cacheRead, cacheWrite, total: input + output + cacheRead + cacheWrite };
1853
+
1854
+ // ../kiro-core/dist/cache-estimator.js
1855
+ var stateByConversation = /* @__PURE__ */ new Map();
1856
+ var MAX_TRACKED_CONVERSATIONS = 128;
1857
+ var warnedWireCacheCounters = false;
1858
+ function applyCacheEstimate(conversationId, usage, wireUsage, config, now = Date.now()) {
1859
+ const previous = stateByConversation.get(conversationId);
1860
+ const promptTokens = usage.input + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0) + usage.output;
1861
+ const wireReportedCache = wireUsage?.cacheReadInputTokens !== void 0 || wireUsage?.cacheWriteInputTokens !== void 0;
1862
+ if (wireReportedCache) {
1863
+ if (config.estimateCacheUsage && !warnedWireCacheCounters) {
1864
+ warnedWireCacheCounters = true;
1865
+ console.warn("[kiro-core] Kiro now reports cache token counters; cache estimation was skipped and may be disabled.");
1866
+ }
1867
+ noteSuccessfulTurn(conversationId, promptTokens, usage.input, now);
1868
+ return 0;
1869
+ }
1870
+ if (!config.estimateCacheUsage || usage.input <= 0)
1871
+ return 0;
1872
+ if (!previous) {
1873
+ noteSuccessfulTurn(conversationId, promptTokens, usage.input, now);
1874
+ return 0;
1875
+ }
1876
+ const expired = config.estimatedCacheTimeout > 0 && now - previous.turnTimestamp > config.estimatedCacheTimeout;
1877
+ const contextShrank = usage.input < previous.promptTokens * 0.6;
1878
+ if (expired || contextShrank) {
1879
+ noteSuccessfulTurn(conversationId, promptTokens, usage.input, now);
1880
+ return 0;
1881
+ }
1882
+ const estimatedRead = Math.min(previous.promptTokens, usage.input);
1883
+ if (estimatedRead > 0) {
1884
+ usage.input -= estimatedRead;
1885
+ usage.cacheRead = (usage.cacheRead ?? 0) + estimatedRead;
1886
+ usage.cacheEstimated = true;
1887
+ }
1888
+ noteSuccessfulTurn(conversationId, promptTokens, promptTokens, now);
1889
+ return estimatedRead;
1890
+ }
1891
+ function noteSuccessfulTurn(conversationId, promptTokens, input, now) {
1892
+ if (input <= 0 || promptTokens <= 0)
1893
+ return;
1894
+ stateByConversation.delete(conversationId);
1895
+ stateByConversation.set(conversationId, { promptTokens, turnTimestamp: now });
1896
+ if (stateByConversation.size > MAX_TRACKED_CONVERSATIONS) {
1897
+ const oldest = stateByConversation.keys().next().value;
1898
+ if (oldest !== void 0)
1899
+ stateByConversation.delete(oldest);
1900
+ }
1901
+ }
1902
+
1903
+ // ../kiro-core/dist/cost.js
1904
+ var PER_MILLION = 1e6;
1905
+ function calculateKiroCost(cost, usage) {
1906
+ const input = usage.input * cost.input / PER_MILLION;
1907
+ const output = usage.output * cost.output / PER_MILLION;
1908
+ const cacheRead = (usage.cacheRead ?? 0) * cost.cacheRead / PER_MILLION;
1909
+ const cacheWrite = (usage.cacheWrite ?? 0) * cost.cacheWrite / PER_MILLION;
1910
+ return { input, output, cacheRead, cacheWrite, total: input + output + cacheRead + cacheWrite };
1911
+ }
1912
+
1913
+ // ../kiro-core/dist/index.js
1914
+ init_debug();
1915
+ init_effort();
1916
+ init_endpoints();
1917
+
1918
+ // ../kiro-core/dist/retry.js
1919
+ init_models();
1920
+ var FIRST_TOKEN_TIMEOUT = 9e4;
1921
+ var REQUEST_HEADER_TIMEOUT = 9e4;
1922
+ function firstTokenTimeoutForModel(modelId) {
1923
+ if (retryConfig.firstTokenTimeoutMs !== FIRST_TOKEN_TIMEOUT) {
1924
+ return retryConfig.firstTokenTimeoutMs;
1925
+ }
1926
+ const model = kiroModels.find((m) => m.id === modelId);
1927
+ return model?.firstTokenTimeout ?? FIRST_TOKEN_TIMEOUT;
1928
+ }
1929
+ var retryConfig = {
1930
+ firstTokenTimeoutMs: FIRST_TOKEN_TIMEOUT,
1931
+ requestHeaderTimeoutMs: REQUEST_HEADER_TIMEOUT
1932
+ };
1933
+ function exponentialBackoff(attempt, baseMs, maxMs) {
1934
+ return Math.min(baseMs * 2 ** attempt, maxMs);
1935
+ }
1936
+ var MAX_RETRY_DELAY = 1e4;
1937
+ var REQUEST_RATE_FALLBACK_DELAY_MS = 1e4;
1938
+ function extractKiroReason(errorText) {
1939
+ if (!errorText)
1940
+ return void 0;
1941
+ try {
1942
+ const parsed = JSON.parse(errorText);
1943
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
1944
+ return void 0;
1945
+ const reason = parsed.reason;
1946
+ return typeof reason === "string" && reason.length > 0 ? reason : void 0;
1947
+ } catch {
1948
+ return void 0;
1949
+ }
1950
+ }
1951
+ function parseRetryAfterMs(headers, nowMs = Date.now()) {
1952
+ const get = headers?.get?.bind(headers);
1953
+ if (!get)
1954
+ return void 0;
1955
+ const milliseconds = nonNegativeNumber(get("retry-after-ms"));
1956
+ if (milliseconds !== void 0)
1957
+ return Math.round(milliseconds);
1958
+ const retryAfter = get("retry-after");
1959
+ if (retryAfter !== null && retryAfter.trim() !== "") {
1960
+ const seconds = Number(retryAfter);
1961
+ if (Number.isFinite(seconds)) {
1962
+ if (seconds >= 0) {
1963
+ const delayMs = seconds * 1e3;
1964
+ if (Number.isFinite(delayMs))
1965
+ return Math.round(delayMs);
1966
+ }
1967
+ } else {
1968
+ const dateMs = Date.parse(retryAfter);
1969
+ if (!Number.isNaN(dateMs))
1970
+ return Math.max(0, dateMs - nowMs);
1971
+ }
1972
+ }
1973
+ const resetAfterSeconds = nonNegativeNumber(get("x-ratelimit-reset-after"));
1974
+ if (resetAfterSeconds !== void 0) {
1975
+ const delayMs = resetAfterSeconds * 1e3;
1976
+ if (Number.isFinite(delayMs))
1977
+ return Math.round(delayMs);
1978
+ }
1979
+ return void 0;
1980
+ }
1981
+ function resolveRequestRateRetryDelay(headers, nowMs = Date.now()) {
1982
+ const advertisedDelayMs = parseRetryAfterMs(headers, nowMs);
1983
+ const requestedDelayMs = advertisedDelayMs ?? REQUEST_RATE_FALLBACK_DELAY_MS;
1984
+ return {
1985
+ delayMs: Math.min(requestedDelayMs, MAX_RETRY_DELAY),
1986
+ ...advertisedDelayMs !== void 0 ? { advertisedDelayMs } : {},
1987
+ capped: requestedDelayMs > MAX_RETRY_DELAY
1988
+ };
1989
+ }
1990
+ function nonNegativeNumber(value) {
1991
+ if (value === null || value.trim() === "")
1992
+ return void 0;
1993
+ const parsed = Number(value);
1994
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
1995
+ }
1996
+ var KIRO_REASON_CODES = Object.freeze({
1997
+ /** Request body exceeded the service's size threshold. */
1998
+ CONTENT_LENGTH_EXCEEDS_THRESHOLD: "CONTENT_LENGTH_EXCEEDS_THRESHOLD",
1999
+ /** Prose-only size rejection; the service sends no reason code for this one. */
2000
+ INPUT_TOO_LONG: "Input is too long",
2001
+ /** Monthly request quota exhausted — hard limit, not transient. */
2002
+ MONTHLY_REQUEST_COUNT: "MONTHLY_REQUEST_COUNT",
2003
+ /** Model capacity temporarily unavailable — transient, worth retrying. */
2004
+ INSUFFICIENT_MODEL_CAPACITY: "INSUFFICIENT_MODEL_CAPACITY",
2005
+ /** Short-window request throttle — retry only within the provider budget. */
2006
+ USER_REQUEST_RATE_EXCEEDED: "USER_REQUEST_RATE_EXCEEDED",
2007
+ /**
2008
+ * Generic request-validation rejection, returned for a malformed body of any
2009
+ * size (empty `content`, history referencing tools absent from the catalog).
2010
+ * Not a size signal: classifying it as "too big" makes the caller compact a
2011
+ * history that was never the problem, a loop it can never satisfy.
2012
+ */
2013
+ REQUEST_BODY_INVALID: "REQUEST_BODY_INVALID"
2014
+ });
2015
+ var TOO_BIG_PATTERNS = Object.freeze([
2016
+ KIRO_REASON_CODES.CONTENT_LENGTH_EXCEEDS_THRESHOLD,
2017
+ KIRO_REASON_CODES.INPUT_TOO_LONG
2018
+ ]);
2019
+ var NON_RETRYABLE_BODY_PATTERNS = Object.freeze([KIRO_REASON_CODES.MONTHLY_REQUEST_COUNT]);
2020
+ var CAPACITY_PATTERN = KIRO_REASON_CODES.INSUFFICIENT_MODEL_CAPACITY;
2021
+ var CAPACITY_MAX_RETRIES = 3;
2022
+ var CAPACITY_BASE_DELAY_MS = 5e3;
2023
+ var capacityRetryConfig = {
2024
+ maxRetries: CAPACITY_MAX_RETRIES,
2025
+ baseDelayMs: CAPACITY_BASE_DELAY_MS
2026
+ };
2027
+ function isTooBigError(status, errorText) {
2028
+ return status === 413 || status === 400 && TOO_BIG_PATTERNS.some((p) => errorText.includes(p));
2029
+ }
2030
+ function isNonRetryableBodyError(errorText) {
2031
+ return NON_RETRYABLE_BODY_PATTERNS.some((p) => errorText.includes(p));
2032
+ }
2033
+ function isCapacityError(errorText) {
2034
+ return errorText.includes(CAPACITY_PATTERN);
1848
2035
  }
1849
2036
 
1850
- // ../kiro-core/dist/index.js
1851
- init_debug();
1852
- init_effort();
1853
- init_endpoints();
2037
+ // ../kiro-core/dist/errors.js
2038
+ var REASON_CODE_MARKERS = [
2039
+ KIRO_REASON_CODES.CONTENT_LENGTH_EXCEEDS_THRESHOLD,
2040
+ KIRO_REASON_CODES.MONTHLY_REQUEST_COUNT,
2041
+ KIRO_REASON_CODES.INSUFFICIENT_MODEL_CAPACITY,
2042
+ KIRO_REASON_CODES.REQUEST_BODY_INVALID
2043
+ ];
2044
+ var KiroApiError = class extends Error {
2045
+ status;
2046
+ reasonCode;
2047
+ retryAfterMs;
2048
+ providerAttempts;
2049
+ constructor(message, status, reasonCode, retryAfterMs, providerAttempts) {
2050
+ super(message);
2051
+ this.status = status;
2052
+ this.reasonCode = reasonCode;
2053
+ this.retryAfterMs = retryAfterMs;
2054
+ this.providerAttempts = providerAttempts;
2055
+ this.name = "KiroApiError";
2056
+ }
2057
+ };
2058
+ function extractKiroReasonCode(errorText) {
2059
+ if (!errorText)
2060
+ return void 0;
2061
+ const trimmed = errorText.trim();
2062
+ if (trimmed.startsWith("{")) {
2063
+ try {
2064
+ const parsed = JSON.parse(trimmed);
2065
+ const reason = parsed.reason ?? parsed.reasonCode;
2066
+ if (typeof reason === "string" && reason.length > 0)
2067
+ return reason;
2068
+ } catch {
2069
+ }
2070
+ }
2071
+ return REASON_CODE_MARKERS.find((code) => errorText.includes(code));
2072
+ }
1854
2073
 
1855
2074
  // ../kiro-core/dist/event-parser.js
2075
+ var KIRO_EVENT_KEYS = [
2076
+ "assistantResponseEvent",
2077
+ "codeReferenceEvent",
2078
+ "contextUsageEvent",
2079
+ "documentCitationEvent",
2080
+ "error",
2081
+ "metadataEvent",
2082
+ "meteringEvent",
2083
+ "reasoningContentEvent",
2084
+ "serviceUnavailableError",
2085
+ "throttlingError",
2086
+ "toolResultEvent",
2087
+ "toolUseEvent",
2088
+ "validationError"
2089
+ ];
2090
+ var KIRO_EVENT_KEY_SET = new Set(KIRO_EVENT_KEYS);
2091
+ var KIRO_ERROR_MEMBERS = {
2092
+ error: { kind: "internalServer", exception: "InternalServerException" },
2093
+ throttlingError: { kind: "throttling", exception: "ThrottlingException" },
2094
+ validationError: { kind: "validation", exception: "ValidationException" },
2095
+ serviceUnavailableError: { kind: "serviceUnavailable", exception: "ServiceUnavailableException" }
2096
+ };
2097
+ var KIRO_ERROR_TOKENS = {
2098
+ ...KIRO_ERROR_MEMBERS,
2099
+ InternalServerException: KIRO_ERROR_MEMBERS.error,
2100
+ ThrottlingException: KIRO_ERROR_MEMBERS.throttlingError,
2101
+ ValidationException: KIRO_ERROR_MEMBERS.validationError,
2102
+ ServiceUnavailableException: KIRO_ERROR_MEMBERS.serviceUnavailableError
2103
+ };
2104
+ function lookupKiroErrorMember(key) {
2105
+ return Object.hasOwn(KIRO_ERROR_TOKENS, key) ? KIRO_ERROR_TOKENS[key] : void 0;
2106
+ }
2107
+ var num = (v) => typeof v === "number" ? v : void 0;
2108
+ var str = (v) => typeof v === "string" ? v : void 0;
1856
2109
  function tokenCount(value) {
1857
2110
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
1858
2111
  }
@@ -1871,30 +2124,136 @@ var CACHE_WRITE_KEYS = [
1871
2124
  "cacheCreationInputTokens",
1872
2125
  "cacheWriteTokens"
1873
2126
  ];
1874
- function parseKiroEvent(parsed) {
1875
- if (parsed.content !== void 0)
1876
- return { type: "content", data: parsed.content };
1877
- if (typeof parsed.text === "string")
1878
- return { type: "thinkingText", data: parsed.text };
1879
- if (typeof parsed.signature === "string")
1880
- return { type: "thinkingSignature", data: parsed.signature };
2127
+ function toolInput(raw) {
2128
+ if (typeof raw === "string")
2129
+ return raw;
2130
+ if (raw && typeof raw === "object" && Object.keys(raw).length > 0) {
2131
+ return JSON.stringify(raw);
2132
+ }
2133
+ return "";
2134
+ }
2135
+ function parseToolUse(parsed) {
1881
2136
  if (parsed.name && parsed.toolUseId) {
1882
- const input = typeof parsed.input === "string" ? parsed.input : parsed.input && typeof parsed.input === "object" && Object.keys(parsed.input).length > 0 ? JSON.stringify(parsed.input) : "";
1883
2137
  return {
1884
2138
  type: "toolUse",
1885
2139
  data: {
1886
2140
  name: parsed.name,
1887
2141
  toolUseId: parsed.toolUseId,
1888
- input,
2142
+ input: toolInput(parsed.input),
1889
2143
  stop: parsed.stop
1890
2144
  }
1891
2145
  };
1892
2146
  }
2147
+ if (parsed.input !== void 0) {
2148
+ return { type: "toolUseInput", data: { input: toolInput(parsed.input) } };
2149
+ }
2150
+ if (parsed.stop !== void 0)
2151
+ return { type: "toolUseStop", data: { stop: parsed.stop } };
2152
+ return null;
2153
+ }
2154
+ function parseMetadata(parsed) {
2155
+ const tu = parsed.tokenUsage ?? {};
2156
+ const data = {
2157
+ inputTokens: tokenCount(tu.uncachedInputTokens) ?? tokenCount(tu.inputTokens),
2158
+ outputTokens: tokenCount(tu.outputTokens),
2159
+ totalTokens: tokenCount(tu.totalTokens),
2160
+ cacheReadInputTokens: firstTokenCount(tu, CACHE_READ_KEYS),
2161
+ cacheWriteInputTokens: firstTokenCount(tu, CACHE_WRITE_KEYS),
2162
+ contextUsagePercentage: num(tu.contextUsagePercentage),
2163
+ normalizedTokenUsage: num(tu.normalizedTokenUsage),
2164
+ rawStopReason: str(parsed.stopReason),
2165
+ stopDetails: parsed.stopDetails && typeof parsed.stopDetails === "object" ? parsed.stopDetails : void 0
2166
+ };
2167
+ for (const k of Object.keys(data)) {
2168
+ if (data[k] === void 0)
2169
+ delete data[k];
2170
+ }
2171
+ return Object.keys(data).length > 0 ? { type: "usage", data } : null;
2172
+ }
2173
+ function parseError(parsed, kind, fallbackName) {
2174
+ const rawError = parsed.error ?? parsed.Error;
2175
+ const error2 = typeof rawError === "string" ? rawError : rawError !== void 0 ? JSON.stringify(rawError) : str(parsed.name) ?? fallbackName;
2176
+ const message = parsed.message ?? parsed.Message ?? parsed.reason;
2177
+ const data = { error: error2, kind };
2178
+ if (typeof message === "string")
2179
+ data.message = message;
2180
+ const reason = str(parsed.reason);
2181
+ if (reason !== void 0)
2182
+ data.reason = reason;
2183
+ const retryAfterMilliseconds = num(parsed.retryAfterMilliseconds);
2184
+ if (retryAfterMilliseconds !== void 0)
2185
+ data.retryAfterMilliseconds = retryAfterMilliseconds;
2186
+ return { type: "error", data };
2187
+ }
2188
+ function parseKiroEvent(key, parsed) {
2189
+ switch (key) {
2190
+ case "assistantResponseEvent": {
2191
+ const content = str(parsed.content);
2192
+ return content !== void 0 ? { type: "content", data: content } : null;
2193
+ }
2194
+ case "reasoningContentEvent": {
2195
+ const text = str(parsed.text);
2196
+ if (text !== void 0)
2197
+ return { type: "thinkingText", data: text };
2198
+ const signature = str(parsed.signature);
2199
+ if (signature !== void 0)
2200
+ return { type: "thinkingSignature", data: signature };
2201
+ return { type: "ignored", data: { key } };
2202
+ }
2203
+ case "toolUseEvent":
2204
+ return parseToolUse(parsed);
2205
+ case "contextUsageEvent": {
2206
+ const pct = num(parsed.contextUsagePercentage);
2207
+ return pct !== void 0 ? { type: "contextUsage", data: { contextUsagePercentage: pct } } : null;
2208
+ }
2209
+ case "metadataEvent":
2210
+ return parseMetadata(parsed);
2211
+ case "meteringEvent":
2212
+ return {
2213
+ type: "metering",
2214
+ data: { credits: tokenCount(parsed.usage), unit: str(parsed.unit), unitPlural: str(parsed.unitPlural) }
2215
+ };
2216
+ // Reachable when a member that models an exception is delivered as an
2217
+ // ordinary event frame. The real wire uses exception framing, handled by
2218
+ // parseKiroExceptionFrame, but both routes must agree.
2219
+ case "error":
2220
+ case "throttlingError":
2221
+ case "validationError":
2222
+ case "serviceUnavailableError": {
2223
+ const member = lookupKiroErrorMember(key);
2224
+ return parseError(parsed, member.kind, member.exception);
2225
+ }
2226
+ // Known members with no consumer yet: explicitly ignored, not unparseable.
2227
+ case "codeReferenceEvent":
2228
+ case "documentCitationEvent":
2229
+ case "toolResultEvent":
2230
+ return { type: "ignored", data: { key } };
2231
+ default: {
2232
+ const member = lookupKiroErrorMember(key);
2233
+ if (member)
2234
+ return parseError(parsed, member.kind, member.exception);
2235
+ return parseKiroEventByShape(parsed);
2236
+ }
2237
+ }
2238
+ }
2239
+ function parseKiroExceptionFrame(key, parsed) {
2240
+ const member = lookupKiroErrorMember(key);
2241
+ if (!member)
2242
+ return null;
2243
+ const event = parseError(parsed, member.kind, member.exception);
2244
+ return event.data;
2245
+ }
2246
+ function parseKiroEventByShape(parsed) {
2247
+ if (parsed.content !== void 0)
2248
+ return { type: "content", data: parsed.content };
2249
+ if (typeof parsed.text === "string")
2250
+ return { type: "thinkingText", data: parsed.text };
2251
+ if (typeof parsed.signature === "string")
2252
+ return { type: "thinkingSignature", data: parsed.signature };
2253
+ if (parsed.name && parsed.toolUseId)
2254
+ return parseToolUse(parsed);
1893
2255
  if (parsed.input !== void 0 && !parsed.name) {
1894
- return {
1895
- type: "toolUseInput",
1896
- data: { input: typeof parsed.input === "string" ? parsed.input : JSON.stringify(parsed.input) }
1897
- };
2256
+ return { type: "toolUseInput", data: { input: toolInput(parsed.input) } };
1898
2257
  }
1899
2258
  if (parsed.stop !== void 0 && parsed.contextUsagePercentage === void 0)
1900
2259
  return { type: "toolUseStop", data: { stop: parsed.stop } };
@@ -1902,42 +2261,23 @@ function parseKiroEvent(parsed) {
1902
2261
  return { type: "contextUsage", data: { contextUsagePercentage: parsed.contextUsagePercentage } };
1903
2262
  if (parsed.followupPrompt !== void 0)
1904
2263
  return { type: "followupPrompt", data: parsed.followupPrompt };
2264
+ if (parsed.tokenUsage !== void 0 || parsed.stopReason !== void 0 || parsed.stopDetails !== void 0) {
2265
+ return parseMetadata(parsed);
2266
+ }
1905
2267
  if (parsed.error !== void 0 || parsed.Error !== void 0) {
1906
- const error2 = parsed.error || parsed.Error || "unknown";
1907
- const message = parsed.message || parsed.Message || parsed.reason;
1908
- return { type: "error", data: { error: typeof error2 === "string" ? error2 : JSON.stringify(error2), message } };
1909
- }
1910
- if (parsed.usage !== void 0) {
1911
- if (typeof parsed.usage === "number") {
1912
- const credits = tokenCount(parsed.usage);
1913
- if (credits === void 0)
1914
- return null;
1915
- return {
1916
- type: "usage",
1917
- data: {
1918
- credits,
1919
- ...typeof parsed.unit === "string" && parsed.unit ? { creditUnit: parsed.unit } : {}
1920
- }
1921
- };
1922
- }
1923
- const u = parsed.usage;
1924
- const cacheReadTokens = firstTokenCount(u, CACHE_READ_KEYS);
1925
- const cacheWriteTokens = firstTokenCount(u, CACHE_WRITE_KEYS);
2268
+ return parseError(parsed, "unknown", "unknown");
2269
+ }
2270
+ if (typeof parsed.usage === "number") {
1926
2271
  return {
1927
- type: "usage",
1928
- data: {
1929
- inputTokens: tokenCount(u.inputTokens),
1930
- outputTokens: tokenCount(u.outputTokens),
1931
- ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
1932
- ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
1933
- }
2272
+ type: "metering",
2273
+ data: { credits: tokenCount(parsed.usage), unit: str(parsed.unit), unitPlural: str(parsed.unitPlural) }
1934
2274
  };
1935
2275
  }
1936
2276
  return null;
1937
2277
  }
1938
2278
 
1939
2279
  // ../kiro-core/dist/transform.js
1940
- import { createHash } from "node:crypto";
2280
+ import { createHash as createHash2 } from "node:crypto";
1941
2281
  var TOOL_RESULT_LIMIT = 25e4;
1942
2282
  var EMPTY_CONTENT_PLACEHOLDER = "Please proceed with the task.";
1943
2283
  function sanitizeSurrogates(text) {
@@ -1955,7 +2295,7 @@ var KIRO_TOOL_USE_ID_PATTERN = /^[a-zA-Z0-9_.:-]{1,64}$/;
1955
2295
  function toKiroToolUseId(toolUseId) {
1956
2296
  if (KIRO_TOOL_USE_ID_PATTERN.test(toolUseId))
1957
2297
  return toolUseId;
1958
- const digest = createHash("sha256").update(toolUseId).digest("base64url").slice(0, 32);
2298
+ const digest = createHash2("sha256").update(toolUseId).digest("base64url").slice(0, 32);
1959
2299
  return `pi_${digest}`;
1960
2300
  }
1961
2301
  function normalizeMessages(messages) {
@@ -2969,8 +3309,8 @@ function bytePairEncode(piece, ranks) {
2969
3309
  return [ranks.get(piece.join(","))];
2970
3310
  return bytePairMerge(piece, ranks).map((p) => ranks.get(piece.slice(p.start, p.end).join(","))).filter((x) => x != null);
2971
3311
  }
2972
- function escapeRegex(str) {
2973
- return str.replace(/[\\^$*+?.()|[\]{}]/g, "\\$&");
3312
+ function escapeRegex(str2) {
3313
+ return str2.replace(/[\\^$*+?.()|[\]{}]/g, "\\$&");
2974
3314
  }
2975
3315
  var _Tiktoken = class {
2976
3316
  /** @internal */
@@ -3117,6 +3457,7 @@ var KiroResponseAssembler = class {
3117
3457
  totalContent = "";
3118
3458
  lastContentData = "";
3119
3459
  usageEvent = null;
3460
+ meteringEvent = null;
3120
3461
  receivedContextUsage = false;
3121
3462
  thinkingParser = null;
3122
3463
  nativeThinkingBlockIndex = null;
@@ -3124,16 +3465,19 @@ var KiroResponseAssembler = class {
3124
3465
  textBlockIndex = null;
3125
3466
  emittedToolCalls = 0;
3126
3467
  sawAnyToolCalls = false;
3468
+ droppedToolCalls = [];
3127
3469
  currentToolCall = null;
3470
+ stopReason = "stop";
3128
3471
  constructor(model, thinkingEnabled) {
3129
3472
  this.model = model;
3130
3473
  this.thinkingEnabled = thinkingEnabled;
3131
3474
  }
3132
- /** Clear per-attempt state. Block indexes and accumulated usage are kept. */
3475
+ /** Clear per-attempt state. Only block indexes are kept. */
3133
3476
  beginAttempt() {
3134
3477
  this.totalContent = "";
3135
3478
  this.lastContentData = "";
3136
3479
  this.usageEvent = null;
3480
+ this.meteringEvent = null;
3137
3481
  this.receivedContextUsage = false;
3138
3482
  this.thinkingParser = this.thinkingEnabled ? new ThinkingTagParser(this.blocks) : null;
3139
3483
  this.nativeThinkingBlockIndex = null;
@@ -3141,7 +3485,17 @@ var KiroResponseAssembler = class {
3141
3485
  this.textBlockIndex = null;
3142
3486
  this.emittedToolCalls = 0;
3143
3487
  this.sawAnyToolCalls = false;
3488
+ this.droppedToolCalls = [];
3144
3489
  this.currentToolCall = null;
3490
+ this.usage.input = 0;
3491
+ this.usage.output = 0;
3492
+ this.usage.totalTokens = 0;
3493
+ this.usage.cost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
3494
+ delete this.usage.cacheRead;
3495
+ delete this.usage.cacheWrite;
3496
+ delete this.usage.contextPercent;
3497
+ delete this.usage.credits;
3498
+ delete this.usage.creditUnit;
3145
3499
  }
3146
3500
  /** Hand over the events buffered so far. */
3147
3501
  takeEvents() {
@@ -3218,11 +3572,17 @@ var KiroResponseAssembler = class {
3218
3572
  break;
3219
3573
  }
3220
3574
  case "usage": {
3221
- this.usageEvent = event.data;
3575
+ this.usageEvent = { ...this.usageEvent ?? {}, ...event.data };
3222
3576
  if (debugEnabled())
3223
3577
  debugLog("response.usageRaw", payload);
3224
3578
  break;
3225
3579
  }
3580
+ case "metering": {
3581
+ this.meteringEvent = event.data;
3582
+ if (debugEnabled())
3583
+ debugLog("stream.metering", [event.data]);
3584
+ break;
3585
+ }
3226
3586
  }
3227
3587
  }
3228
3588
  /**
@@ -3230,8 +3590,12 @@ var KiroResponseAssembler = class {
3230
3590
  * text-dialect recovery and echo-stripping passes.
3231
3591
  */
3232
3592
  endTurn() {
3233
- if (this.currentToolCall && this.emitToolCall(this.currentToolCall))
3234
- this.emittedToolCalls++;
3593
+ if (this.currentToolCall) {
3594
+ if (this.emitToolCall(this.currentToolCall))
3595
+ this.emittedToolCalls++;
3596
+ else
3597
+ this.droppedToolCalls.push(this.currentToolCall.name);
3598
+ }
3235
3599
  this.currentToolCall = null;
3236
3600
  this.endNativeThinking();
3237
3601
  if (this.thinkingParser) {
@@ -3242,13 +3606,16 @@ var KiroResponseAssembler = class {
3242
3606
  this.stripEchoNoise();
3243
3607
  const responseText = this.textBlockIndex === null ? "" : this.blocks.getText(this.textBlockIndex);
3244
3608
  const hasText2 = responseText.length > 0;
3609
+ this.stopReason = !this.receivedContextUsage && this.emittedToolCalls === 0 ? "length" : this.emittedToolCalls > 0 ? "toolUse" : "stop";
3245
3610
  return {
3246
3611
  responseText,
3247
3612
  hasText: hasText2,
3248
3613
  sawAnyToolCalls: this.sawAnyToolCalls,
3249
3614
  emittedToolCalls: this.emittedToolCalls,
3250
3615
  isEchoLoop: hasText2 && !this.sawAnyToolCalls && ECHO_NOISE_PATTERN.test(responseText),
3251
- isEmpty: !hasText2 && !this.sawAnyToolCalls
3616
+ isEmpty: !hasText2 && !this.sawAnyToolCalls,
3617
+ stopReason: this.stopReason,
3618
+ droppedToolCalls: this.droppedToolCalls
3252
3619
  };
3253
3620
  }
3254
3621
  /** Drop an echo the caller decided not to retry, so it is not read as a continuation signal. */
@@ -3256,6 +3623,10 @@ var KiroResponseAssembler = class {
3256
3623
  if (this.textBlockIndex !== null)
3257
3624
  this.blocks.setText(this.textBlockIndex, "");
3258
3625
  }
3626
+ /** Kinds of the blocks this attempt produced, for the exhaustion diagnostic. */
3627
+ contentKinds() {
3628
+ return this.blocks.kinds();
3629
+ }
3259
3630
  /** Close the text block and settle usage and the stop reason. */
3260
3631
  complete() {
3261
3632
  if (this.textBlockIndex !== null)
@@ -3263,17 +3634,17 @@ var KiroResponseAssembler = class {
3263
3634
  if (this.usageEvent?.inputTokens !== void 0)
3264
3635
  this.usage.input = this.usageEvent.inputTokens;
3265
3636
  this.usage.output = this.usageEvent?.outputTokens ?? countTokens(this.totalContent);
3266
- this.usage.totalTokens = this.usage.input + this.usage.output;
3267
- if (this.usageEvent?.cacheReadTokens !== void 0)
3268
- this.usage.cacheRead = this.usageEvent.cacheReadTokens;
3269
- if (this.usageEvent?.cacheWriteTokens !== void 0)
3270
- this.usage.cacheWrite = this.usageEvent.cacheWriteTokens;
3271
- if (this.usageEvent?.credits !== void 0)
3272
- this.usage.credits = this.usageEvent.credits;
3273
- if (this.usageEvent?.creditUnit !== void 0)
3274
- this.usage.creditUnit = this.usageEvent.creditUnit;
3637
+ this.usage.totalTokens = this.usageEvent?.totalTokens ?? this.usage.input + (this.usageEvent?.cacheReadInputTokens ?? 0) + (this.usageEvent?.cacheWriteInputTokens ?? 0) + this.usage.output;
3638
+ if (this.usageEvent?.cacheReadInputTokens !== void 0)
3639
+ this.usage.cacheRead = this.usageEvent.cacheReadInputTokens;
3640
+ if (this.usageEvent?.cacheWriteInputTokens !== void 0)
3641
+ this.usage.cacheWrite = this.usageEvent.cacheWriteInputTokens;
3642
+ if (this.meteringEvent?.credits !== void 0)
3643
+ this.usage.credits = this.meteringEvent.credits;
3644
+ if (this.meteringEvent?.unit !== void 0)
3645
+ this.usage.creditUnit = this.meteringEvent.unit;
3275
3646
  this.usage.cost = calculateKiroCost(this.model.cost, this.usage);
3276
- const stopReason = !this.receivedContextUsage && this.emittedToolCalls === 0 ? "length" : this.emittedToolCalls > 0 ? "toolUse" : "stop";
3647
+ const stopReason = this.stopReason;
3277
3648
  debugLog("response.done", {
3278
3649
  stopReason,
3279
3650
  receivedContextUsage: this.receivedContextUsage,
@@ -3282,7 +3653,12 @@ var KiroResponseAssembler = class {
3282
3653
  textLen: this.textBlockIndex === null ? 0 : this.blocks.getText(this.textBlockIndex).length,
3283
3654
  usage: this.usage
3284
3655
  });
3285
- return { stopReason, usage: this.usage };
3656
+ return {
3657
+ stopReason,
3658
+ usage: this.usage,
3659
+ wireUsage: this.usageEvent,
3660
+ metering: this.meteringEvent
3661
+ };
3286
3662
  }
3287
3663
  ensureNativeThinkingBlock() {
3288
3664
  if (this.nativeThinkingBlockIndex === null)
@@ -3317,6 +3693,8 @@ var KiroResponseAssembler = class {
3317
3693
  return;
3318
3694
  if (this.emitToolCall(this.currentToolCall))
3319
3695
  this.emittedToolCalls++;
3696
+ else
3697
+ this.droppedToolCalls.push(this.currentToolCall.name);
3320
3698
  this.currentToolCall = null;
3321
3699
  }
3322
3700
  /**
@@ -3358,6 +3736,8 @@ var KiroResponseAssembler = class {
3358
3736
  for (const call of recovered) {
3359
3737
  if (this.emitToolCall({ toolUseId: call.toolUseId, name: call.name, input: JSON.stringify(call.arguments) })) {
3360
3738
  this.emittedToolCalls++;
3739
+ } else {
3740
+ this.droppedToolCalls.push(call.name);
3361
3741
  }
3362
3742
  }
3363
3743
  }
@@ -4117,6 +4497,7 @@ function readKiroEventStream(body, options) {
4117
4497
  const outcome = { firstTokenTimedOut: false, idleTimedOut: false, error: null };
4118
4498
  const idleTimeoutMs = options.idleTimeoutMs ?? IDLE_TIMEOUT;
4119
4499
  const bodyReader = body.getReader();
4500
+ const callerSignal = options.signal;
4120
4501
  async function* frames() {
4121
4502
  let idleTimer = null;
4122
4503
  const resetIdle = () => {
@@ -4128,6 +4509,14 @@ function readKiroEventStream(body, options) {
4128
4509
  });
4129
4510
  }, idleTimeoutMs);
4130
4511
  };
4512
+ const onCallerStreamAbort = () => {
4513
+ void bodyReader.cancel().catch(() => {
4514
+ });
4515
+ };
4516
+ if (callerSignal?.aborted)
4517
+ onCallerStreamAbort();
4518
+ else
4519
+ callerSignal?.addEventListener("abort", onCallerStreamAbort, { once: true });
4131
4520
  const bodyIterable = {
4132
4521
  async *[Symbol.asyncIterator]() {
4133
4522
  try {
@@ -4148,6 +4537,26 @@ function readKiroEventStream(body, options) {
4148
4537
  if (!entry)
4149
4538
  throw new Error("Received an empty event stream message");
4150
4539
  const [key, msg] = entry;
4540
+ if (msg.headers[":message-type"]?.value === "exception") {
4541
+ let parsedException = {};
4542
+ try {
4543
+ const decoded = JSON.parse(utf8Decoder.decode(msg.body));
4544
+ if (decoded && typeof decoded === "object")
4545
+ parsedException = decoded;
4546
+ } catch {
4547
+ }
4548
+ const data = parseKiroExceptionFrame(key, parsedException) ?? {
4549
+ error: key,
4550
+ kind: "unknown",
4551
+ ...typeof parsedException.message === "string" ? { message: parsedException.message } : {},
4552
+ ...typeof parsedException.reason === "string" ? { reason: parsedException.reason } : {},
4553
+ ...typeof parsedException.retryAfterMilliseconds === "number" ? { retryAfterMilliseconds: parsedException.retryAfterMilliseconds } : {}
4554
+ };
4555
+ const error2 = new Error(data.message ? `${data.error}: ${data.message}` : data.error);
4556
+ error2.name = data.error;
4557
+ error2.kiroError = data;
4558
+ return { [key]: error2 };
4559
+ }
4151
4560
  const parsed = JSON.parse(utf8Decoder.decode(msg.body));
4152
4561
  return { [key]: parsed };
4153
4562
  });
@@ -4156,29 +4565,43 @@ function readKiroEventStream(body, options) {
4156
4565
  const FIRST_TOKEN_SENTINEL = /* @__PURE__ */ Symbol("firstTokenTimeout");
4157
4566
  try {
4158
4567
  while (true) {
4568
+ if (callerSignal?.aborted) {
4569
+ throw callerSignal.reason ?? new Error("Request aborted");
4570
+ }
4159
4571
  let iterResult;
4160
4572
  try {
4161
4573
  if (!gotFirstToken) {
4162
4574
  const readPromise = iterator.next();
4163
- const result = await Promise.race([
4164
- readPromise,
4165
- new Promise((resolve) => setTimeout(() => resolve(FIRST_TOKEN_SENTINEL), options.firstTokenTimeoutMs))
4166
- ]);
4167
- if (result === FIRST_TOKEN_SENTINEL) {
4168
- readPromise.catch(() => {
4169
- });
4170
- void bodyReader.cancel().catch(() => {
4171
- });
4172
- outcome.firstTokenTimedOut = true;
4173
- return;
4575
+ let firstTokenTimer;
4576
+ try {
4577
+ const result = await Promise.race([
4578
+ readPromise,
4579
+ new Promise((resolve) => {
4580
+ firstTokenTimer = setTimeout(() => resolve(FIRST_TOKEN_SENTINEL), options.firstTokenTimeoutMs);
4581
+ })
4582
+ ]);
4583
+ if (result === FIRST_TOKEN_SENTINEL) {
4584
+ readPromise.catch(() => {
4585
+ });
4586
+ void bodyReader.cancel().catch(() => {
4587
+ });
4588
+ outcome.firstTokenTimedOut = true;
4589
+ return;
4590
+ }
4591
+ iterResult = result;
4592
+ gotFirstToken = true;
4593
+ resetIdle();
4594
+ } finally {
4595
+ if (firstTokenTimer !== void 0)
4596
+ clearTimeout(firstTokenTimer);
4174
4597
  }
4175
- iterResult = result;
4176
- gotFirstToken = true;
4177
- resetIdle();
4178
4598
  } else {
4179
4599
  iterResult = await iterator.next();
4180
4600
  }
4181
4601
  } catch (e) {
4602
+ const kiroError = e?.kiroError;
4603
+ if (kiroError)
4604
+ outcome.errorData = kiroError;
4182
4605
  outcome.error = e instanceof Error ? e.message : (typeof e === "object" && e !== null ? JSON.stringify(e) : String(e)) || "Unknown stream error";
4183
4606
  return;
4184
4607
  }
@@ -4186,148 +4609,40 @@ function readKiroEventStream(body, options) {
4186
4609
  if (done)
4187
4610
  return;
4188
4611
  resetIdle();
4189
- const payload = Object.values(value)[0];
4190
- const event = parseKiroEvent(payload);
4612
+ const frameEntry = Object.entries(value)[0];
4613
+ if (!frameEntry)
4614
+ continue;
4615
+ const [frameKey, framePayload] = frameEntry;
4616
+ const event = parseKiroEvent(frameKey, framePayload ?? {});
4191
4617
  if (!event)
4192
4618
  continue;
4619
+ if (event.type === "ignored") {
4620
+ if (debugEnabled())
4621
+ debugLog("stream.events.ignored", [event.data.key]);
4622
+ continue;
4623
+ }
4193
4624
  if (debugEnabled())
4194
4625
  debugLog("stream.events", [event]);
4195
4626
  if (event.type === "error") {
4196
4627
  outcome.error = event.data.message ? `${event.data.error}: ${event.data.message}` : event.data.error;
4628
+ outcome.errorData = event.data;
4197
4629
  void bodyReader.cancel().catch(() => {
4198
4630
  });
4199
4631
  return;
4200
4632
  }
4201
- yield { event, payload };
4633
+ yield { event, payload: framePayload };
4202
4634
  }
4203
4635
  } finally {
4204
4636
  if (idleTimer)
4205
4637
  clearTimeout(idleTimer);
4638
+ callerSignal?.removeEventListener("abort", onCallerStreamAbort);
4206
4639
  }
4207
4640
  }
4208
4641
  return { frames: frames(), outcome };
4209
4642
  }
4210
4643
 
4211
- // ../kiro-core/dist/retry.js
4212
- init_models();
4213
- var FIRST_TOKEN_TIMEOUT = 9e4;
4214
- var REQUEST_HEADER_TIMEOUT = 9e4;
4215
- function firstTokenTimeoutForModel(modelId) {
4216
- if (retryConfig.firstTokenTimeoutMs !== FIRST_TOKEN_TIMEOUT) {
4217
- return retryConfig.firstTokenTimeoutMs;
4218
- }
4219
- const model = kiroModels.find((m) => m.id === modelId);
4220
- return model?.firstTokenTimeout ?? FIRST_TOKEN_TIMEOUT;
4221
- }
4222
- var retryConfig = {
4223
- firstTokenTimeoutMs: FIRST_TOKEN_TIMEOUT,
4224
- requestHeaderTimeoutMs: REQUEST_HEADER_TIMEOUT
4225
- };
4226
- function exponentialBackoff(attempt, baseMs, maxMs) {
4227
- return Math.min(baseMs * 2 ** attempt, maxMs);
4228
- }
4229
- var MAX_RETRY_DELAY = 1e4;
4230
- var REQUEST_RATE_FALLBACK_DELAY_MS = 1e4;
4231
- function extractKiroReason(errorText) {
4232
- if (!errorText)
4233
- return void 0;
4234
- try {
4235
- const parsed = JSON.parse(errorText);
4236
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
4237
- return void 0;
4238
- const reason = parsed.reason;
4239
- return typeof reason === "string" && reason.length > 0 ? reason : void 0;
4240
- } catch {
4241
- return void 0;
4242
- }
4243
- }
4244
- function parseRetryAfterMs(headers, nowMs = Date.now()) {
4245
- const get = headers?.get?.bind(headers);
4246
- if (!get)
4247
- return void 0;
4248
- const milliseconds = nonNegativeNumber(get("retry-after-ms"));
4249
- if (milliseconds !== void 0)
4250
- return Math.round(milliseconds);
4251
- const retryAfter = get("retry-after");
4252
- if (retryAfter !== null && retryAfter.trim() !== "") {
4253
- const seconds = Number(retryAfter);
4254
- if (Number.isFinite(seconds)) {
4255
- if (seconds >= 0) {
4256
- const delayMs = seconds * 1e3;
4257
- if (Number.isFinite(delayMs))
4258
- return Math.round(delayMs);
4259
- }
4260
- } else {
4261
- const dateMs = Date.parse(retryAfter);
4262
- if (!Number.isNaN(dateMs))
4263
- return Math.max(0, dateMs - nowMs);
4264
- }
4265
- }
4266
- const resetAfterSeconds = nonNegativeNumber(get("x-ratelimit-reset-after"));
4267
- if (resetAfterSeconds !== void 0) {
4268
- const delayMs = resetAfterSeconds * 1e3;
4269
- if (Number.isFinite(delayMs))
4270
- return Math.round(delayMs);
4271
- }
4272
- return void 0;
4273
- }
4274
- function resolveRequestRateRetryDelay(headers, nowMs = Date.now()) {
4275
- const advertisedDelayMs = parseRetryAfterMs(headers, nowMs);
4276
- const requestedDelayMs = advertisedDelayMs ?? REQUEST_RATE_FALLBACK_DELAY_MS;
4277
- return {
4278
- delayMs: Math.min(requestedDelayMs, MAX_RETRY_DELAY),
4279
- ...advertisedDelayMs !== void 0 ? { advertisedDelayMs } : {},
4280
- capped: requestedDelayMs > MAX_RETRY_DELAY
4281
- };
4282
- }
4283
- function nonNegativeNumber(value) {
4284
- if (value === null || value.trim() === "")
4285
- return void 0;
4286
- const parsed = Number(value);
4287
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
4288
- }
4289
- var KIRO_REASON_CODES = Object.freeze({
4290
- /** Request body exceeded the service's size threshold. */
4291
- CONTENT_LENGTH_EXCEEDS_THRESHOLD: "CONTENT_LENGTH_EXCEEDS_THRESHOLD",
4292
- /** Prose-only size rejection; the service sends no reason code for this one. */
4293
- INPUT_TOO_LONG: "Input is too long",
4294
- /** Monthly request quota exhausted — hard limit, not transient. */
4295
- MONTHLY_REQUEST_COUNT: "MONTHLY_REQUEST_COUNT",
4296
- /** Model capacity temporarily unavailable — transient, worth retrying. */
4297
- INSUFFICIENT_MODEL_CAPACITY: "INSUFFICIENT_MODEL_CAPACITY",
4298
- /** Short-window request throttle — retry only within the provider budget. */
4299
- USER_REQUEST_RATE_EXCEEDED: "USER_REQUEST_RATE_EXCEEDED",
4300
- /**
4301
- * Generic request-validation rejection, returned for a malformed body of any
4302
- * size (empty `content`, history referencing tools absent from the catalog).
4303
- * Not a size signal: classifying it as "too big" makes the caller compact a
4304
- * history that was never the problem, a loop it can never satisfy.
4305
- */
4306
- REQUEST_BODY_INVALID: "REQUEST_BODY_INVALID"
4307
- });
4308
- var TOO_BIG_PATTERNS = Object.freeze([
4309
- KIRO_REASON_CODES.CONTENT_LENGTH_EXCEEDS_THRESHOLD,
4310
- KIRO_REASON_CODES.INPUT_TOO_LONG
4311
- ]);
4312
- var NON_RETRYABLE_BODY_PATTERNS = Object.freeze([KIRO_REASON_CODES.MONTHLY_REQUEST_COUNT]);
4313
- var CAPACITY_PATTERN = KIRO_REASON_CODES.INSUFFICIENT_MODEL_CAPACITY;
4314
- var CAPACITY_MAX_RETRIES = 3;
4315
- var CAPACITY_BASE_DELAY_MS = 5e3;
4316
- var capacityRetryConfig = {
4317
- maxRetries: CAPACITY_MAX_RETRIES,
4318
- baseDelayMs: CAPACITY_BASE_DELAY_MS
4319
- };
4320
- function isTooBigError(status, errorText) {
4321
- return status === 413 || status === 400 && TOO_BIG_PATTERNS.some((p) => errorText.includes(p));
4322
- }
4323
- function isNonRetryableBodyError(errorText) {
4324
- return NON_RETRYABLE_BODY_PATTERNS.some((p) => errorText.includes(p));
4325
- }
4326
- function isCapacityError(errorText) {
4327
- return errorText.includes(CAPACITY_PATTERN);
4328
- }
4329
-
4330
4644
  // ../kiro-core/dist/stream.js
4645
+ import { createHash as createHash3 } from "node:crypto";
4331
4646
  init_debug();
4332
4647
  init_effort();
4333
4648
  init_endpoints();
@@ -4400,7 +4715,64 @@ function createResponseHeaderDeadline(callerSignal, timeoutMs) {
4400
4715
  };
4401
4716
  }
4402
4717
 
4718
+ // ../kiro-core/dist/usage-tracking.js
4719
+ var DEFAULT_USD_PER_CREDIT = 0.04;
4720
+ var DEFAULT_ESTIMATED_CACHE_TIMEOUT_MS = 5 * 60 * 1e3;
4721
+ var KIRO_USAGE_TRACKING_DISABLED = Object.freeze({
4722
+ estimateDollarValue: false,
4723
+ usdPerCredit: DEFAULT_USD_PER_CREDIT,
4724
+ estimateCacheUsage: false,
4725
+ estimatedCacheTimeout: DEFAULT_ESTIMATED_CACHE_TIMEOUT_MS
4726
+ });
4727
+ var CREDIT_UNITS = /* @__PURE__ */ new Set(["credit", "credits"]);
4728
+ function estimateKiroCreditCost(tracking, metering) {
4729
+ if (!tracking.estimateDollarValue || !metering)
4730
+ return void 0;
4731
+ if (typeof metering.unit !== "string" || !CREDIT_UNITS.has(metering.unit.toLowerCase()))
4732
+ return void 0;
4733
+ const { credits } = metering;
4734
+ if (typeof credits !== "number" || !Number.isFinite(credits) || credits < 0)
4735
+ return void 0;
4736
+ return credits * tracking.usdPerCredit;
4737
+ }
4738
+
4403
4739
  // ../kiro-core/dist/stream.js
4740
+ function describeAttempts(count) {
4741
+ return count === 1 ? "1 attempt" : `${count} attempts`;
4742
+ }
4743
+ var DIAGNOSTIC_QUOTE_LIMIT = 200;
4744
+ function clampForDiagnostic(text) {
4745
+ return text.length <= DIAGNOSTIC_QUOTE_LIMIT ? text : `${text.slice(0, DIAGNOSTIC_QUOTE_LIMIT)}\u2026 (truncated)`;
4746
+ }
4747
+ function encodeBytesForDiagnostic(bytes) {
4748
+ let encoded = "";
4749
+ for (const byte of bytes) {
4750
+ encoded += String.fromCharCode(65 + (byte >> 4), 65 + (byte & 15));
4751
+ }
4752
+ return encoded;
4753
+ }
4754
+ function encodeToolNameForDiagnostic(name) {
4755
+ const identity = typeof name === "string" ? name : `${typeof name}:${JSON.stringify(name)}`;
4756
+ let encoded = "";
4757
+ for (let i = 0; i < identity.length; i++) {
4758
+ const codeUnit = identity.charCodeAt(i);
4759
+ encoded += String.fromCharCode(65 + (codeUnit >> 12), 65 + (codeUnit >> 8 & 15), 65 + (codeUnit >> 4 & 15), 65 + (codeUnit & 15));
4760
+ }
4761
+ return encoded;
4762
+ }
4763
+ function describeDroppedToolNames(names) {
4764
+ const encoded = names.map((name) => `A-P:${encodeToolNameForDiagnostic(name)}`).join(", ");
4765
+ if (encoded.length <= DIAGNOSTIC_QUOTE_LIMIT)
4766
+ return encoded;
4767
+ const digest = createHash3("sha256").update(JSON.stringify(names)).digest();
4768
+ return `A-P-DIGEST:${encodeBytesForDiagnostic(digest)} (tool identities fingerprinted)`;
4769
+ }
4770
+ function describeReturnedContent(kinds) {
4771
+ const unique = [...new Set(kinds)].sort();
4772
+ if (unique.length === 0)
4773
+ return "returning empty content";
4774
+ return `returning only ${unique.join(" and ")} content`;
4775
+ }
4404
4776
  var skipProfileResolutionForTests = false;
4405
4777
  var TEST_PROFILE_ARN = "arn:aws:codewhisperer:us-east-1:000000000000:profile/test";
4406
4778
  async function* streamKiro(request) {
@@ -4411,7 +4783,6 @@ async function* streamKiro(request) {
4411
4783
  }
4412
4784
  let accessToken = initialAccessToken;
4413
4785
  const region = model.region ?? "us-east-1";
4414
- const endpoint = new URL("generateAssistantResponse", getKiroEndpoints(region).runtime).toString();
4415
4786
  let managementAuth = { accessToken, region };
4416
4787
  const cliCreds = getKiroCliCredentials() ?? getKiroCliCredentialsAllowExpired();
4417
4788
  const cliProfileArn = cliCreds?.access === accessToken ? cliCreds.profileArn : void 0;
@@ -4430,9 +4801,11 @@ async function* streamKiro(request) {
4430
4801
  managementAuth = { accessToken, region };
4431
4802
  profileArn = freshCreds.profileArn || (skipProfileResolutionForTests ? TEST_PROFILE_ARN : await resolveKiroProfileArn(managementAuth));
4432
4803
  }
4433
- if (!process.env.VITEST && isCacheStale(region)) {
4434
- updateKiroModelsCache(accessToken, region, profileArn).catch((error2) => {
4435
- console.warn(`[kiro-core] Failed to refresh Kiro model catalog in ${region}: ${formatSafeError(error2)}`);
4804
+ let runtimeRegion = getKiroRegionFromProfileArn(profileArn) ?? region;
4805
+ let endpoint = new URL("generateAssistantResponse", getKiroEndpoints(runtimeRegion).runtime).toString();
4806
+ if (!process.env.VITEST && isCacheStale(runtimeRegion)) {
4807
+ updateKiroModelsCache(accessToken, runtimeRegion, profileArn).catch((error2) => {
4808
+ console.warn(`[kiro-core] Failed to refresh Kiro model catalog in ${runtimeRegion}: ${formatSafeError(error2)}`);
4436
4809
  });
4437
4810
  }
4438
4811
  const kiroModelId = resolveKiroModel(model.id, model.kiroModelId);
@@ -4461,8 +4834,13 @@ async function* streamKiro(request) {
4461
4834
  ${systemPrompt}` : ""}`;
4462
4835
  }
4463
4836
  const assembler = new KiroResponseAssembler(model, thinkingEnabled);
4837
+ const usageTracking = request.usageTracking ?? KIRO_USAGE_TRACKING_DISABLED;
4464
4838
  let retryCount = 0;
4465
4839
  const maxRetries = 3;
4840
+ let emptyAttempts = 0;
4841
+ let echoAttempts = 0;
4842
+ let credentialRefreshTotal = 0;
4843
+ let capacityRetryTotal = 0;
4466
4844
  const conversationId = request.sessionId ?? crypto.randomUUID();
4467
4845
  requestLoop: while (retryCount <= maxRetries) {
4468
4846
  if (signal?.aborted)
@@ -4545,6 +4923,7 @@ ${systemPrompt}` : ""}`;
4545
4923
  });
4546
4924
  if (isCapacityError(errText) && capacityRetryCount < capacityRetryConfig.maxRetries) {
4547
4925
  capacityRetryCount++;
4926
+ capacityRetryTotal++;
4548
4927
  const delayMs = exponentialBackoff(capacityRetryCount - 1, capacityRetryConfig.baseDelayMs, 3e4);
4549
4928
  logCapacityEvent(`INSUFFICIENT_MODEL_CAPACITY \u2014 retrying in ${delayMs}ms (${capacityRetryCount}/${capacityRetryConfig.maxRetries})`);
4550
4929
  await abortableDelay(delayMs, signal);
@@ -4572,6 +4951,7 @@ ${systemPrompt}` : ""}`;
4572
4951
  }
4573
4952
  if (response.status === 403 && !isCapacityError(errText) && retryCount < maxRetries) {
4574
4953
  retryCount++;
4954
+ credentialRefreshTotal++;
4575
4955
  invalidateKiroProfileArn(managementAuth);
4576
4956
  const rejectedAccessToken = accessToken;
4577
4957
  const rejectedProfileArn = profileArn;
@@ -4583,16 +4963,23 @@ ${systemPrompt}` : ""}`;
4583
4963
  managementAuth = { accessToken, region };
4584
4964
  const inheritedDesktopProfileArn = rejectedCliCreds?.authMethod === "desktop" && freshCreds?.authMethod === "desktop" ? rejectedProfileArn : void 0;
4585
4965
  profileArn = freshCreds?.profileArn || inheritedDesktopProfileArn || (skipProfileResolutionForTests ? TEST_PROFILE_ARN : await resolveKiroProfileArn(managementAuth));
4966
+ runtimeRegion = getKiroRegionFromProfileArn(profileArn) ?? region;
4967
+ endpoint = new URL("generateAssistantResponse", getKiroEndpoints(runtimeRegion).runtime).toString();
4586
4968
  await abortableDelay(exponentialBackoff(retryCount - 1, 500, MAX_RETRY_DELAY), signal);
4587
4969
  break;
4588
4970
  }
4971
+ const errorMeta = {
4972
+ reasonCode: extractKiroReasonCode(errText),
4973
+ retryAfterMs: parseRetryAfterMs(response.headers),
4974
+ providerAttempts: { credentialRefresh: credentialRefreshTotal, capacity: capacityRetryTotal }
4975
+ };
4589
4976
  if (isNonRetryableBodyError(errText) || isCapacityError(errText)) {
4590
- throw new Error(`Kiro API error: ${errText || safeStatusText}`);
4977
+ throw new KiroApiError(`Kiro API error: ${errText || safeStatusText}`, response.status, errorMeta.reasonCode, errorMeta.retryAfterMs, errorMeta.providerAttempts);
4591
4978
  }
4592
4979
  if (isTooBigError(response.status, errText)) {
4593
- throw new Error(`Kiro API error: context_length_exceeded (${response.status} ${errText})`);
4980
+ throw new KiroApiError(`Kiro API error: context_length_exceeded (${response.status} ${errText})`, response.status, errorMeta.reasonCode, errorMeta.retryAfterMs, errorMeta.providerAttempts);
4594
4981
  }
4595
- throw new Error(`Kiro API error: ${response.status} ${safeStatusText} ${errText}`);
4982
+ throw new KiroApiError(`Kiro API error: ${response.status} ${safeStatusText} ${errText}`, response.status, errorMeta.reasonCode, errorMeta.retryAfterMs, errorMeta.providerAttempts);
4596
4983
  }
4597
4984
  break;
4598
4985
  }
@@ -4606,7 +4993,8 @@ ${systemPrompt}` : ""}`;
4606
4993
  throw new Error("No response body");
4607
4994
  assembler.beginAttempt();
4608
4995
  const { frames, outcome } = readKiroEventStream(response.body, {
4609
- firstTokenTimeoutMs: model.firstTokenTimeout ?? firstTokenTimeoutForModel(model.id)
4996
+ firstTokenTimeoutMs: model.firstTokenTimeout ?? firstTokenTimeoutForModel(model.id),
4997
+ signal
4610
4998
  });
4611
4999
  for await (const frame of frames) {
4612
5000
  assembler.handle(frame);
@@ -4616,6 +5004,11 @@ ${systemPrompt}` : ""}`;
4616
5004
  if (outcome.firstTokenTimedOut || outcome.idleTimedOut || outcome.error) {
4617
5005
  if (retryCount < maxRetries) {
4618
5006
  retryCount++;
5007
+ if (outcome.errorData && debugEnabled()) {
5008
+ debugLog("stream.error.typed", [outcome.errorData]);
5009
+ }
5010
+ assembler.discard();
5011
+ yield* assembler.takeEvents();
4619
5012
  await abortableDelay(exponentialBackoff(retryCount - 1, 1e3, MAX_RETRY_DELAY), signal);
4620
5013
  continue;
4621
5014
  }
@@ -4625,7 +5018,13 @@ ${systemPrompt}` : ""}`;
4625
5018
  }
4626
5019
  const summary = assembler.endTurn();
4627
5020
  yield* assembler.takeEvents();
4628
- if (summary.isEmpty || summary.isEchoLoop) {
5021
+ const degenerate = summary.isEmpty || summary.isEchoLoop;
5022
+ if (summary.isEchoLoop)
5023
+ echoAttempts++;
5024
+ else if (degenerate)
5025
+ emptyAttempts++;
5026
+ let errorMessage;
5027
+ if (degenerate) {
4629
5028
  const mayRetry = retryCount < maxRetries && (!summary.isEchoLoop || request.canDiscardEmittedBlocks === true);
4630
5029
  if (mayRetry) {
4631
5030
  retryCount++;
@@ -4637,15 +5036,39 @@ ${systemPrompt}` : ""}`;
4637
5036
  }
4638
5037
  if (summary.isEchoLoop) {
4639
5038
  assembler.stripEcho();
4640
- console.warn(`[kiro-core] Echo loop \u2014 stripping "Continue" response`);
5039
+ const alsoEmpty = emptyAttempts > 0 ? ` (plus ${describeAttempts(emptyAttempts)} with no text at all)` : "";
5040
+ console.warn(`[kiro-core] Echo loop persisted across ${describeAttempts(echoAttempts)}${alsoEmpty} \u2014 stripping "Continue" response (${summary.responseText.length} chars)`);
5041
+ errorMessage = `Kiro model echoed its own continuation prompt (${JSON.stringify(clampForDiagnostic(summary.responseText))}) on ${describeAttempts(echoAttempts)}${alsoEmpty} and emitted no tool calls; retry budget exhausted, text stripped, stopReason:"${summary.stopReason}"`;
4641
5042
  } else {
4642
- console.warn(`[kiro-core] Empty response after ${maxRetries} retries \u2014 giving up on this turn`);
5043
+ const alsoEchoed = echoAttempts > 0 ? ` (plus ${describeAttempts(echoAttempts)} that echoed the continuation prompt)` : "";
5044
+ console.warn(`[kiro-core] Empty response on ${describeAttempts(emptyAttempts)}${alsoEchoed}, retry budget exhausted \u2014 returning stopReason:"${summary.stopReason}" to avoid agent loop stall`);
5045
+ errorMessage = `Kiro returned no text and no tool calls on ${describeAttempts(emptyAttempts)}${alsoEchoed}; retry budget exhausted, ${describeReturnedContent(assembler.contentKinds())} with stopReason:"${summary.stopReason}"`;
4643
5046
  }
4644
5047
  }
4645
- const { stopReason, usage } = assembler.complete();
5048
+ if (summary.droppedToolCalls.length > 0) {
5049
+ const names = describeDroppedToolNames(summary.droppedToolCalls);
5050
+ const one = summary.droppedToolCalls.length === 1;
5051
+ const dropDiagnostic = `Kiro sent ${one ? "a tool call" : "tool calls"} with unparseable arguments (${names}); ${one ? "it was" : "they were"} dropped and never reached the agent, stopReason:"${summary.stopReason}"`;
5052
+ errorMessage = errorMessage ? `${errorMessage}. ${dropDiagnostic}` : dropDiagnostic;
5053
+ }
5054
+ const { stopReason, usage, wireUsage, metering } = assembler.complete();
4646
5055
  yield* assembler.takeEvents();
5056
+ if (!errorMessage) {
5057
+ const estimatedRead = applyCacheEstimate(conversationId, usage, wireUsage, usageTracking);
5058
+ if (estimatedRead > 0) {
5059
+ debugLog("usage.estimate", {
5060
+ conversationId,
5061
+ estimatedRead,
5062
+ input: usage.input,
5063
+ cacheRead: usage.cacheRead
5064
+ });
5065
+ }
5066
+ const estimatedCost = estimateKiroCreditCost(usageTracking, metering);
5067
+ if (estimatedCost !== void 0)
5068
+ usage.cost.total = estimatedCost;
5069
+ }
4647
5070
  yield { type: "usage", usage };
4648
- yield { type: "done", stopReason };
5071
+ yield { type: "done", stopReason, ...errorMessage ? { errorMessage } : {} };
4649
5072
  return;
4650
5073
  }
4651
5074
  }
@@ -5023,10 +5446,20 @@ function streamKiroForOmp(model, context, options) {
5023
5446
  if (event.usage.contextPercent !== void 0) {
5024
5447
  output.usage.contextPercent = event.usage.contextPercent;
5025
5448
  }
5449
+ if (event.usage.credits !== void 0) {
5450
+ output.usage.credits = event.usage.credits;
5451
+ }
5452
+ if (event.usage.creditUnit !== void 0) {
5453
+ output.usage.creditUnit = event.usage.creditUnit;
5454
+ }
5455
+ if (event.usage.cacheEstimated !== void 0) {
5456
+ output.usage.cacheEstimated = event.usage.cacheEstimated;
5457
+ }
5026
5458
  break;
5027
5459
  }
5028
5460
  case "done":
5029
5461
  output.stopReason = event.stopReason;
5462
+ if (event.errorMessage) output.errorMessage = event.errorMessage;
5030
5463
  stream.push({ type: "done", reason: event.stopReason, message: output });
5031
5464
  break;
5032
5465
  }