ns-omp-provider-kiro 0.2.3 → 0.3.1

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 +1473 -1024
  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",
@@ -285,6 +292,7 @@ __export(kiro_cli_exports, {
285
292
  getKiroCliCredentials: () => getKiroCliCredentials,
286
293
  getKiroCliCredentialsAllowExpired: () => getKiroCliCredentialsAllowExpired,
287
294
  getKiroCliDbPath: () => getKiroCliDbPath,
295
+ getKiroCliExternalIdpCredentials: () => getKiroCliExternalIdpCredentials,
288
296
  getKiroCliModelRates: () => getKiroCliModelRates,
289
297
  getKiroCliSocialToken: () => getKiroCliSocialToken,
290
298
  getKiroCliSocialTokenAllowExpired: () => getKiroCliSocialTokenAllowExpired,
@@ -308,6 +316,16 @@ function getKiroCliDbPath() {
308
316
  dbPath = join2(homedir2(), ".local", "share", "kiro-cli", "data.sqlite3");
309
317
  return existsSync(dbPath) ? dbPath : void 0;
310
318
  }
319
+ function getKiroCliExternalIdpCredentials() {
320
+ const dbPath = getKiroCliDbPath();
321
+ if (!dbPath)
322
+ return void 0;
323
+ try {
324
+ return tryKiroCliToken(dbPath, EXTERNAL_IDP_TOKEN_KEY, "external-idp", true);
325
+ } catch {
326
+ return void 0;
327
+ }
328
+ }
311
329
  function getNodeSqlite() {
312
330
  try {
313
331
  return require2("node:sqlite");
@@ -642,778 +660,317 @@ var init_kiro_ide = __esm({
642
660
  }
643
661
  });
644
662
 
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));
663
+ // ../kiro-core/dist/oauth.js
664
+ function kiroUserAgent(service, sdkVersion) {
665
+ return {
666
+ "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`,
667
+ "amz-sdk-invocation-id": "00000000-0000-0000-0000-000000000000",
668
+ "amz-sdk-request": `attempt=1; max=${sdkVersion}`
669
+ };
674
670
  }
675
- function isEffortMap(value) {
676
- return isRecord2(value) && Object.values(value).every((mapped) => typeof mapped === "string");
671
+ function kiroAuthHeaders(token) {
672
+ const headers = { Authorization: `Bearer ${token}` };
673
+ if (isApiKey(token))
674
+ headers.tokentype = "API_KEY";
675
+ return headers;
677
676
  }
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");
677
+ function isApiKey(token) {
678
+ return token.startsWith("ksk_");
686
679
  }
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;
680
+ async function loginKiroWithApiKey(apiKey, onProgress) {
681
+ if (!apiKey.startsWith("ksk_")) {
682
+ throw new Error("Invalid API key format. Kiro API keys start with 'ksk_'.");
696
683
  }
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;
684
+ onProgress?.("Validating API key...");
685
+ const region = "us-east-1";
686
+ const managementUrl = `https://management.${resolveApiRegion(region)}.kiro.dev/`;
687
+ const response = await fetch(managementUrl, {
688
+ method: "POST",
689
+ headers: {
690
+ "Content-Type": "application/x-amz-json-1.0",
691
+ "X-Amz-Target": "AmazonCodeWhispererService.GetProfile",
692
+ ...kiroAuthHeaders(apiKey),
693
+ ...kiroUserAgent("codewhispererruntime", "F,C")
694
+ },
695
+ body: "{}"
696
+ });
697
+ if (!response.ok) {
698
+ let detail = "";
699
+ try {
700
+ detail = await response.text();
701
+ } catch {
702
+ detail = "";
701
703
  }
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);
704
+ if (response.status === 401 || response.status === 403 || /Invalid token/i.test(detail)) {
705
+ throw new Error("API key was rejected by Kiro. Check that the key is valid and not expired.");
707
706
  }
708
- regions[region] = rawEntry;
707
+ throw new Error(`Kiro GetProfile failed: ${response.status} ${response.statusText} ${detail}`.trim());
709
708
  }
709
+ const data = await response.json();
710
+ const profileArn = data.profile?.arn;
710
711
  return {
711
- version: KIRO_MANAGEMENT_CACHE_VERSION,
712
- source: KIRO_MANAGEMENT_CACHE_SOURCE,
713
- regions
712
+ access: apiKey,
713
+ refresh: `${apiKey}|apikey`,
714
+ expires: Date.now() + 365 * 24 * 60 * 60 * 1e3,
715
+ clientId: "",
716
+ clientSecret: "",
717
+ region,
718
+ authMethod: "apikey",
719
+ ...profileArn ? { profileArn } : {}
714
720
  };
715
721
  }
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;
722
+ async function refreshKiroToken(credentials) {
723
+ const refreshed = await refreshKiroTokenInternal(credentials);
724
+ if (!process.env.VITEST) {
725
+ try {
726
+ const { updateKiroModelsCache: updateKiroModelsCache2 } = await Promise.resolve().then(() => (init_models(), models_exports));
727
+ const region = resolveApiRegion(refreshed.region);
728
+ updateKiroModelsCache2(refreshed.access, region, refreshed.profileArn).catch((error2) => {
729
+ console.warn(`[kiro-core] Failed to refresh Kiro model catalog in ${region}: ${formatSafeError(error2)}`);
730
+ });
731
+ } catch (error2) {
732
+ console.warn(`[kiro-core] Failed to start Kiro model catalog refresh: ${formatSafeError(error2)}`);
733
+ }
723
734
  }
735
+ return refreshed;
724
736
  }
725
- function writeManagementCache(cache) {
726
- const temporaryPath = `${KIRO_MANAGEMENT_CACHE_PATH}.${process.pid}.${randomUUID()}.tmp`;
737
+ async function refreshKiroTokenInternal(credentials) {
738
+ const { getKiroCliCredentials: getKiroCliCredentials2, getKiroCliCredentialsAllowExpired: getKiroCliCredentialsAllowExpired2, saveKiroCliCredentials: saveKiroCliCredentials2, getKiroCliSocialToken: getKiroCliSocialToken2, getKiroCliSocialTokenAllowExpired: getKiroCliSocialTokenAllowExpired2 } = await Promise.resolve().then(() => (init_kiro_cli(), kiro_cli_exports));
739
+ const credentialAuthMethod = credentials.authMethod ?? (credentials.refresh.split("|").at(-1) === "desktop" ? "desktop" : "idc");
740
+ const getValidCliCredentials = () => {
741
+ if (credentialAuthMethod === "desktop")
742
+ return getKiroCliSocialToken2();
743
+ const cliCreds = getKiroCliCredentials2();
744
+ return cliCreds?.authMethod === "idc" ? cliCreds : void 0;
745
+ };
746
+ const getExpiredCliCredentials = () => {
747
+ if (credentialAuthMethod === "desktop")
748
+ return getKiroCliSocialTokenAllowExpired2();
749
+ const cliCreds = getKiroCliCredentialsAllowExpired2();
750
+ return cliCreds?.authMethod === "idc" ? cliCreds : void 0;
751
+ };
752
+ if (credentials.authMethod === "apikey" || isApiKey(credentials.access))
753
+ return credentials;
754
+ if (credentialAuthMethod === "idc") {
755
+ const ideCreds = getKiroIdeCredentials();
756
+ if (ideCreds)
757
+ return ideCreds;
758
+ }
759
+ const preCheckCreds = getValidCliCredentials();
760
+ if (preCheckCreds)
761
+ return preCheckCreds;
727
762
  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 });
763
+ const refreshed = await refreshKiroTokenDirect(credentials);
764
+ saveKiroCliCredentials2(refreshed);
765
+ return refreshed;
766
+ } catch (refreshError) {
767
+ const retryCreds = getValidCliCredentials();
768
+ if (retryCreds)
769
+ return retryCreds;
770
+ const expiredCliCreds = getExpiredCliCredentials();
771
+ if (expiredCliCreds && expiredCliCreds.refresh !== credentials.refresh) {
772
+ try {
773
+ const refreshedFromCli = await refreshKiroTokenDirect(expiredCliCreds);
774
+ saveKiroCliCredentials2(refreshedFromCli);
775
+ return refreshedFromCli;
776
+ } catch {
777
+ }
778
+ }
779
+ const actualExpiry = credentials.expires + EXPIRES_BUFFER_MS;
780
+ if (credentials.access && Date.now() < actualExpiry) {
781
+ return { ...credentials, expires: actualExpiry };
782
+ }
783
+ throw refreshError;
732
784
  }
733
785
  }
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`);
786
+ async function refreshKiroTokenDirect(credentials) {
787
+ const parts = credentials.refresh.split("|");
788
+ const refreshToken = parts[0] ?? "";
789
+ const authMethod = parts[parts.length - 1] ?? "idc";
790
+ const region = credentials.region || "us-east-1";
791
+ if (authMethod === "apikey")
792
+ return credentials;
793
+ if (authMethod === "desktop") {
794
+ const url = KIRO_DESKTOP_REFRESH_URL.replace("{region}", region);
795
+ const response2 = await fetch(url, {
796
+ method: "POST",
797
+ headers: { "Content-Type": "application/json", "User-Agent": KIRO_DESKTOP_USER_AGENT },
798
+ body: JSON.stringify({ refreshToken })
799
+ });
800
+ if (!response2.ok)
801
+ throw new Error(`Desktop token refresh failed: ${response2.status}`);
802
+ const data2 = await response2.json();
803
+ if (!data2.accessToken)
804
+ throw new Error("Desktop token refresh: missing accessToken");
805
+ return {
806
+ refresh: `${data2.refreshToken || refreshToken}|desktop`,
807
+ access: data2.accessToken,
808
+ expires: Date.now() + data2.expiresIn * 1e3 - EXPIRES_BUFFER_MS,
809
+ clientId: "",
810
+ clientSecret: "",
811
+ region,
812
+ authMethod: "desktop",
813
+ profileArn: data2.profileArn || credentials.profileArn,
814
+ startUrl: credentials.startUrl
815
+ };
765
816
  }
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`);
772
- }
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;
817
+ if (authMethod === "external-idp") {
818
+ const idpClientId = parts[1] ?? "";
819
+ const tokenEndpoint = parts[2] ?? "";
820
+ if (!tokenEndpoint)
821
+ throw new Error("External IdP token refresh: missing token endpoint");
822
+ const response2 = await fetch(tokenEndpoint, {
823
+ method: "POST",
824
+ headers: {
825
+ "Content-Type": "application/x-www-form-urlencoded",
826
+ Accept: "application/json",
827
+ "User-Agent": "kiro-core"
828
+ },
829
+ body: new URLSearchParams({
830
+ grant_type: "refresh_token",
831
+ client_id: idpClientId,
832
+ refresh_token: refreshToken
833
+ }).toString()
834
+ });
835
+ if (!response2.ok)
836
+ throw new Error(`External IdP token refresh failed: ${response2.status}`);
837
+ const data2 = await response2.json();
838
+ if (!data2.access_token)
839
+ throw new Error("External IdP token refresh: missing access_token");
840
+ const expiresIn = typeof data2.expires_in === "number" ? data2.expires_in : 3600;
795
841
  return {
796
- id,
797
- kiroModelId,
798
- name: catalogName ?? existing?.name ?? humanizeModelId(id),
842
+ refresh: `${data2.refresh_token || refreshToken}|${idpClientId}|${tokenEndpoint}|external-idp`,
843
+ access: data2.access_token,
844
+ expires: Date.now() + expiresIn * 1e3 - EXPIRES_BUFFER_MS,
845
+ clientId: idpClientId,
846
+ clientSecret: "",
799
847
  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 } : {}
848
+ authMethod: "external-idp",
849
+ profileArn: credentials.profileArn
814
850
  };
851
+ }
852
+ const clientId = parts[1] ?? "";
853
+ const clientSecret = parts[2] ?? "";
854
+ const ssoEndpoint = `https://oidc.${region}.amazonaws.com`;
855
+ const response = await fetch(`${ssoEndpoint}/token`, {
856
+ method: "POST",
857
+ headers: { "Content-Type": "application/json", ...kiroUserAgent("ssooidc", "E") },
858
+ body: JSON.stringify({ clientId, clientSecret, refreshToken, grantType: "refresh_token" })
815
859
  });
860
+ if (!response.ok)
861
+ throw new Error(`Token refresh failed: ${response.status}`);
862
+ const data = await response.json();
863
+ return {
864
+ refresh: `${data.refreshToken}|${clientId}|${clientSecret}|idc`,
865
+ access: data.accessToken,
866
+ expires: Date.now() + data.expiresIn * 1e3 - EXPIRES_BUFFER_MS,
867
+ clientId,
868
+ clientSecret,
869
+ region,
870
+ authMethod: "idc",
871
+ profileArn: credentials.profileArn,
872
+ startUrl: credentials.startUrl,
873
+ isEnterprise: credentials.isEnterprise
874
+ };
816
875
  }
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
- }
876
+ async function resolveKiroCredentials() {
877
+ const { getKiroCliCredentials: getKiroCliCredentials2, getKiroCliCredentialsAllowExpired: getKiroCliCredentialsAllowExpired2 } = await Promise.resolve().then(() => (init_kiro_cli(), kiro_cli_exports));
878
+ const fresh = getKiroCliCredentials2() ?? getKiroIdeCredentials();
879
+ if (fresh)
880
+ return fresh;
881
+ const stale = getKiroCliCredentialsAllowExpired2();
882
+ if (!stale)
883
+ return void 0;
884
+ return refreshKiroToken(stale);
827
885
  }
828
- function loadCachedModelIds() {
829
- refreshKnownModelIds(readManagementCache());
886
+ var KIRO_DESKTOP_REFRESH_URL, EXPIRES_BUFFER_MS, KIRO_DESKTOP_USER_AGENT;
887
+ var init_oauth = __esm({
888
+ "../kiro-core/dist/oauth.js"() {
889
+ "use strict";
890
+ init_debug();
891
+ init_endpoints();
892
+ init_kiro_ide();
893
+ KIRO_DESKTOP_REFRESH_URL = "https://prod.{region}.auth.desktop.kiro.dev/refreshToken";
894
+ EXPIRES_BUFFER_MS = 5 * 60 * 1e3;
895
+ KIRO_DESKTOP_USER_AGENT = "Kiro-Desktop/0.2.13 (darwin; arm64)";
896
+ }
897
+ });
898
+
899
+ // ../kiro-core/dist/token-type.js
900
+ function isExternalIdpAccessToken(accessToken) {
901
+ if (typeof accessToken !== "string" || !accessToken)
902
+ return false;
903
+ const segments = accessToken.split(".");
904
+ const payloadSegment = segments[1];
905
+ if (segments.length === 3 && payloadSegment) {
906
+ try {
907
+ const payload = JSON.parse(Buffer.from(payloadSegment.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf-8"));
908
+ if (payload.aud === EXTERNAL_IDP_AUDIENCE || Array.isArray(payload.aud) && payload.aud.includes(EXTERNAL_IDP_AUDIENCE)) {
909
+ return true;
910
+ }
911
+ } catch {
912
+ }
913
+ }
914
+ try {
915
+ return getKiroCliExternalIdpCredentials()?.access === accessToken;
916
+ } catch {
917
+ return false;
918
+ }
830
919
  }
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;
920
+ function kiroTokenTypeHeaders(accessToken) {
921
+ return isExternalIdpAccessToken(accessToken) ? { tokentype: "EXTERNAL_IDP" } : {};
843
922
  }
844
- function isCacheStale(region) {
845
- const entry = readManagementCache()?.regions[region];
846
- return !entry || Date.now() - entry.fetchedAt > CACHE_MAX_AGE_MS;
923
+ var EXTERNAL_IDP_AUDIENCE;
924
+ var init_token_type = __esm({
925
+ "../kiro-core/dist/token-type.js"() {
926
+ "use strict";
927
+ init_kiro_cli();
928
+ EXTERNAL_IDP_AUDIENCE = "api://kiro";
929
+ }
930
+ });
931
+
932
+ // ../kiro-core/dist/management.js
933
+ import { createHash } from "node:crypto";
934
+ function candidateManagementRegions(primary) {
935
+ const seen = /* @__PURE__ */ new Set([primary]);
936
+ const candidates = [primary];
937
+ for (const region of CANONICAL_MANAGEMENT_REGIONS) {
938
+ if (!seen.has(region)) {
939
+ seen.add(region);
940
+ candidates.push(region);
941
+ }
942
+ }
943
+ return candidates;
847
944
  }
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: {}
945
+ async function requestManagement(auth, operation, path, method, body) {
946
+ const url = new URL(path, getKiroEndpoints(auth.region).management);
947
+ const request = {
948
+ method,
949
+ headers: {
950
+ Accept: "application/json",
951
+ ...kiroAuthHeaders(auth.accessToken),
952
+ ...kiroTokenTypeHeaders(auth.accessToken)
953
+ }
855
954
  };
856
- cache.regions[region] = { region, fetchedAt: Date.now(), models };
857
- writeManagementCache(cache);
858
- refreshKnownModelIds(cache);
859
- }
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;
955
+ if (method === "GET") {
956
+ for (const [name, value] of Object.entries(body)) {
957
+ if (value !== void 0)
958
+ url.searchParams.set(name, String(value));
959
+ }
960
+ } else {
961
+ request.headers = { ...request.headers, "Content-Type": "application/json" };
962
+ request.body = JSON.stringify(body);
867
963
  }
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}`);
964
+ let response;
965
+ try {
966
+ response = await fetch(url.toString(), request);
967
+ } catch (error2) {
968
+ throw new Error(`Kiro management ${operation} request failed in ${auth.region}`, { cause: error2 });
875
969
  }
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);
970
+ return parseManagementResponse(response, operation, auth.region);
1414
971
  }
1415
972
  function profileCacheKey(auth) {
1416
- const tokenHash = createHash2("sha256").update(auth.accessToken).digest("base64url");
973
+ const tokenHash = createHash("sha256").update(auth.accessToken).digest("base64url");
1417
974
  return `${auth.region}:${tokenHash}`;
1418
975
  }
1419
976
  async function getApiKeyProfileArn(accessToken) {
@@ -1517,75 +1074,544 @@ async function resolveKiroProfileArn(auth, providedArn) {
1517
1074
  pendingProfileRequests.delete(key);
1518
1075
  }
1519
1076
  }
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}`);
1077
+ async function listAvailableModels(auth, profileArn) {
1078
+ const response = await requestManagement(auth, "ListAvailableModels", LIST_MODELS_PATH, "GET", {
1079
+ origin: "KIRO_CLI",
1080
+ profileArn
1081
+ });
1082
+ if (!Array.isArray(response.models) || response.models.length === 0) {
1083
+ throw new Error(`Kiro management ListAvailableModels returned no models in ${auth.region}`);
1084
+ }
1085
+ if (response.models.some((model) => !model || typeof model.modelId !== "string" || !model.modelId)) {
1086
+ throw new Error(`Kiro management ListAvailableModels returned an invalid catalog in ${auth.region}`);
1087
+ }
1088
+ return response;
1089
+ }
1090
+ async function fetchKiroModelCatalog(auth, providedProfileArn) {
1091
+ const profileArn = await resolveKiroProfileArn(auth, providedProfileArn);
1092
+ const region = isApiKey(auth.accessToken) ? API_KEY_REGION : profileRegionCache.get(profileCacheKey(auth)) ?? auth.region;
1093
+ if (region !== auth.region) {
1094
+ return listAvailableModels({ ...auth, region }, profileArn);
1095
+ }
1096
+ return listAvailableModels(auth, profileArn);
1097
+ }
1098
+ async function getUsageLimits(auth, request) {
1099
+ const operation = "GetUsageLimits";
1100
+ const url = new URL("Get-Usage-Limits", getKiroEndpoints(auth.region).management);
1101
+ for (const [name, value] of Object.entries(request)) {
1102
+ if (value !== void 0)
1103
+ url.searchParams.set(name, String(value));
1104
+ }
1105
+ let response;
1106
+ try {
1107
+ response = await fetch(url.toString(), {
1108
+ method: "GET",
1109
+ headers: {
1110
+ Accept: "application/json",
1111
+ ...kiroAuthHeaders(auth.accessToken),
1112
+ ...kiroTokenTypeHeaders(auth.accessToken),
1113
+ "User-Agent": "ns-kiro-provider"
1114
+ }
1115
+ });
1116
+ } catch (error2) {
1117
+ throw new Error(`Kiro management ${operation} request failed in ${auth.region}`, { cause: error2 });
1118
+ }
1119
+ return parseManagementResponse(response, operation, auth.region);
1120
+ }
1121
+ var LIST_PROFILES_PATH, LIST_MODELS_PATH, GET_PROFILE_TARGET, API_KEY_REGION, profileArnCache, pendingProfileRequests, profileRegionCache, CANONICAL_MANAGEMENT_REGIONS, ENV_PROFILE_ARN, KiroManagementHttpError;
1122
+ var init_management = __esm({
1123
+ "../kiro-core/dist/management.js"() {
1124
+ "use strict";
1125
+ init_debug();
1126
+ init_endpoints();
1127
+ init_oauth();
1128
+ init_token_type();
1129
+ LIST_PROFILES_PATH = "List-Available-Profiles";
1130
+ LIST_MODELS_PATH = "List-Available-Models";
1131
+ GET_PROFILE_TARGET = "AmazonCodeWhispererService.GetProfile";
1132
+ API_KEY_REGION = "us-east-1";
1133
+ profileArnCache = /* @__PURE__ */ new Map();
1134
+ pendingProfileRequests = /* @__PURE__ */ new Map();
1135
+ profileRegionCache = /* @__PURE__ */ new Map();
1136
+ CANONICAL_MANAGEMENT_REGIONS = ["us-east-1", "eu-central-1"];
1137
+ ENV_PROFILE_ARN = "KIRO_PROFILE_ARN";
1138
+ KiroManagementHttpError = class extends Error {
1139
+ status;
1140
+ constructor(message, status) {
1141
+ super(message);
1142
+ this.status = status;
1143
+ this.name = "KiroManagementHttpError";
1144
+ }
1145
+ };
1146
+ }
1147
+ });
1148
+
1149
+ // ../kiro-core/dist/models.js
1150
+ var models_exports = {};
1151
+ __export(models_exports, {
1152
+ KIRO_MANAGEMENT_CACHE_PATH: () => KIRO_MANAGEMENT_CACHE_PATH,
1153
+ KIRO_MANAGEMENT_CACHE_SOURCE: () => KIRO_MANAGEMENT_CACHE_SOURCE,
1154
+ KIRO_MANAGEMENT_CACHE_VERSION: () => KIRO_MANAGEMENT_CACHE_VERSION,
1155
+ KIRO_MODEL_IDS: () => KIRO_MODEL_IDS,
1156
+ applyEffortLadder: () => applyEffortLadder,
1157
+ getCachedModels: () => getCachedModels,
1158
+ isCacheStale: () => isCacheStale,
1159
+ kiroModels: () => kiroModels,
1160
+ loadCachedModelIds: () => loadCachedModelIds,
1161
+ mapKiroCatalogModels: () => mapKiroCatalogModels,
1162
+ resolveApiRegion: () => resolveApiRegion,
1163
+ resolveKiroModel: () => resolveKiroModel,
1164
+ updateKiroModelsCache: () => updateKiroModelsCache
1165
+ });
1166
+ import { randomUUID } from "node:crypto";
1167
+ import { existsSync as existsSync3, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync } from "node:fs";
1168
+ import { homedir as homedir4 } from "node:os";
1169
+ import { join as join4 } from "node:path";
1170
+ function isRecord2(value) {
1171
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1172
+ }
1173
+ function isPositiveNumber(value) {
1174
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
1175
+ }
1176
+ function isEffortList(value) {
1177
+ return Array.isArray(value) && value.length > 0 && value.every((effort) => KIRO_EFFORT_ORDER.includes(effort));
1178
+ }
1179
+ function isEffortMap(value) {
1180
+ return isRecord2(value) && Object.values(value).every((mapped) => typeof mapped === "string");
1181
+ }
1182
+ function isCachedKiroModel(value) {
1183
+ if (!isRecord2(value))
1184
+ return false;
1185
+ const cost = value.cost;
1186
+ const input = value.input;
1187
+ const schema = value.additionalModelRequestFieldsSchema;
1188
+ const tokenLimits = value.tokenLimits;
1189
+ 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");
1190
+ }
1191
+ function parseManagementCache(raw) {
1192
+ let value;
1193
+ try {
1194
+ value = JSON.parse(raw);
1195
+ } catch {
1196
+ return void 0;
1197
+ }
1198
+ if (!isRecord2(value) || value.version !== KIRO_MANAGEMENT_CACHE_VERSION || value.source !== KIRO_MANAGEMENT_CACHE_SOURCE || !isRecord2(value.regions)) {
1199
+ return void 0;
1200
+ }
1201
+ const regions = {};
1202
+ for (const [region, rawEntry] of Object.entries(value.regions)) {
1203
+ if (!isRecord2(rawEntry) || rawEntry.region !== region || !isPositiveNumber(rawEntry.fetchedAt) || !Array.isArray(rawEntry.models) || rawEntry.models.length === 0 || !rawEntry.models.every(isCachedKiroModel)) {
1204
+ return void 0;
1205
+ }
1206
+ const modelIds = /* @__PURE__ */ new Set();
1207
+ for (const model of rawEntry.models) {
1208
+ if (modelIds.has(model.id))
1209
+ return void 0;
1210
+ modelIds.add(model.id);
1211
+ }
1212
+ regions[region] = rawEntry;
1213
+ }
1214
+ return {
1215
+ version: KIRO_MANAGEMENT_CACHE_VERSION,
1216
+ source: KIRO_MANAGEMENT_CACHE_SOURCE,
1217
+ regions
1218
+ };
1219
+ }
1220
+ function readManagementCache() {
1221
+ if (!existsSync3(KIRO_MANAGEMENT_CACHE_PATH))
1222
+ return void 0;
1223
+ try {
1224
+ return parseManagementCache(readFileSync2(KIRO_MANAGEMENT_CACHE_PATH, "utf-8"));
1225
+ } catch {
1226
+ return void 0;
1227
+ }
1228
+ }
1229
+ function writeManagementCache(cache) {
1230
+ const temporaryPath = `${KIRO_MANAGEMENT_CACHE_PATH}.${process.pid}.${randomUUID()}.tmp`;
1231
+ try {
1232
+ writeFileSync(temporaryPath, JSON.stringify(cache, null, 2), "utf-8");
1233
+ renameSync(temporaryPath, KIRO_MANAGEMENT_CACHE_PATH);
1234
+ } finally {
1235
+ rmSync(temporaryPath, { force: true });
1236
+ }
1237
+ }
1238
+ function toLocalModelId(kiroModelId) {
1239
+ return kiroModelId.replace(/(\d)\.(\d)/g, "$1-$2");
1240
+ }
1241
+ function humanizeModelId(modelId) {
1242
+ return modelId.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
1243
+ }
1244
+ function applyEffortLadder(config) {
1245
+ if (!config || config.values.length === 0)
1246
+ return {};
1247
+ const efforts = KIRO_EFFORT_ORDER.filter((effort) => config.values.includes(effort));
1248
+ if (efforts.length === 0)
1249
+ return {};
1250
+ return { efforts, ...config.summarizedThinking ? { supportsSummarizedThinking: true } : {} };
1251
+ }
1252
+ function hasReasoningFamilyFallback(modelId) {
1253
+ const normalizedId = modelId.toLowerCase();
1254
+ return normalizedId === "auto" || REASONING_FAMILY_MARKERS.some((marker) => normalizedId.includes(marker));
1255
+ }
1256
+ function hasVerifiedImageInput(kiroModelId) {
1257
+ return kiroModelId.startsWith("claude-") || VERIFIED_IMAGE_MODEL_IDS.has(kiroModelId.toLowerCase());
1258
+ }
1259
+ function applyVerifiedCapabilities(model) {
1260
+ if (!hasVerifiedImageInput(model.kiroModelId) || model.input.includes("image"))
1261
+ return model;
1262
+ return { ...model, input: ["text", "image"] };
1263
+ }
1264
+ function validateCatalogMetadata(model) {
1265
+ const rawSchema = model.additionalModelRequestFieldsSchema;
1266
+ const schema = rawSchema ?? void 0;
1267
+ if (schema !== void 0 && !isRecord2(schema)) {
1268
+ throw new Error(`Kiro management catalog model ${model.modelId} has an invalid request-fields schema`);
1527
1269
  }
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}`);
1270
+ const tokenLimits = model.tokenLimits;
1271
+ if (tokenLimits !== void 0 && !isRecord2(tokenLimits)) {
1272
+ throw new Error(`Kiro management catalog model ${model.modelId} has invalid token limits`);
1530
1273
  }
1531
- return response;
1274
+ if (tokenLimits && (tokenLimits.maxInputTokens !== void 0 && !isPositiveNumber(tokenLimits.maxInputTokens) || tokenLimits.maxOutputTokens !== void 0 && !isPositiveNumber(tokenLimits.maxOutputTokens))) {
1275
+ throw new Error(`Kiro management catalog model ${model.modelId} has invalid token limits`);
1276
+ }
1277
+ return { schema, tokenLimits };
1532
1278
  }
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);
1279
+ function mapKiroCatalogModels(catalogModels, region, rates) {
1280
+ if (catalogModels.length === 0) {
1281
+ throw new Error(`Kiro management catalog returned no models in ${region}`);
1538
1282
  }
1539
- return listAvailableModels(auth, profileArn);
1283
+ const seenIds = /* @__PURE__ */ new Set();
1284
+ return catalogModels.map((catalogModel) => {
1285
+ const kiroModelId = catalogModel.modelId;
1286
+ if (!kiroModelId || kiroModelId.trim() !== kiroModelId) {
1287
+ throw new Error(`Kiro management catalog returned an invalid model ID in ${region}`);
1288
+ }
1289
+ const id = toLocalModelId(kiroModelId);
1290
+ if (seenIds.has(id)) {
1291
+ throw new Error(`Kiro management catalog contains conflicting model ID ${id} in ${region}`);
1292
+ }
1293
+ seenIds.add(id);
1294
+ const existing = kiroModels.find((model) => model.id === id);
1295
+ const rate = rates?.get(kiroModelId);
1296
+ const { schema, tokenLimits } = validateCatalogMetadata(catalogModel);
1297
+ const effortConfig = getKiroEffortConfig(schema, kiroModelId);
1298
+ const catalogName = typeof catalogModel.displayName === "string" && catalogModel.displayName.length > 0 ? catalogModel.displayName : void 0;
1299
+ return {
1300
+ id,
1301
+ kiroModelId,
1302
+ // Humanize the wire id, not the local one: `toLocalModelId` rewrites
1303
+ // `5.1` as `5-1`, and a display name derived from it loses the dot —
1304
+ // "Claude Fable 5 1" instead of "Claude Fable 5.1".
1305
+ name: catalogName ?? existing?.name ?? humanizeModelId(kiroModelId),
1306
+ region,
1307
+ // Deliberately schema-only: when a schema exists the resolver returns
1308
+ // `deriveKiroEffort(schema)`, and when it does not the family-marker guess
1309
+ // decides. The fallback tier feeds the ladders, not this flag.
1310
+ reasoning: schema !== void 0 && effortConfig !== void 0 || schema === void 0 && hasReasoningFamilyFallback(id),
1311
+ ...applyEffortLadder(effortConfig),
1312
+ input: existing ? [...existing.input] : hasVerifiedImageInput(kiroModelId) ? ["text", "image"] : ["text"],
1313
+ ...id.startsWith("claude-") ? { recoverTextToolCalls: false } : {},
1314
+ cost: ZERO_COST,
1315
+ ...rate ? { rateMultiplier: rate.multiplier, ...rate.unit ? { rateUnit: rate.unit } : {} } : {},
1316
+ contextWindow: tokenLimits?.maxInputTokens ?? DEFAULT_CONTEXT_WINDOW,
1317
+ maxTokens: tokenLimits?.maxOutputTokens ?? DEFAULT_MAX_TOKENS,
1318
+ ...existing?.firstTokenTimeout ? { firstTokenTimeout: existing.firstTokenTimeout } : {},
1319
+ ...schema ? { additionalModelRequestFieldsSchema: schema } : {},
1320
+ ...tokenLimits ? { tokenLimits } : {}
1321
+ };
1322
+ });
1540
1323
  }
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));
1324
+ function refreshKnownModelIds(cache) {
1325
+ KIRO_MODEL_IDS.clear();
1326
+ for (const modelId of BOOTSTRAP_KIRO_MODEL_IDS)
1327
+ KIRO_MODEL_IDS.add(modelId);
1328
+ if (!cache)
1329
+ return;
1330
+ for (const entry of Object.values(cache.regions)) {
1331
+ for (const model of entry.models)
1332
+ KIRO_MODEL_IDS.add(model.kiroModelId);
1547
1333
  }
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 });
1334
+ }
1335
+ function loadCachedModelIds() {
1336
+ refreshKnownModelIds(readManagementCache());
1337
+ }
1338
+ function getCachedModels(region) {
1339
+ const cache = readManagementCache();
1340
+ refreshKnownModelIds(cache);
1341
+ const models = cache?.regions[region]?.models ?? kiroModels;
1342
+ let changed = false;
1343
+ const corrected = models.map((model) => {
1344
+ const result = applyVerifiedCapabilities(model);
1345
+ if (result !== model)
1346
+ changed = true;
1347
+ return result;
1348
+ });
1349
+ return changed ? corrected : models;
1350
+ }
1351
+ function isCacheStale(region) {
1352
+ const entry = readManagementCache()?.regions[region];
1353
+ return !entry || Date.now() - entry.fetchedAt > CACHE_MAX_AGE_MS;
1354
+ }
1355
+ async function updateKiroModelsCache(accessToken, region, profileArn) {
1356
+ const response = await fetchKiroModelCatalog({ accessToken, region }, profileArn);
1357
+ const models = mapKiroCatalogModels(response.models, region, getKiroCliModelRates());
1358
+ const cache = readManagementCache() ?? {
1359
+ version: KIRO_MANAGEMENT_CACHE_VERSION,
1360
+ source: KIRO_MANAGEMENT_CACHE_SOURCE,
1361
+ regions: {}
1362
+ };
1363
+ cache.regions[region] = { region, fetchedAt: Date.now(), models };
1364
+ writeManagementCache(cache);
1365
+ refreshKnownModelIds(cache);
1366
+ }
1367
+ function resolveKiroModel(modelId, exactKiroModelId) {
1368
+ if (exactKiroModelId)
1369
+ return exactKiroModelId;
1370
+ const cachedModel = Object.values(readManagementCache()?.regions ?? {}).flatMap((entry) => entry.models).find((model) => model.id === modelId);
1371
+ if (cachedModel) {
1372
+ KIRO_MODEL_IDS.add(cachedModel.kiroModelId);
1373
+ return cachedModel.kiroModelId;
1561
1374
  }
1562
- return parseManagementResponse(response, operation, auth.region);
1375
+ const bootstrapModel = kiroModels.find((model) => model.id === modelId);
1376
+ if (bootstrapModel)
1377
+ return bootstrapModel.kiroModelId;
1378
+ const normalizedId = modelId.replace(/(\d)-(\d)/g, "$1.$2");
1379
+ loadCachedModelIds();
1380
+ if (!KIRO_MODEL_IDS.has(normalizedId)) {
1381
+ throw new Error(`Unknown Kiro model ID: ${modelId}`);
1382
+ }
1383
+ return normalizedId;
1563
1384
  }
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"() {
1385
+ 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;
1386
+ var init_models = __esm({
1387
+ "../kiro-core/dist/models.js"() {
1567
1388
  "use strict";
1568
- init_debug();
1389
+ init_effort();
1390
+ init_kiro_cli();
1391
+ init_management();
1392
+ init_types();
1569
1393
  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";
1394
+ KIRO_MANAGEMENT_CACHE_VERSION = 1;
1395
+ KIRO_MANAGEMENT_CACHE_SOURCE = "ns-kiro-provider-management";
1396
+ KIRO_MANAGEMENT_CACHE_PATH = join4(homedir4(), ".ns-kiro-provider-models-cache.json");
1397
+ CACHE_MAX_AGE_MS = 36e5;
1398
+ DEFAULT_CONTEXT_WINDOW = 2e5;
1399
+ DEFAULT_MAX_TOKENS = 8192;
1400
+ ZERO_COST = Object.freeze({ input: 0, output: 0, cacheRead: 0, cacheWrite: 0 });
1401
+ REASONING_FAMILY_MARKERS = ["opus", "sonnet", "fable", "coder", "deepseek", "gpt", "glm", "qwen"];
1402
+ VERIFIED_IMAGE_MODEL_IDS = /* @__PURE__ */ new Set(["gpt-5.6-luna"]);
1403
+ bootstrapKiroModels = [
1404
+ {
1405
+ id: "claude-opus-5",
1406
+ kiroModelId: "claude-opus-5",
1407
+ name: "Claude Opus 5",
1408
+ reasoning: true,
1409
+ input: ["text", "image"],
1410
+ cost: ZERO_COST,
1411
+ contextWindow: 1e6,
1412
+ maxTokens: 128e3,
1413
+ firstTokenTimeout: 18e4
1414
+ },
1415
+ {
1416
+ id: "claude-opus-4-8",
1417
+ kiroModelId: "claude-opus-4.8",
1418
+ name: "Claude Opus 4.8",
1419
+ reasoning: true,
1420
+ input: ["text", "image"],
1421
+ cost: ZERO_COST,
1422
+ contextWindow: 1e6,
1423
+ maxTokens: 128e3,
1424
+ firstTokenTimeout: 18e4
1425
+ },
1426
+ {
1427
+ id: "claude-opus-4-7",
1428
+ kiroModelId: "claude-opus-4.7",
1429
+ name: "Claude Opus 4.7",
1430
+ reasoning: true,
1431
+ input: ["text", "image"],
1432
+ cost: ZERO_COST,
1433
+ contextWindow: 1e6,
1434
+ maxTokens: 128e3,
1435
+ firstTokenTimeout: 18e4
1436
+ },
1437
+ {
1438
+ id: "claude-opus-4-6",
1439
+ kiroModelId: "claude-opus-4.6",
1440
+ name: "Claude Opus 4.6",
1441
+ reasoning: true,
1442
+ input: ["text", "image"],
1443
+ cost: ZERO_COST,
1444
+ contextWindow: 1e6,
1445
+ maxTokens: 32768
1446
+ },
1447
+ {
1448
+ id: "claude-opus-4-5",
1449
+ kiroModelId: "claude-opus-4.5",
1450
+ name: "Claude Opus 4.5",
1451
+ reasoning: true,
1452
+ input: ["text", "image"],
1453
+ cost: ZERO_COST,
1454
+ contextWindow: 2e5,
1455
+ maxTokens: 64e3
1456
+ },
1457
+ {
1458
+ id: "claude-sonnet-5",
1459
+ kiroModelId: "claude-sonnet-5",
1460
+ name: "Claude Sonnet 5",
1461
+ reasoning: true,
1462
+ input: ["text", "image"],
1463
+ cost: ZERO_COST,
1464
+ contextWindow: 1e6,
1465
+ maxTokens: 65536
1466
+ },
1467
+ {
1468
+ id: "claude-sonnet-4-6",
1469
+ kiroModelId: "claude-sonnet-4.6",
1470
+ name: "Claude Sonnet 4.6",
1471
+ reasoning: true,
1472
+ input: ["text", "image"],
1473
+ cost: ZERO_COST,
1474
+ contextWindow: 1e6,
1475
+ maxTokens: 65536
1476
+ },
1477
+ {
1478
+ id: "claude-sonnet-4-5",
1479
+ kiroModelId: "claude-sonnet-4.5",
1480
+ name: "Claude Sonnet 4.5",
1481
+ reasoning: true,
1482
+ input: ["text", "image"],
1483
+ cost: ZERO_COST,
1484
+ contextWindow: 2e5,
1485
+ maxTokens: 65536
1486
+ },
1487
+ {
1488
+ id: "claude-sonnet-4",
1489
+ kiroModelId: "claude-sonnet-4",
1490
+ name: "Claude Sonnet 4",
1491
+ reasoning: true,
1492
+ input: ["text", "image"],
1493
+ cost: ZERO_COST,
1494
+ contextWindow: 2e5,
1495
+ maxTokens: 65536
1496
+ },
1497
+ {
1498
+ id: "claude-haiku-4-5",
1499
+ kiroModelId: "claude-haiku-4.5",
1500
+ name: "Claude Haiku 4.5",
1501
+ reasoning: false,
1502
+ input: ["text", "image"],
1503
+ cost: ZERO_COST,
1504
+ contextWindow: 2e5,
1505
+ maxTokens: 65536
1506
+ },
1507
+ {
1508
+ id: "claude-fable-5",
1509
+ kiroModelId: "claude-fable-5",
1510
+ name: "Claude Fable 5",
1511
+ reasoning: true,
1512
+ input: ["text", "image"],
1513
+ cost: ZERO_COST,
1514
+ contextWindow: 1e6,
1515
+ maxTokens: 65536
1516
+ },
1517
+ {
1518
+ id: "gpt-5-6-luna",
1519
+ kiroModelId: "gpt-5.6-luna",
1520
+ name: "GPT-5.6 Luna",
1521
+ reasoning: true,
1522
+ input: ["text", "image"],
1523
+ cost: ZERO_COST,
1524
+ contextWindow: 272e3,
1525
+ maxTokens: 128e3
1526
+ },
1527
+ {
1528
+ id: "gpt-5-6-sol",
1529
+ kiroModelId: "gpt-5.6-sol",
1530
+ name: "GPT-5.6 Sol",
1531
+ reasoning: true,
1532
+ input: ["text"],
1533
+ cost: ZERO_COST,
1534
+ contextWindow: 272e3,
1535
+ maxTokens: 128e3
1536
+ },
1537
+ {
1538
+ id: "gpt-5-6-terra",
1539
+ kiroModelId: "gpt-5.6-terra",
1540
+ name: "GPT-5.6 Terra",
1541
+ reasoning: true,
1542
+ input: ["text"],
1543
+ cost: ZERO_COST,
1544
+ contextWindow: 272e3,
1545
+ maxTokens: 128e3
1546
+ },
1547
+ {
1548
+ id: "deepseek-3-2",
1549
+ kiroModelId: "deepseek-3.2",
1550
+ name: "DeepSeek 3.2",
1551
+ reasoning: true,
1552
+ input: ["text"],
1553
+ cost: ZERO_COST,
1554
+ contextWindow: 164e3,
1555
+ maxTokens: 8192
1556
+ },
1557
+ {
1558
+ id: "minimax-m2-5",
1559
+ kiroModelId: "minimax-m2.5",
1560
+ name: "MiniMax M2.5",
1561
+ reasoning: false,
1562
+ input: ["text"],
1563
+ cost: ZERO_COST,
1564
+ contextWindow: 196e3,
1565
+ maxTokens: 8192
1566
+ },
1567
+ {
1568
+ id: "minimax-m2-1",
1569
+ kiroModelId: "minimax-m2.1",
1570
+ name: "MiniMax M2.1",
1571
+ reasoning: false,
1572
+ input: ["text"],
1573
+ cost: ZERO_COST,
1574
+ contextWindow: 196e3,
1575
+ maxTokens: 8192
1576
+ },
1577
+ {
1578
+ id: "glm-5",
1579
+ kiroModelId: "glm-5",
1580
+ name: "GLM 5",
1581
+ reasoning: true,
1582
+ input: ["text"],
1583
+ cost: ZERO_COST,
1584
+ contextWindow: 2e5,
1585
+ maxTokens: 8192
1586
+ },
1587
+ {
1588
+ id: "qwen3-coder-next",
1589
+ kiroModelId: "qwen3-coder-next",
1590
+ name: "Qwen3 Coder Next",
1591
+ reasoning: true,
1592
+ input: ["text"],
1593
+ cost: ZERO_COST,
1594
+ contextWindow: 256e3,
1595
+ maxTokens: 8192
1596
+ },
1597
+ {
1598
+ id: "auto",
1599
+ kiroModelId: "auto",
1600
+ name: "Auto",
1601
+ reasoning: true,
1602
+ input: ["text", "image"],
1603
+ cost: ZERO_COST,
1604
+ contextWindow: 1e6,
1605
+ maxTokens: 65536
1587
1606
  }
1588
- };
1607
+ ];
1608
+ kiroModels = bootstrapKiroModels.map((model) => ({
1609
+ ...model,
1610
+ ...applyEffortLadder(model.reasoning ? getKiroEffortConfig(model.additionalModelRequestFieldsSchema, model.kiroModelId) : void 0),
1611
+ ...model.id.startsWith("claude-") ? { recoverTextToolCalls: false } : {}
1612
+ }));
1613
+ BOOTSTRAP_KIRO_MODEL_IDS = kiroModels.map((model) => model.kiroModelId);
1614
+ KIRO_MODEL_IDS = new Set(BOOTSTRAP_KIRO_MODEL_IDS);
1589
1615
  }
1590
1616
  });
1591
1617
 
@@ -1653,8 +1679,8 @@ var require_base64_js = __commonJS({
1653
1679
  }
1654
1680
  return arr;
1655
1681
  }
1656
- function tripletToBase64(num) {
1657
- return lookup[num >> 18 & 63] + lookup[num >> 12 & 63] + lookup[num >> 6 & 63] + lookup[num & 63];
1682
+ function tripletToBase64(num2) {
1683
+ return lookup[num2 >> 18 & 63] + lookup[num2 >> 12 & 63] + lookup[num2 >> 6 & 63] + lookup[num2 & 63];
1658
1684
  }
1659
1685
  function encodeChunk(uint8, start, end) {
1660
1686
  var tmp;
@@ -1747,6 +1773,10 @@ var KiroBlockBuffer = class {
1747
1773
  getText(index) {
1748
1774
  return this.blocks.get(index)?.text ?? "";
1749
1775
  }
1776
+ /** Kinds of the live blocks, in allocation order. */
1777
+ kinds() {
1778
+ return [...this.blocks.values()].map((block) => block.kind);
1779
+ }
1750
1780
  /**
1751
1781
  * Rewrite one accumulated block before it is closed. Used by the recovery
1752
1782
  * passes that lift tool calls out of text and by echo-noise stripping, both
@@ -1836,23 +1866,262 @@ function parseBracketToolCalls(text) {
1836
1866
  }
1837
1867
  return { toolCalls, cleanedText };
1838
1868
  }
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 };
1869
+
1870
+ // ../kiro-core/dist/cache-estimator.js
1871
+ var stateByConversation = /* @__PURE__ */ new Map();
1872
+ var MAX_TRACKED_CONVERSATIONS = 128;
1873
+ var warnedWireCacheCounters = false;
1874
+ function applyCacheEstimate(conversationId, usage, wireUsage, config, now = Date.now()) {
1875
+ const previous = stateByConversation.get(conversationId);
1876
+ const promptTokens = usage.input + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0) + usage.output;
1877
+ const wireReportedCache = wireUsage?.cacheReadInputTokens !== void 0 || wireUsage?.cacheWriteInputTokens !== void 0;
1878
+ if (wireReportedCache) {
1879
+ if (config.estimateCacheUsage && !warnedWireCacheCounters) {
1880
+ warnedWireCacheCounters = true;
1881
+ console.warn("[kiro-core] Kiro now reports cache token counters; cache estimation was skipped and may be disabled.");
1882
+ }
1883
+ noteSuccessfulTurn(conversationId, promptTokens, usage.input, now);
1884
+ return 0;
1885
+ }
1886
+ if (!config.estimateCacheUsage || usage.input <= 0)
1887
+ return 0;
1888
+ if (!previous) {
1889
+ noteSuccessfulTurn(conversationId, promptTokens, usage.input, now);
1890
+ return 0;
1891
+ }
1892
+ const expired = config.estimatedCacheTimeout > 0 && now - previous.turnTimestamp > config.estimatedCacheTimeout;
1893
+ const contextShrank = usage.input < previous.promptTokens * 0.6;
1894
+ if (expired || contextShrank) {
1895
+ noteSuccessfulTurn(conversationId, promptTokens, usage.input, now);
1896
+ return 0;
1897
+ }
1898
+ const estimatedRead = Math.min(previous.promptTokens, usage.input);
1899
+ if (estimatedRead > 0) {
1900
+ usage.input -= estimatedRead;
1901
+ usage.cacheRead = (usage.cacheRead ?? 0) + estimatedRead;
1902
+ usage.cacheEstimated = true;
1903
+ }
1904
+ noteSuccessfulTurn(conversationId, promptTokens, promptTokens, now);
1905
+ return estimatedRead;
1906
+ }
1907
+ function noteSuccessfulTurn(conversationId, promptTokens, input, now) {
1908
+ if (input <= 0 || promptTokens <= 0)
1909
+ return;
1910
+ stateByConversation.delete(conversationId);
1911
+ stateByConversation.set(conversationId, { promptTokens, turnTimestamp: now });
1912
+ if (stateByConversation.size > MAX_TRACKED_CONVERSATIONS) {
1913
+ const oldest = stateByConversation.keys().next().value;
1914
+ if (oldest !== void 0)
1915
+ stateByConversation.delete(oldest);
1916
+ }
1917
+ }
1918
+
1919
+ // ../kiro-core/dist/cost.js
1920
+ var PER_MILLION = 1e6;
1921
+ function calculateKiroCost(cost, usage) {
1922
+ const input = usage.input * cost.input / PER_MILLION;
1923
+ const output = usage.output * cost.output / PER_MILLION;
1924
+ const cacheRead = (usage.cacheRead ?? 0) * cost.cacheRead / PER_MILLION;
1925
+ const cacheWrite = (usage.cacheWrite ?? 0) * cost.cacheWrite / PER_MILLION;
1926
+ return { input, output, cacheRead, cacheWrite, total: input + output + cacheRead + cacheWrite };
1927
+ }
1928
+
1929
+ // ../kiro-core/dist/index.js
1930
+ init_debug();
1931
+ init_effort();
1932
+ init_endpoints();
1933
+
1934
+ // ../kiro-core/dist/retry.js
1935
+ init_models();
1936
+ var FIRST_TOKEN_TIMEOUT = 9e4;
1937
+ var REQUEST_HEADER_TIMEOUT = 9e4;
1938
+ function firstTokenTimeoutForModel(modelId) {
1939
+ if (retryConfig.firstTokenTimeoutMs !== FIRST_TOKEN_TIMEOUT) {
1940
+ return retryConfig.firstTokenTimeoutMs;
1941
+ }
1942
+ const model = kiroModels.find((m) => m.id === modelId);
1943
+ return model?.firstTokenTimeout ?? FIRST_TOKEN_TIMEOUT;
1944
+ }
1945
+ var retryConfig = {
1946
+ firstTokenTimeoutMs: FIRST_TOKEN_TIMEOUT,
1947
+ requestHeaderTimeoutMs: REQUEST_HEADER_TIMEOUT
1948
+ };
1949
+ function exponentialBackoff(attempt, baseMs, maxMs) {
1950
+ return Math.min(baseMs * 2 ** attempt, maxMs);
1951
+ }
1952
+ var MAX_RETRY_DELAY = 1e4;
1953
+ var REQUEST_RATE_FALLBACK_DELAY_MS = 1e4;
1954
+ function extractKiroReason(errorText) {
1955
+ if (!errorText)
1956
+ return void 0;
1957
+ try {
1958
+ const parsed = JSON.parse(errorText);
1959
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
1960
+ return void 0;
1961
+ const reason = parsed.reason;
1962
+ return typeof reason === "string" && reason.length > 0 ? reason : void 0;
1963
+ } catch {
1964
+ return void 0;
1965
+ }
1966
+ }
1967
+ function parseRetryAfterMs(headers, nowMs = Date.now()) {
1968
+ const get = headers?.get?.bind(headers);
1969
+ if (!get)
1970
+ return void 0;
1971
+ const milliseconds = nonNegativeNumber(get("retry-after-ms"));
1972
+ if (milliseconds !== void 0)
1973
+ return Math.round(milliseconds);
1974
+ const retryAfter = get("retry-after");
1975
+ if (retryAfter !== null && retryAfter.trim() !== "") {
1976
+ const seconds = Number(retryAfter);
1977
+ if (Number.isFinite(seconds)) {
1978
+ if (seconds >= 0) {
1979
+ const delayMs = seconds * 1e3;
1980
+ if (Number.isFinite(delayMs))
1981
+ return Math.round(delayMs);
1982
+ }
1983
+ } else {
1984
+ const dateMs = Date.parse(retryAfter);
1985
+ if (!Number.isNaN(dateMs))
1986
+ return Math.max(0, dateMs - nowMs);
1987
+ }
1988
+ }
1989
+ const resetAfterSeconds = nonNegativeNumber(get("x-ratelimit-reset-after"));
1990
+ if (resetAfterSeconds !== void 0) {
1991
+ const delayMs = resetAfterSeconds * 1e3;
1992
+ if (Number.isFinite(delayMs))
1993
+ return Math.round(delayMs);
1994
+ }
1995
+ return void 0;
1996
+ }
1997
+ function resolveRequestRateRetryDelay(headers, nowMs = Date.now()) {
1998
+ const advertisedDelayMs = parseRetryAfterMs(headers, nowMs);
1999
+ const requestedDelayMs = advertisedDelayMs ?? REQUEST_RATE_FALLBACK_DELAY_MS;
2000
+ return {
2001
+ delayMs: Math.min(requestedDelayMs, MAX_RETRY_DELAY),
2002
+ ...advertisedDelayMs !== void 0 ? { advertisedDelayMs } : {},
2003
+ capped: requestedDelayMs > MAX_RETRY_DELAY
2004
+ };
2005
+ }
2006
+ function nonNegativeNumber(value) {
2007
+ if (value === null || value.trim() === "")
2008
+ return void 0;
2009
+ const parsed = Number(value);
2010
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
2011
+ }
2012
+ var KIRO_REASON_CODES = Object.freeze({
2013
+ /** Request body exceeded the service's size threshold. */
2014
+ CONTENT_LENGTH_EXCEEDS_THRESHOLD: "CONTENT_LENGTH_EXCEEDS_THRESHOLD",
2015
+ /** Prose-only size rejection; the service sends no reason code for this one. */
2016
+ INPUT_TOO_LONG: "Input is too long",
2017
+ /** Monthly request quota exhausted — hard limit, not transient. */
2018
+ MONTHLY_REQUEST_COUNT: "MONTHLY_REQUEST_COUNT",
2019
+ /** Model capacity temporarily unavailable — transient, worth retrying. */
2020
+ INSUFFICIENT_MODEL_CAPACITY: "INSUFFICIENT_MODEL_CAPACITY",
2021
+ /** Short-window request throttle — retry only within the provider budget. */
2022
+ USER_REQUEST_RATE_EXCEEDED: "USER_REQUEST_RATE_EXCEEDED",
2023
+ /**
2024
+ * Generic request-validation rejection, returned for a malformed body of any
2025
+ * size (empty `content`, history referencing tools absent from the catalog).
2026
+ * Not a size signal: classifying it as "too big" makes the caller compact a
2027
+ * history that was never the problem, a loop it can never satisfy.
2028
+ */
2029
+ REQUEST_BODY_INVALID: "REQUEST_BODY_INVALID"
2030
+ });
2031
+ var TOO_BIG_PATTERNS = Object.freeze([
2032
+ KIRO_REASON_CODES.CONTENT_LENGTH_EXCEEDS_THRESHOLD,
2033
+ KIRO_REASON_CODES.INPUT_TOO_LONG
2034
+ ]);
2035
+ var NON_RETRYABLE_BODY_PATTERNS = Object.freeze([KIRO_REASON_CODES.MONTHLY_REQUEST_COUNT]);
2036
+ var CAPACITY_PATTERN = KIRO_REASON_CODES.INSUFFICIENT_MODEL_CAPACITY;
2037
+ var CAPACITY_MAX_RETRIES = 3;
2038
+ var CAPACITY_BASE_DELAY_MS = 5e3;
2039
+ var capacityRetryConfig = {
2040
+ maxRetries: CAPACITY_MAX_RETRIES,
2041
+ baseDelayMs: CAPACITY_BASE_DELAY_MS
2042
+ };
2043
+ function isTooBigError(status, errorText) {
2044
+ return status === 413 || status === 400 && TOO_BIG_PATTERNS.some((p) => errorText.includes(p));
2045
+ }
2046
+ function isNonRetryableBodyError(errorText) {
2047
+ return NON_RETRYABLE_BODY_PATTERNS.some((p) => errorText.includes(p));
2048
+ }
2049
+ function isCapacityError(errorText) {
2050
+ return errorText.includes(CAPACITY_PATTERN);
1848
2051
  }
1849
2052
 
1850
- // ../kiro-core/dist/index.js
1851
- init_debug();
1852
- init_effort();
1853
- init_endpoints();
2053
+ // ../kiro-core/dist/errors.js
2054
+ var REASON_CODE_MARKERS = [
2055
+ KIRO_REASON_CODES.CONTENT_LENGTH_EXCEEDS_THRESHOLD,
2056
+ KIRO_REASON_CODES.MONTHLY_REQUEST_COUNT,
2057
+ KIRO_REASON_CODES.INSUFFICIENT_MODEL_CAPACITY,
2058
+ KIRO_REASON_CODES.REQUEST_BODY_INVALID
2059
+ ];
2060
+ var KiroApiError = class extends Error {
2061
+ status;
2062
+ reasonCode;
2063
+ retryAfterMs;
2064
+ providerAttempts;
2065
+ constructor(message, status, reasonCode, retryAfterMs, providerAttempts) {
2066
+ super(message);
2067
+ this.status = status;
2068
+ this.reasonCode = reasonCode;
2069
+ this.retryAfterMs = retryAfterMs;
2070
+ this.providerAttempts = providerAttempts;
2071
+ this.name = "KiroApiError";
2072
+ }
2073
+ };
2074
+ function extractKiroReasonCode(errorText) {
2075
+ if (!errorText)
2076
+ return void 0;
2077
+ const trimmed = errorText.trim();
2078
+ if (trimmed.startsWith("{")) {
2079
+ try {
2080
+ const parsed = JSON.parse(trimmed);
2081
+ const reason = parsed.reason ?? parsed.reasonCode;
2082
+ if (typeof reason === "string" && reason.length > 0)
2083
+ return reason;
2084
+ } catch {
2085
+ }
2086
+ }
2087
+ return REASON_CODE_MARKERS.find((code) => errorText.includes(code));
2088
+ }
1854
2089
 
1855
2090
  // ../kiro-core/dist/event-parser.js
2091
+ var KIRO_EVENT_KEYS = [
2092
+ "assistantResponseEvent",
2093
+ "codeReferenceEvent",
2094
+ "contextUsageEvent",
2095
+ "documentCitationEvent",
2096
+ "error",
2097
+ "metadataEvent",
2098
+ "meteringEvent",
2099
+ "reasoningContentEvent",
2100
+ "serviceUnavailableError",
2101
+ "throttlingError",
2102
+ "toolResultEvent",
2103
+ "toolUseEvent",
2104
+ "validationError"
2105
+ ];
2106
+ var KIRO_EVENT_KEY_SET = new Set(KIRO_EVENT_KEYS);
2107
+ var KIRO_ERROR_MEMBERS = {
2108
+ error: { kind: "internalServer", exception: "InternalServerException" },
2109
+ throttlingError: { kind: "throttling", exception: "ThrottlingException" },
2110
+ validationError: { kind: "validation", exception: "ValidationException" },
2111
+ serviceUnavailableError: { kind: "serviceUnavailable", exception: "ServiceUnavailableException" }
2112
+ };
2113
+ var KIRO_ERROR_TOKENS = {
2114
+ ...KIRO_ERROR_MEMBERS,
2115
+ InternalServerException: KIRO_ERROR_MEMBERS.error,
2116
+ ThrottlingException: KIRO_ERROR_MEMBERS.throttlingError,
2117
+ ValidationException: KIRO_ERROR_MEMBERS.validationError,
2118
+ ServiceUnavailableException: KIRO_ERROR_MEMBERS.serviceUnavailableError
2119
+ };
2120
+ function lookupKiroErrorMember(key) {
2121
+ return Object.hasOwn(KIRO_ERROR_TOKENS, key) ? KIRO_ERROR_TOKENS[key] : void 0;
2122
+ }
2123
+ var num = (v) => typeof v === "number" ? v : void 0;
2124
+ var str = (v) => typeof v === "string" ? v : void 0;
1856
2125
  function tokenCount(value) {
1857
2126
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
1858
2127
  }
@@ -1871,30 +2140,136 @@ var CACHE_WRITE_KEYS = [
1871
2140
  "cacheCreationInputTokens",
1872
2141
  "cacheWriteTokens"
1873
2142
  ];
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 };
2143
+ function toolInput(raw) {
2144
+ if (typeof raw === "string")
2145
+ return raw;
2146
+ if (raw && typeof raw === "object" && Object.keys(raw).length > 0) {
2147
+ return JSON.stringify(raw);
2148
+ }
2149
+ return "";
2150
+ }
2151
+ function parseToolUse(parsed) {
1881
2152
  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
2153
  return {
1884
2154
  type: "toolUse",
1885
2155
  data: {
1886
2156
  name: parsed.name,
1887
2157
  toolUseId: parsed.toolUseId,
1888
- input,
2158
+ input: toolInput(parsed.input),
1889
2159
  stop: parsed.stop
1890
2160
  }
1891
2161
  };
1892
2162
  }
2163
+ if (parsed.input !== void 0) {
2164
+ return { type: "toolUseInput", data: { input: toolInput(parsed.input) } };
2165
+ }
2166
+ if (parsed.stop !== void 0)
2167
+ return { type: "toolUseStop", data: { stop: parsed.stop } };
2168
+ return null;
2169
+ }
2170
+ function parseMetadata(parsed) {
2171
+ const tu = parsed.tokenUsage ?? {};
2172
+ const data = {
2173
+ inputTokens: tokenCount(tu.uncachedInputTokens) ?? tokenCount(tu.inputTokens),
2174
+ outputTokens: tokenCount(tu.outputTokens),
2175
+ totalTokens: tokenCount(tu.totalTokens),
2176
+ cacheReadInputTokens: firstTokenCount(tu, CACHE_READ_KEYS),
2177
+ cacheWriteInputTokens: firstTokenCount(tu, CACHE_WRITE_KEYS),
2178
+ contextUsagePercentage: num(tu.contextUsagePercentage),
2179
+ normalizedTokenUsage: num(tu.normalizedTokenUsage),
2180
+ rawStopReason: str(parsed.stopReason),
2181
+ stopDetails: parsed.stopDetails && typeof parsed.stopDetails === "object" ? parsed.stopDetails : void 0
2182
+ };
2183
+ for (const k of Object.keys(data)) {
2184
+ if (data[k] === void 0)
2185
+ delete data[k];
2186
+ }
2187
+ return Object.keys(data).length > 0 ? { type: "usage", data } : null;
2188
+ }
2189
+ function parseError(parsed, kind, fallbackName) {
2190
+ const rawError = parsed.error ?? parsed.Error;
2191
+ const error2 = typeof rawError === "string" ? rawError : rawError !== void 0 ? JSON.stringify(rawError) : str(parsed.name) ?? fallbackName;
2192
+ const message = parsed.message ?? parsed.Message ?? parsed.reason;
2193
+ const data = { error: error2, kind };
2194
+ if (typeof message === "string")
2195
+ data.message = message;
2196
+ const reason = str(parsed.reason);
2197
+ if (reason !== void 0)
2198
+ data.reason = reason;
2199
+ const retryAfterMilliseconds = num(parsed.retryAfterMilliseconds);
2200
+ if (retryAfterMilliseconds !== void 0)
2201
+ data.retryAfterMilliseconds = retryAfterMilliseconds;
2202
+ return { type: "error", data };
2203
+ }
2204
+ function parseKiroEvent(key, parsed) {
2205
+ switch (key) {
2206
+ case "assistantResponseEvent": {
2207
+ const content = str(parsed.content);
2208
+ return content !== void 0 ? { type: "content", data: content } : null;
2209
+ }
2210
+ case "reasoningContentEvent": {
2211
+ const text = str(parsed.text);
2212
+ if (text !== void 0)
2213
+ return { type: "thinkingText", data: text };
2214
+ const signature = str(parsed.signature);
2215
+ if (signature !== void 0)
2216
+ return { type: "thinkingSignature", data: signature };
2217
+ return { type: "ignored", data: { key } };
2218
+ }
2219
+ case "toolUseEvent":
2220
+ return parseToolUse(parsed);
2221
+ case "contextUsageEvent": {
2222
+ const pct = num(parsed.contextUsagePercentage);
2223
+ return pct !== void 0 ? { type: "contextUsage", data: { contextUsagePercentage: pct } } : null;
2224
+ }
2225
+ case "metadataEvent":
2226
+ return parseMetadata(parsed);
2227
+ case "meteringEvent":
2228
+ return {
2229
+ type: "metering",
2230
+ data: { credits: tokenCount(parsed.usage), unit: str(parsed.unit), unitPlural: str(parsed.unitPlural) }
2231
+ };
2232
+ // Reachable when a member that models an exception is delivered as an
2233
+ // ordinary event frame. The real wire uses exception framing, handled by
2234
+ // parseKiroExceptionFrame, but both routes must agree.
2235
+ case "error":
2236
+ case "throttlingError":
2237
+ case "validationError":
2238
+ case "serviceUnavailableError": {
2239
+ const member = lookupKiroErrorMember(key);
2240
+ return parseError(parsed, member.kind, member.exception);
2241
+ }
2242
+ // Known members with no consumer yet: explicitly ignored, not unparseable.
2243
+ case "codeReferenceEvent":
2244
+ case "documentCitationEvent":
2245
+ case "toolResultEvent":
2246
+ return { type: "ignored", data: { key } };
2247
+ default: {
2248
+ const member = lookupKiroErrorMember(key);
2249
+ if (member)
2250
+ return parseError(parsed, member.kind, member.exception);
2251
+ return parseKiroEventByShape(parsed);
2252
+ }
2253
+ }
2254
+ }
2255
+ function parseKiroExceptionFrame(key, parsed) {
2256
+ const member = lookupKiroErrorMember(key);
2257
+ if (!member)
2258
+ return null;
2259
+ const event = parseError(parsed, member.kind, member.exception);
2260
+ return event.data;
2261
+ }
2262
+ function parseKiroEventByShape(parsed) {
2263
+ if (parsed.content !== void 0)
2264
+ return { type: "content", data: parsed.content };
2265
+ if (typeof parsed.text === "string")
2266
+ return { type: "thinkingText", data: parsed.text };
2267
+ if (typeof parsed.signature === "string")
2268
+ return { type: "thinkingSignature", data: parsed.signature };
2269
+ if (parsed.name && parsed.toolUseId)
2270
+ return parseToolUse(parsed);
1893
2271
  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
- };
2272
+ return { type: "toolUseInput", data: { input: toolInput(parsed.input) } };
1898
2273
  }
1899
2274
  if (parsed.stop !== void 0 && parsed.contextUsagePercentage === void 0)
1900
2275
  return { type: "toolUseStop", data: { stop: parsed.stop } };
@@ -1902,42 +2277,23 @@ function parseKiroEvent(parsed) {
1902
2277
  return { type: "contextUsage", data: { contextUsagePercentage: parsed.contextUsagePercentage } };
1903
2278
  if (parsed.followupPrompt !== void 0)
1904
2279
  return { type: "followupPrompt", data: parsed.followupPrompt };
2280
+ if (parsed.tokenUsage !== void 0 || parsed.stopReason !== void 0 || parsed.stopDetails !== void 0) {
2281
+ return parseMetadata(parsed);
2282
+ }
1905
2283
  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);
2284
+ return parseError(parsed, "unknown", "unknown");
2285
+ }
2286
+ if (typeof parsed.usage === "number") {
1926
2287
  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
- }
2288
+ type: "metering",
2289
+ data: { credits: tokenCount(parsed.usage), unit: str(parsed.unit), unitPlural: str(parsed.unitPlural) }
1934
2290
  };
1935
2291
  }
1936
2292
  return null;
1937
2293
  }
1938
2294
 
1939
2295
  // ../kiro-core/dist/transform.js
1940
- import { createHash } from "node:crypto";
2296
+ import { createHash as createHash2 } from "node:crypto";
1941
2297
  var TOOL_RESULT_LIMIT = 25e4;
1942
2298
  var EMPTY_CONTENT_PLACEHOLDER = "Please proceed with the task.";
1943
2299
  function sanitizeSurrogates(text) {
@@ -1955,7 +2311,7 @@ var KIRO_TOOL_USE_ID_PATTERN = /^[a-zA-Z0-9_.:-]{1,64}$/;
1955
2311
  function toKiroToolUseId(toolUseId) {
1956
2312
  if (KIRO_TOOL_USE_ID_PATTERN.test(toolUseId))
1957
2313
  return toolUseId;
1958
- const digest = createHash("sha256").update(toolUseId).digest("base64url").slice(0, 32);
2314
+ const digest = createHash2("sha256").update(toolUseId).digest("base64url").slice(0, 32);
1959
2315
  return `pi_${digest}`;
1960
2316
  }
1961
2317
  function normalizeMessages(messages) {
@@ -2969,8 +3325,8 @@ function bytePairEncode(piece, ranks) {
2969
3325
  return [ranks.get(piece.join(","))];
2970
3326
  return bytePairMerge(piece, ranks).map((p) => ranks.get(piece.slice(p.start, p.end).join(","))).filter((x) => x != null);
2971
3327
  }
2972
- function escapeRegex(str) {
2973
- return str.replace(/[\\^$*+?.()|[\]{}]/g, "\\$&");
3328
+ function escapeRegex(str2) {
3329
+ return str2.replace(/[\\^$*+?.()|[\]{}]/g, "\\$&");
2974
3330
  }
2975
3331
  var _Tiktoken = class {
2976
3332
  /** @internal */
@@ -3117,6 +3473,7 @@ var KiroResponseAssembler = class {
3117
3473
  totalContent = "";
3118
3474
  lastContentData = "";
3119
3475
  usageEvent = null;
3476
+ meteringEvent = null;
3120
3477
  receivedContextUsage = false;
3121
3478
  thinkingParser = null;
3122
3479
  nativeThinkingBlockIndex = null;
@@ -3124,16 +3481,19 @@ var KiroResponseAssembler = class {
3124
3481
  textBlockIndex = null;
3125
3482
  emittedToolCalls = 0;
3126
3483
  sawAnyToolCalls = false;
3484
+ droppedToolCalls = [];
3127
3485
  currentToolCall = null;
3486
+ stopReason = "stop";
3128
3487
  constructor(model, thinkingEnabled) {
3129
3488
  this.model = model;
3130
3489
  this.thinkingEnabled = thinkingEnabled;
3131
3490
  }
3132
- /** Clear per-attempt state. Block indexes and accumulated usage are kept. */
3491
+ /** Clear per-attempt state. Only block indexes are kept. */
3133
3492
  beginAttempt() {
3134
3493
  this.totalContent = "";
3135
3494
  this.lastContentData = "";
3136
3495
  this.usageEvent = null;
3496
+ this.meteringEvent = null;
3137
3497
  this.receivedContextUsage = false;
3138
3498
  this.thinkingParser = this.thinkingEnabled ? new ThinkingTagParser(this.blocks) : null;
3139
3499
  this.nativeThinkingBlockIndex = null;
@@ -3141,7 +3501,17 @@ var KiroResponseAssembler = class {
3141
3501
  this.textBlockIndex = null;
3142
3502
  this.emittedToolCalls = 0;
3143
3503
  this.sawAnyToolCalls = false;
3504
+ this.droppedToolCalls = [];
3144
3505
  this.currentToolCall = null;
3506
+ this.usage.input = 0;
3507
+ this.usage.output = 0;
3508
+ this.usage.totalTokens = 0;
3509
+ this.usage.cost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
3510
+ delete this.usage.cacheRead;
3511
+ delete this.usage.cacheWrite;
3512
+ delete this.usage.contextPercent;
3513
+ delete this.usage.credits;
3514
+ delete this.usage.creditUnit;
3145
3515
  }
3146
3516
  /** Hand over the events buffered so far. */
3147
3517
  takeEvents() {
@@ -3218,11 +3588,17 @@ var KiroResponseAssembler = class {
3218
3588
  break;
3219
3589
  }
3220
3590
  case "usage": {
3221
- this.usageEvent = event.data;
3591
+ this.usageEvent = { ...this.usageEvent ?? {}, ...event.data };
3222
3592
  if (debugEnabled())
3223
3593
  debugLog("response.usageRaw", payload);
3224
3594
  break;
3225
3595
  }
3596
+ case "metering": {
3597
+ this.meteringEvent = event.data;
3598
+ if (debugEnabled())
3599
+ debugLog("stream.metering", [event.data]);
3600
+ break;
3601
+ }
3226
3602
  }
3227
3603
  }
3228
3604
  /**
@@ -3230,8 +3606,12 @@ var KiroResponseAssembler = class {
3230
3606
  * text-dialect recovery and echo-stripping passes.
3231
3607
  */
3232
3608
  endTurn() {
3233
- if (this.currentToolCall && this.emitToolCall(this.currentToolCall))
3234
- this.emittedToolCalls++;
3609
+ if (this.currentToolCall) {
3610
+ if (this.emitToolCall(this.currentToolCall))
3611
+ this.emittedToolCalls++;
3612
+ else
3613
+ this.droppedToolCalls.push(this.currentToolCall.name);
3614
+ }
3235
3615
  this.currentToolCall = null;
3236
3616
  this.endNativeThinking();
3237
3617
  if (this.thinkingParser) {
@@ -3242,13 +3622,16 @@ var KiroResponseAssembler = class {
3242
3622
  this.stripEchoNoise();
3243
3623
  const responseText = this.textBlockIndex === null ? "" : this.blocks.getText(this.textBlockIndex);
3244
3624
  const hasText2 = responseText.length > 0;
3625
+ this.stopReason = !this.receivedContextUsage && this.emittedToolCalls === 0 ? "length" : this.emittedToolCalls > 0 ? "toolUse" : "stop";
3245
3626
  return {
3246
3627
  responseText,
3247
3628
  hasText: hasText2,
3248
3629
  sawAnyToolCalls: this.sawAnyToolCalls,
3249
3630
  emittedToolCalls: this.emittedToolCalls,
3250
3631
  isEchoLoop: hasText2 && !this.sawAnyToolCalls && ECHO_NOISE_PATTERN.test(responseText),
3251
- isEmpty: !hasText2 && !this.sawAnyToolCalls
3632
+ isEmpty: !hasText2 && !this.sawAnyToolCalls,
3633
+ stopReason: this.stopReason,
3634
+ droppedToolCalls: this.droppedToolCalls
3252
3635
  };
3253
3636
  }
3254
3637
  /** Drop an echo the caller decided not to retry, so it is not read as a continuation signal. */
@@ -3256,6 +3639,10 @@ var KiroResponseAssembler = class {
3256
3639
  if (this.textBlockIndex !== null)
3257
3640
  this.blocks.setText(this.textBlockIndex, "");
3258
3641
  }
3642
+ /** Kinds of the blocks this attempt produced, for the exhaustion diagnostic. */
3643
+ contentKinds() {
3644
+ return this.blocks.kinds();
3645
+ }
3259
3646
  /** Close the text block and settle usage and the stop reason. */
3260
3647
  complete() {
3261
3648
  if (this.textBlockIndex !== null)
@@ -3263,17 +3650,17 @@ var KiroResponseAssembler = class {
3263
3650
  if (this.usageEvent?.inputTokens !== void 0)
3264
3651
  this.usage.input = this.usageEvent.inputTokens;
3265
3652
  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;
3653
+ this.usage.totalTokens = this.usageEvent?.totalTokens ?? this.usage.input + (this.usageEvent?.cacheReadInputTokens ?? 0) + (this.usageEvent?.cacheWriteInputTokens ?? 0) + this.usage.output;
3654
+ if (this.usageEvent?.cacheReadInputTokens !== void 0)
3655
+ this.usage.cacheRead = this.usageEvent.cacheReadInputTokens;
3656
+ if (this.usageEvent?.cacheWriteInputTokens !== void 0)
3657
+ this.usage.cacheWrite = this.usageEvent.cacheWriteInputTokens;
3658
+ if (this.meteringEvent?.credits !== void 0)
3659
+ this.usage.credits = this.meteringEvent.credits;
3660
+ if (this.meteringEvent?.unit !== void 0)
3661
+ this.usage.creditUnit = this.meteringEvent.unit;
3275
3662
  this.usage.cost = calculateKiroCost(this.model.cost, this.usage);
3276
- const stopReason = !this.receivedContextUsage && this.emittedToolCalls === 0 ? "length" : this.emittedToolCalls > 0 ? "toolUse" : "stop";
3663
+ const stopReason = this.stopReason;
3277
3664
  debugLog("response.done", {
3278
3665
  stopReason,
3279
3666
  receivedContextUsage: this.receivedContextUsage,
@@ -3282,7 +3669,12 @@ var KiroResponseAssembler = class {
3282
3669
  textLen: this.textBlockIndex === null ? 0 : this.blocks.getText(this.textBlockIndex).length,
3283
3670
  usage: this.usage
3284
3671
  });
3285
- return { stopReason, usage: this.usage };
3672
+ return {
3673
+ stopReason,
3674
+ usage: this.usage,
3675
+ wireUsage: this.usageEvent,
3676
+ metering: this.meteringEvent
3677
+ };
3286
3678
  }
3287
3679
  ensureNativeThinkingBlock() {
3288
3680
  if (this.nativeThinkingBlockIndex === null)
@@ -3317,6 +3709,8 @@ var KiroResponseAssembler = class {
3317
3709
  return;
3318
3710
  if (this.emitToolCall(this.currentToolCall))
3319
3711
  this.emittedToolCalls++;
3712
+ else
3713
+ this.droppedToolCalls.push(this.currentToolCall.name);
3320
3714
  this.currentToolCall = null;
3321
3715
  }
3322
3716
  /**
@@ -3358,6 +3752,8 @@ var KiroResponseAssembler = class {
3358
3752
  for (const call of recovered) {
3359
3753
  if (this.emitToolCall({ toolUseId: call.toolUseId, name: call.name, input: JSON.stringify(call.arguments) })) {
3360
3754
  this.emittedToolCalls++;
3755
+ } else {
3756
+ this.droppedToolCalls.push(call.name);
3361
3757
  }
3362
3758
  }
3363
3759
  }
@@ -4117,6 +4513,7 @@ function readKiroEventStream(body, options) {
4117
4513
  const outcome = { firstTokenTimedOut: false, idleTimedOut: false, error: null };
4118
4514
  const idleTimeoutMs = options.idleTimeoutMs ?? IDLE_TIMEOUT;
4119
4515
  const bodyReader = body.getReader();
4516
+ const callerSignal = options.signal;
4120
4517
  async function* frames() {
4121
4518
  let idleTimer = null;
4122
4519
  const resetIdle = () => {
@@ -4128,6 +4525,14 @@ function readKiroEventStream(body, options) {
4128
4525
  });
4129
4526
  }, idleTimeoutMs);
4130
4527
  };
4528
+ const onCallerStreamAbort = () => {
4529
+ void bodyReader.cancel().catch(() => {
4530
+ });
4531
+ };
4532
+ if (callerSignal?.aborted)
4533
+ onCallerStreamAbort();
4534
+ else
4535
+ callerSignal?.addEventListener("abort", onCallerStreamAbort, { once: true });
4131
4536
  const bodyIterable = {
4132
4537
  async *[Symbol.asyncIterator]() {
4133
4538
  try {
@@ -4148,6 +4553,26 @@ function readKiroEventStream(body, options) {
4148
4553
  if (!entry)
4149
4554
  throw new Error("Received an empty event stream message");
4150
4555
  const [key, msg] = entry;
4556
+ if (msg.headers[":message-type"]?.value === "exception") {
4557
+ let parsedException = {};
4558
+ try {
4559
+ const decoded = JSON.parse(utf8Decoder.decode(msg.body));
4560
+ if (decoded && typeof decoded === "object")
4561
+ parsedException = decoded;
4562
+ } catch {
4563
+ }
4564
+ const data = parseKiroExceptionFrame(key, parsedException) ?? {
4565
+ error: key,
4566
+ kind: "unknown",
4567
+ ...typeof parsedException.message === "string" ? { message: parsedException.message } : {},
4568
+ ...typeof parsedException.reason === "string" ? { reason: parsedException.reason } : {},
4569
+ ...typeof parsedException.retryAfterMilliseconds === "number" ? { retryAfterMilliseconds: parsedException.retryAfterMilliseconds } : {}
4570
+ };
4571
+ const error2 = new Error(data.message ? `${data.error}: ${data.message}` : data.error);
4572
+ error2.name = data.error;
4573
+ error2.kiroError = data;
4574
+ return { [key]: error2 };
4575
+ }
4151
4576
  const parsed = JSON.parse(utf8Decoder.decode(msg.body));
4152
4577
  return { [key]: parsed };
4153
4578
  });
@@ -4156,29 +4581,43 @@ function readKiroEventStream(body, options) {
4156
4581
  const FIRST_TOKEN_SENTINEL = /* @__PURE__ */ Symbol("firstTokenTimeout");
4157
4582
  try {
4158
4583
  while (true) {
4584
+ if (callerSignal?.aborted) {
4585
+ throw callerSignal.reason ?? new Error("Request aborted");
4586
+ }
4159
4587
  let iterResult;
4160
4588
  try {
4161
4589
  if (!gotFirstToken) {
4162
4590
  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;
4591
+ let firstTokenTimer;
4592
+ try {
4593
+ const result = await Promise.race([
4594
+ readPromise,
4595
+ new Promise((resolve) => {
4596
+ firstTokenTimer = setTimeout(() => resolve(FIRST_TOKEN_SENTINEL), options.firstTokenTimeoutMs);
4597
+ })
4598
+ ]);
4599
+ if (result === FIRST_TOKEN_SENTINEL) {
4600
+ readPromise.catch(() => {
4601
+ });
4602
+ void bodyReader.cancel().catch(() => {
4603
+ });
4604
+ outcome.firstTokenTimedOut = true;
4605
+ return;
4606
+ }
4607
+ iterResult = result;
4608
+ gotFirstToken = true;
4609
+ resetIdle();
4610
+ } finally {
4611
+ if (firstTokenTimer !== void 0)
4612
+ clearTimeout(firstTokenTimer);
4174
4613
  }
4175
- iterResult = result;
4176
- gotFirstToken = true;
4177
- resetIdle();
4178
4614
  } else {
4179
4615
  iterResult = await iterator.next();
4180
4616
  }
4181
4617
  } catch (e) {
4618
+ const kiroError = e?.kiroError;
4619
+ if (kiroError)
4620
+ outcome.errorData = kiroError;
4182
4621
  outcome.error = e instanceof Error ? e.message : (typeof e === "object" && e !== null ? JSON.stringify(e) : String(e)) || "Unknown stream error";
4183
4622
  return;
4184
4623
  }
@@ -4186,148 +4625,40 @@ function readKiroEventStream(body, options) {
4186
4625
  if (done)
4187
4626
  return;
4188
4627
  resetIdle();
4189
- const payload = Object.values(value)[0];
4190
- const event = parseKiroEvent(payload);
4628
+ const frameEntry = Object.entries(value)[0];
4629
+ if (!frameEntry)
4630
+ continue;
4631
+ const [frameKey, framePayload] = frameEntry;
4632
+ const event = parseKiroEvent(frameKey, framePayload ?? {});
4191
4633
  if (!event)
4192
4634
  continue;
4635
+ if (event.type === "ignored") {
4636
+ if (debugEnabled())
4637
+ debugLog("stream.events.ignored", [event.data.key]);
4638
+ continue;
4639
+ }
4193
4640
  if (debugEnabled())
4194
4641
  debugLog("stream.events", [event]);
4195
4642
  if (event.type === "error") {
4196
4643
  outcome.error = event.data.message ? `${event.data.error}: ${event.data.message}` : event.data.error;
4644
+ outcome.errorData = event.data;
4197
4645
  void bodyReader.cancel().catch(() => {
4198
4646
  });
4199
4647
  return;
4200
4648
  }
4201
- yield { event, payload };
4649
+ yield { event, payload: framePayload };
4202
4650
  }
4203
4651
  } finally {
4204
4652
  if (idleTimer)
4205
4653
  clearTimeout(idleTimer);
4654
+ callerSignal?.removeEventListener("abort", onCallerStreamAbort);
4206
4655
  }
4207
4656
  }
4208
4657
  return { frames: frames(), outcome };
4209
4658
  }
4210
4659
 
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
4660
  // ../kiro-core/dist/stream.js
4661
+ import { createHash as createHash3 } from "node:crypto";
4331
4662
  init_debug();
4332
4663
  init_effort();
4333
4664
  init_endpoints();
@@ -4400,7 +4731,64 @@ function createResponseHeaderDeadline(callerSignal, timeoutMs) {
4400
4731
  };
4401
4732
  }
4402
4733
 
4734
+ // ../kiro-core/dist/usage-tracking.js
4735
+ var DEFAULT_USD_PER_CREDIT = 0.04;
4736
+ var DEFAULT_ESTIMATED_CACHE_TIMEOUT_MS = 5 * 60 * 1e3;
4737
+ var KIRO_USAGE_TRACKING_DISABLED = Object.freeze({
4738
+ estimateDollarValue: false,
4739
+ usdPerCredit: DEFAULT_USD_PER_CREDIT,
4740
+ estimateCacheUsage: false,
4741
+ estimatedCacheTimeout: DEFAULT_ESTIMATED_CACHE_TIMEOUT_MS
4742
+ });
4743
+ var CREDIT_UNITS = /* @__PURE__ */ new Set(["credit", "credits"]);
4744
+ function estimateKiroCreditCost(tracking, metering) {
4745
+ if (!tracking.estimateDollarValue || !metering)
4746
+ return void 0;
4747
+ if (typeof metering.unit !== "string" || !CREDIT_UNITS.has(metering.unit.toLowerCase()))
4748
+ return void 0;
4749
+ const { credits } = metering;
4750
+ if (typeof credits !== "number" || !Number.isFinite(credits) || credits < 0)
4751
+ return void 0;
4752
+ return credits * tracking.usdPerCredit;
4753
+ }
4754
+
4403
4755
  // ../kiro-core/dist/stream.js
4756
+ function describeAttempts(count) {
4757
+ return count === 1 ? "1 attempt" : `${count} attempts`;
4758
+ }
4759
+ var DIAGNOSTIC_QUOTE_LIMIT = 200;
4760
+ function clampForDiagnostic(text) {
4761
+ return text.length <= DIAGNOSTIC_QUOTE_LIMIT ? text : `${text.slice(0, DIAGNOSTIC_QUOTE_LIMIT)}\u2026 (truncated)`;
4762
+ }
4763
+ function encodeBytesForDiagnostic(bytes) {
4764
+ let encoded = "";
4765
+ for (const byte of bytes) {
4766
+ encoded += String.fromCharCode(65 + (byte >> 4), 65 + (byte & 15));
4767
+ }
4768
+ return encoded;
4769
+ }
4770
+ function encodeToolNameForDiagnostic(name) {
4771
+ const identity = typeof name === "string" ? name : `${typeof name}:${JSON.stringify(name)}`;
4772
+ let encoded = "";
4773
+ for (let i = 0; i < identity.length; i++) {
4774
+ const codeUnit = identity.charCodeAt(i);
4775
+ encoded += String.fromCharCode(65 + (codeUnit >> 12), 65 + (codeUnit >> 8 & 15), 65 + (codeUnit >> 4 & 15), 65 + (codeUnit & 15));
4776
+ }
4777
+ return encoded;
4778
+ }
4779
+ function describeDroppedToolNames(names) {
4780
+ const encoded = names.map((name) => `A-P:${encodeToolNameForDiagnostic(name)}`).join(", ");
4781
+ if (encoded.length <= DIAGNOSTIC_QUOTE_LIMIT)
4782
+ return encoded;
4783
+ const digest = createHash3("sha256").update(JSON.stringify(names)).digest();
4784
+ return `A-P-DIGEST:${encodeBytesForDiagnostic(digest)} (tool identities fingerprinted)`;
4785
+ }
4786
+ function describeReturnedContent(kinds) {
4787
+ const unique = [...new Set(kinds)].sort();
4788
+ if (unique.length === 0)
4789
+ return "returning empty content";
4790
+ return `returning only ${unique.join(" and ")} content`;
4791
+ }
4404
4792
  var skipProfileResolutionForTests = false;
4405
4793
  var TEST_PROFILE_ARN = "arn:aws:codewhisperer:us-east-1:000000000000:profile/test";
4406
4794
  async function* streamKiro(request) {
@@ -4411,7 +4799,6 @@ async function* streamKiro(request) {
4411
4799
  }
4412
4800
  let accessToken = initialAccessToken;
4413
4801
  const region = model.region ?? "us-east-1";
4414
- const endpoint = new URL("generateAssistantResponse", getKiroEndpoints(region).runtime).toString();
4415
4802
  let managementAuth = { accessToken, region };
4416
4803
  const cliCreds = getKiroCliCredentials() ?? getKiroCliCredentialsAllowExpired();
4417
4804
  const cliProfileArn = cliCreds?.access === accessToken ? cliCreds.profileArn : void 0;
@@ -4430,9 +4817,11 @@ async function* streamKiro(request) {
4430
4817
  managementAuth = { accessToken, region };
4431
4818
  profileArn = freshCreds.profileArn || (skipProfileResolutionForTests ? TEST_PROFILE_ARN : await resolveKiroProfileArn(managementAuth));
4432
4819
  }
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)}`);
4820
+ let runtimeRegion = getKiroRegionFromProfileArn(profileArn) ?? region;
4821
+ let endpoint = new URL("generateAssistantResponse", getKiroEndpoints(runtimeRegion).runtime).toString();
4822
+ if (!process.env.VITEST && isCacheStale(runtimeRegion)) {
4823
+ updateKiroModelsCache(accessToken, runtimeRegion, profileArn).catch((error2) => {
4824
+ console.warn(`[kiro-core] Failed to refresh Kiro model catalog in ${runtimeRegion}: ${formatSafeError(error2)}`);
4436
4825
  });
4437
4826
  }
4438
4827
  const kiroModelId = resolveKiroModel(model.id, model.kiroModelId);
@@ -4461,8 +4850,13 @@ async function* streamKiro(request) {
4461
4850
  ${systemPrompt}` : ""}`;
4462
4851
  }
4463
4852
  const assembler = new KiroResponseAssembler(model, thinkingEnabled);
4853
+ const usageTracking = request.usageTracking ?? KIRO_USAGE_TRACKING_DISABLED;
4464
4854
  let retryCount = 0;
4465
4855
  const maxRetries = 3;
4856
+ let emptyAttempts = 0;
4857
+ let echoAttempts = 0;
4858
+ let credentialRefreshTotal = 0;
4859
+ let capacityRetryTotal = 0;
4466
4860
  const conversationId = request.sessionId ?? crypto.randomUUID();
4467
4861
  requestLoop: while (retryCount <= maxRetries) {
4468
4862
  if (signal?.aborted)
@@ -4545,6 +4939,7 @@ ${systemPrompt}` : ""}`;
4545
4939
  });
4546
4940
  if (isCapacityError(errText) && capacityRetryCount < capacityRetryConfig.maxRetries) {
4547
4941
  capacityRetryCount++;
4942
+ capacityRetryTotal++;
4548
4943
  const delayMs = exponentialBackoff(capacityRetryCount - 1, capacityRetryConfig.baseDelayMs, 3e4);
4549
4944
  logCapacityEvent(`INSUFFICIENT_MODEL_CAPACITY \u2014 retrying in ${delayMs}ms (${capacityRetryCount}/${capacityRetryConfig.maxRetries})`);
4550
4945
  await abortableDelay(delayMs, signal);
@@ -4572,6 +4967,7 @@ ${systemPrompt}` : ""}`;
4572
4967
  }
4573
4968
  if (response.status === 403 && !isCapacityError(errText) && retryCount < maxRetries) {
4574
4969
  retryCount++;
4970
+ credentialRefreshTotal++;
4575
4971
  invalidateKiroProfileArn(managementAuth);
4576
4972
  const rejectedAccessToken = accessToken;
4577
4973
  const rejectedProfileArn = profileArn;
@@ -4583,16 +4979,23 @@ ${systemPrompt}` : ""}`;
4583
4979
  managementAuth = { accessToken, region };
4584
4980
  const inheritedDesktopProfileArn = rejectedCliCreds?.authMethod === "desktop" && freshCreds?.authMethod === "desktop" ? rejectedProfileArn : void 0;
4585
4981
  profileArn = freshCreds?.profileArn || inheritedDesktopProfileArn || (skipProfileResolutionForTests ? TEST_PROFILE_ARN : await resolveKiroProfileArn(managementAuth));
4982
+ runtimeRegion = getKiroRegionFromProfileArn(profileArn) ?? region;
4983
+ endpoint = new URL("generateAssistantResponse", getKiroEndpoints(runtimeRegion).runtime).toString();
4586
4984
  await abortableDelay(exponentialBackoff(retryCount - 1, 500, MAX_RETRY_DELAY), signal);
4587
4985
  break;
4588
4986
  }
4987
+ const errorMeta = {
4988
+ reasonCode: extractKiroReasonCode(errText),
4989
+ retryAfterMs: parseRetryAfterMs(response.headers),
4990
+ providerAttempts: { credentialRefresh: credentialRefreshTotal, capacity: capacityRetryTotal }
4991
+ };
4589
4992
  if (isNonRetryableBodyError(errText) || isCapacityError(errText)) {
4590
- throw new Error(`Kiro API error: ${errText || safeStatusText}`);
4993
+ throw new KiroApiError(`Kiro API error: ${errText || safeStatusText}`, response.status, errorMeta.reasonCode, errorMeta.retryAfterMs, errorMeta.providerAttempts);
4591
4994
  }
4592
4995
  if (isTooBigError(response.status, errText)) {
4593
- throw new Error(`Kiro API error: context_length_exceeded (${response.status} ${errText})`);
4996
+ throw new KiroApiError(`Kiro API error: context_length_exceeded (${response.status} ${errText})`, response.status, errorMeta.reasonCode, errorMeta.retryAfterMs, errorMeta.providerAttempts);
4594
4997
  }
4595
- throw new Error(`Kiro API error: ${response.status} ${safeStatusText} ${errText}`);
4998
+ throw new KiroApiError(`Kiro API error: ${response.status} ${safeStatusText} ${errText}`, response.status, errorMeta.reasonCode, errorMeta.retryAfterMs, errorMeta.providerAttempts);
4596
4999
  }
4597
5000
  break;
4598
5001
  }
@@ -4606,7 +5009,8 @@ ${systemPrompt}` : ""}`;
4606
5009
  throw new Error("No response body");
4607
5010
  assembler.beginAttempt();
4608
5011
  const { frames, outcome } = readKiroEventStream(response.body, {
4609
- firstTokenTimeoutMs: model.firstTokenTimeout ?? firstTokenTimeoutForModel(model.id)
5012
+ firstTokenTimeoutMs: model.firstTokenTimeout ?? firstTokenTimeoutForModel(model.id),
5013
+ signal
4610
5014
  });
4611
5015
  for await (const frame of frames) {
4612
5016
  assembler.handle(frame);
@@ -4616,6 +5020,11 @@ ${systemPrompt}` : ""}`;
4616
5020
  if (outcome.firstTokenTimedOut || outcome.idleTimedOut || outcome.error) {
4617
5021
  if (retryCount < maxRetries) {
4618
5022
  retryCount++;
5023
+ if (outcome.errorData && debugEnabled()) {
5024
+ debugLog("stream.error.typed", [outcome.errorData]);
5025
+ }
5026
+ assembler.discard();
5027
+ yield* assembler.takeEvents();
4619
5028
  await abortableDelay(exponentialBackoff(retryCount - 1, 1e3, MAX_RETRY_DELAY), signal);
4620
5029
  continue;
4621
5030
  }
@@ -4625,7 +5034,13 @@ ${systemPrompt}` : ""}`;
4625
5034
  }
4626
5035
  const summary = assembler.endTurn();
4627
5036
  yield* assembler.takeEvents();
4628
- if (summary.isEmpty || summary.isEchoLoop) {
5037
+ const degenerate = summary.isEmpty || summary.isEchoLoop;
5038
+ if (summary.isEchoLoop)
5039
+ echoAttempts++;
5040
+ else if (degenerate)
5041
+ emptyAttempts++;
5042
+ let errorMessage;
5043
+ if (degenerate) {
4629
5044
  const mayRetry = retryCount < maxRetries && (!summary.isEchoLoop || request.canDiscardEmittedBlocks === true);
4630
5045
  if (mayRetry) {
4631
5046
  retryCount++;
@@ -4637,15 +5052,39 @@ ${systemPrompt}` : ""}`;
4637
5052
  }
4638
5053
  if (summary.isEchoLoop) {
4639
5054
  assembler.stripEcho();
4640
- console.warn(`[kiro-core] Echo loop \u2014 stripping "Continue" response`);
5055
+ const alsoEmpty = emptyAttempts > 0 ? ` (plus ${describeAttempts(emptyAttempts)} with no text at all)` : "";
5056
+ console.warn(`[kiro-core] Echo loop persisted across ${describeAttempts(echoAttempts)}${alsoEmpty} \u2014 stripping "Continue" response (${summary.responseText.length} chars)`);
5057
+ 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
5058
  } else {
4642
- console.warn(`[kiro-core] Empty response after ${maxRetries} retries \u2014 giving up on this turn`);
5059
+ const alsoEchoed = echoAttempts > 0 ? ` (plus ${describeAttempts(echoAttempts)} that echoed the continuation prompt)` : "";
5060
+ console.warn(`[kiro-core] Empty response on ${describeAttempts(emptyAttempts)}${alsoEchoed}, retry budget exhausted \u2014 returning stopReason:"${summary.stopReason}" to avoid agent loop stall`);
5061
+ errorMessage = `Kiro returned no text and no tool calls on ${describeAttempts(emptyAttempts)}${alsoEchoed}; retry budget exhausted, ${describeReturnedContent(assembler.contentKinds())} with stopReason:"${summary.stopReason}"`;
4643
5062
  }
4644
5063
  }
4645
- const { stopReason, usage } = assembler.complete();
5064
+ if (summary.droppedToolCalls.length > 0) {
5065
+ const names = describeDroppedToolNames(summary.droppedToolCalls);
5066
+ const one = summary.droppedToolCalls.length === 1;
5067
+ 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}"`;
5068
+ errorMessage = errorMessage ? `${errorMessage}. ${dropDiagnostic}` : dropDiagnostic;
5069
+ }
5070
+ const { stopReason, usage, wireUsage, metering } = assembler.complete();
4646
5071
  yield* assembler.takeEvents();
5072
+ if (!errorMessage) {
5073
+ const estimatedRead = applyCacheEstimate(conversationId, usage, wireUsage, usageTracking);
5074
+ if (estimatedRead > 0) {
5075
+ debugLog("usage.estimate", {
5076
+ conversationId,
5077
+ estimatedRead,
5078
+ input: usage.input,
5079
+ cacheRead: usage.cacheRead
5080
+ });
5081
+ }
5082
+ const estimatedCost = estimateKiroCreditCost(usageTracking, metering);
5083
+ if (estimatedCost !== void 0)
5084
+ usage.cost.total = estimatedCost;
5085
+ }
4647
5086
  yield { type: "usage", usage };
4648
- yield { type: "done", stopReason };
5087
+ yield { type: "done", stopReason, ...errorMessage ? { errorMessage } : {} };
4649
5088
  return;
4650
5089
  }
4651
5090
  }
@@ -5023,10 +5462,20 @@ function streamKiroForOmp(model, context, options) {
5023
5462
  if (event.usage.contextPercent !== void 0) {
5024
5463
  output.usage.contextPercent = event.usage.contextPercent;
5025
5464
  }
5465
+ if (event.usage.credits !== void 0) {
5466
+ output.usage.credits = event.usage.credits;
5467
+ }
5468
+ if (event.usage.creditUnit !== void 0) {
5469
+ output.usage.creditUnit = event.usage.creditUnit;
5470
+ }
5471
+ if (event.usage.cacheEstimated !== void 0) {
5472
+ output.usage.cacheEstimated = event.usage.cacheEstimated;
5473
+ }
5026
5474
  break;
5027
5475
  }
5028
5476
  case "done":
5029
5477
  output.stopReason = event.stopReason;
5478
+ if (event.errorMessage) output.errorMessage = event.errorMessage;
5030
5479
  stream.push({ type: "done", reason: event.stopReason, message: output });
5031
5480
  break;
5032
5481
  }