@pentoshi/clai 4.11.26 → 4.11.27

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 (276) hide show
  1. package/dist/agent/compaction/summary-execution.js +11 -1
  2. package/dist/agent/compaction/summary-execution.js.map +1 -1
  3. package/dist/agent/context-manager.d.ts +0 -1
  4. package/dist/agent/context-manager.js +0 -1
  5. package/dist/agent/context-manager.js.map +1 -1
  6. package/dist/agent/events.d.ts +1 -5
  7. package/dist/agent/plan/actions/plan-clear.js +1 -1
  8. package/dist/agent/plan/actions/plan-clear.js.map +1 -1
  9. package/dist/agent/plan/actions/plan-create.js +38 -17
  10. package/dist/agent/plan/actions/plan-create.js.map +1 -1
  11. package/dist/agent/plan/actions/task-add.js +15 -4
  12. package/dist/agent/plan/actions/task-add.js.map +1 -1
  13. package/dist/agent/plan/actions/task-move.js +16 -5
  14. package/dist/agent/plan/actions/task-move.js.map +1 -1
  15. package/dist/agent/plan/handle-plan-tool.js +20 -6
  16. package/dist/agent/plan/handle-plan-tool.js.map +1 -1
  17. package/dist/agent/reliability-policy.d.ts +0 -10
  18. package/dist/agent/reliability-policy.js +0 -23
  19. package/dist/agent/reliability-policy.js.map +1 -1
  20. package/dist/agent/request-accounting.js +10 -10
  21. package/dist/agent/request-accounting.js.map +1 -1
  22. package/dist/agent/request-budget.d.ts +11 -18
  23. package/dist/agent/request-budget.js +14 -53
  24. package/dist/agent/request-budget.js.map +1 -1
  25. package/dist/agent/runner.js +16 -12
  26. package/dist/agent/runner.js.map +1 -1
  27. package/dist/agent/stream-recovery.d.ts +1 -2
  28. package/dist/agent/stream-recovery.js +5 -10
  29. package/dist/agent/stream-recovery.js.map +1 -1
  30. package/dist/agent/subagents/worker.js +3 -2
  31. package/dist/agent/subagents/worker.js.map +1 -1
  32. package/dist/agent/turn/automatic-compaction-execution.js +2 -2
  33. package/dist/agent/turn/automatic-compaction-execution.js.map +1 -1
  34. package/dist/agent/turn/compaction-admission.d.ts +2 -2
  35. package/dist/agent/turn/compaction-admission.js +14 -17
  36. package/dist/agent/turn/compaction-admission.js.map +1 -1
  37. package/dist/agent/turn/compaction-coordinator.d.ts +2 -1
  38. package/dist/agent/turn/compaction-coordinator.js +12 -7
  39. package/dist/agent/turn/compaction-coordinator.js.map +1 -1
  40. package/dist/agent/turn/compaction-durable-envelope.js +1 -1
  41. package/dist/agent/turn/compaction-durable-envelope.js.map +1 -1
  42. package/dist/agent/turn/compaction-replay-selection.js +2 -3
  43. package/dist/agent/turn/compaction-replay-selection.js.map +1 -1
  44. package/dist/agent/turn/compaction-summarizer.js +1 -1
  45. package/dist/agent/turn/compaction-summarizer.js.map +1 -1
  46. package/dist/agent/turn/loop/answer-path.js +2 -2
  47. package/dist/agent/turn/loop/answer-path.js.map +1 -1
  48. package/dist/agent/turn/loop/completion-interpretation.d.ts +0 -1
  49. package/dist/agent/turn/loop/completion-interpretation.js +2 -7
  50. package/dist/agent/turn/loop/completion-interpretation.js.map +1 -1
  51. package/dist/agent/turn/loop/deps.d.ts +1 -0
  52. package/dist/agent/turn/loop/empty-response.d.ts +1 -1
  53. package/dist/agent/turn/loop/empty-response.js +2 -4
  54. package/dist/agent/turn/loop/empty-response.js.map +1 -1
  55. package/dist/agent/turn/loop/output-budget.d.ts +0 -1
  56. package/dist/agent/turn/loop/output-budget.js +4 -9
  57. package/dist/agent/turn/loop/output-budget.js.map +1 -1
  58. package/dist/agent/turn/loop/request-assembly.d.ts +1 -3
  59. package/dist/agent/turn/loop/request-assembly.js +15 -15
  60. package/dist/agent/turn/loop/request-assembly.js.map +1 -1
  61. package/dist/agent/turn/loop/round-request.js +1 -6
  62. package/dist/agent/turn/loop/round-request.js.map +1 -1
  63. package/dist/agent/turn/loop/run-rounds.js +9 -15
  64. package/dist/agent/turn/loop/run-rounds.js.map +1 -1
  65. package/dist/agent/turn/loop/state.d.ts +2 -3
  66. package/dist/agent/turn/loop/state.js +1 -2
  67. package/dist/agent/turn/loop/state.js.map +1 -1
  68. package/dist/agent/turn/loop/stream-failure.d.ts +0 -1
  69. package/dist/agent/turn/loop/stream-failure.js +5 -5
  70. package/dist/agent/turn/loop/stream-failure.js.map +1 -1
  71. package/dist/agent/turn/loop/stream-request.d.ts +0 -1
  72. package/dist/agent/turn/loop/stream-request.js +1 -3
  73. package/dist/agent/turn/loop/stream-request.js.map +1 -1
  74. package/dist/agent/turn/plan-persistence.d.ts +4 -5
  75. package/dist/agent/turn/plan-persistence.js +13 -4
  76. package/dist/agent/turn/plan-persistence.js.map +1 -1
  77. package/dist/agent/turn/provider-measurement.d.ts +6 -0
  78. package/dist/agent/turn/provider-measurement.js +19 -0
  79. package/dist/agent/turn/provider-measurement.js.map +1 -0
  80. package/dist/agent/turn/responder-job-linkage.js +2 -2
  81. package/dist/agent/turn/responder-job-linkage.js.map +1 -1
  82. package/dist/agent/turn/setup/compaction-services.d.ts +2 -1
  83. package/dist/agent/turn/setup/compaction-services.js +2 -1
  84. package/dist/agent/turn/setup/compaction-services.js.map +1 -1
  85. package/dist/agent/turn/setup/task-gate-setup.d.ts +3 -3
  86. package/dist/agent/turn/setup/task-gate-setup.js.map +1 -1
  87. package/dist/agent/turn/task-autostart.d.ts +2 -2
  88. package/dist/agent/turn/task-autostart.js +7 -9
  89. package/dist/agent/turn/task-autostart.js.map +1 -1
  90. package/dist/agent/turn/task-credit.d.ts +2 -2
  91. package/dist/agent/turn/task-credit.js +8 -5
  92. package/dist/agent/turn/task-credit.js.map +1 -1
  93. package/dist/agent/turn/tool-execution/deps.d.ts +3 -3
  94. package/dist/agent/turn/tool-execution/dispatch.js +3 -5
  95. package/dist/agent/turn/tool-execution/dispatch.js.map +1 -1
  96. package/dist/agent/turn/tool-execution/meta-tools.d.ts +1 -1
  97. package/dist/agent/turn/tool-execution/meta-tools.js +13 -11
  98. package/dist/agent/turn/tool-execution/meta-tools.js.map +1 -1
  99. package/dist/agent/turn/tool-execution/plan-tool-ledger.d.ts +2 -2
  100. package/dist/agent/turn/tool-execution/plan-tool-ledger.js +6 -6
  101. package/dist/agent/turn/tool-execution/plan-tool-ledger.js.map +1 -1
  102. package/dist/agent/turn/tool-execution/result-framing.d.ts +1 -1
  103. package/dist/agent/turn/tool-execution/result-framing.js +1 -2
  104. package/dist/agent/turn/tool-execution/result-framing.js.map +1 -1
  105. package/dist/agent/turn/tool-execution/single-tool.js +28 -16
  106. package/dist/agent/turn/tool-execution/single-tool.js.map +1 -1
  107. package/dist/app/adapters/agent-event-adapter.js +7 -7
  108. package/dist/app/adapters/agent-event-adapter.js.map +1 -1
  109. package/dist/app/controllers/plan-controller.js +4 -1
  110. package/dist/app/controllers/plan-controller.js.map +1 -1
  111. package/dist/app/controllers/session-compact-helper.js +11 -8
  112. package/dist/app/controllers/session-compact-helper.js.map +1 -1
  113. package/dist/app/controllers/session-context-usage.d.ts +6 -4
  114. package/dist/app/controllers/session-context-usage.js +94 -33
  115. package/dist/app/controllers/session-context-usage.js.map +1 -1
  116. package/dist/app/controllers/session-controller.d.ts +5 -6
  117. package/dist/app/controllers/session-controller.js +48 -40
  118. package/dist/app/controllers/session-controller.js.map +1 -1
  119. package/dist/app/controllers/session-responder.d.ts +2 -0
  120. package/dist/app/controllers/session-responder.js +18 -0
  121. package/dist/app/controllers/session-responder.js.map +1 -1
  122. package/dist/app/events/app-event.d.ts +3 -5
  123. package/dist/app/events/app-event.js.map +1 -1
  124. package/dist/classic/panels/pager-panel.js +3 -2
  125. package/dist/classic/panels/pager-panel.js.map +1 -1
  126. package/dist/commands/providers.d.ts +2 -0
  127. package/dist/commands/providers.js +49 -7
  128. package/dist/commands/providers.js.map +1 -1
  129. package/dist/llm/anthropic.d.ts +1 -0
  130. package/dist/llm/anthropic.js +20 -21
  131. package/dist/llm/anthropic.js.map +1 -1
  132. package/dist/llm/capabilities.d.ts +1 -4
  133. package/dist/llm/capabilities.js +24 -19
  134. package/dist/llm/capabilities.js.map +1 -1
  135. package/dist/llm/capability/state.js +0 -17
  136. package/dist/llm/capability/state.js.map +1 -1
  137. package/dist/llm/catalog-facts.js +40 -11
  138. package/dist/llm/catalog-facts.js.map +1 -1
  139. package/dist/llm/catalog-prefetch.js +9 -6
  140. package/dist/llm/catalog-prefetch.js.map +1 -1
  141. package/dist/llm/context-snapshot.d.ts +7 -3
  142. package/dist/llm/context-snapshot.js +35 -14
  143. package/dist/llm/context-snapshot.js.map +1 -1
  144. package/dist/llm/context-windows.d.ts +16 -0
  145. package/dist/llm/context-windows.js +54 -19
  146. package/dist/llm/context-windows.js.map +1 -1
  147. package/dist/llm/custom-providers.js +5 -4
  148. package/dist/llm/custom-providers.js.map +1 -1
  149. package/dist/llm/explabs.js +35 -26
  150. package/dist/llm/explabs.js.map +1 -1
  151. package/dist/llm/free.d.ts +1 -1
  152. package/dist/llm/free.js +26 -18
  153. package/dist/llm/free.js.map +1 -1
  154. package/dist/llm/gemini.js +25 -12
  155. package/dist/llm/gemini.js.map +1 -1
  156. package/dist/llm/kiro-auth.d.ts +34 -3
  157. package/dist/llm/kiro-auth.js +315 -144
  158. package/dist/llm/kiro-auth.js.map +1 -1
  159. package/dist/llm/kiro-import-sources.d.ts +10 -0
  160. package/dist/llm/kiro-import-sources.js +117 -0
  161. package/dist/llm/kiro-import-sources.js.map +1 -0
  162. package/dist/llm/kiro-reasoning.d.ts +28 -0
  163. package/dist/llm/kiro-reasoning.js +150 -0
  164. package/dist/llm/kiro-reasoning.js.map +1 -0
  165. package/dist/llm/kiro-social-device.d.ts +21 -0
  166. package/dist/llm/kiro-social-device.js +101 -0
  167. package/dist/llm/kiro-social-device.js.map +1 -0
  168. package/dist/llm/kiro.d.ts +3 -1
  169. package/dist/llm/kiro.js +653 -187
  170. package/dist/llm/kiro.js.map +1 -1
  171. package/dist/llm/learned-capabilities.js +1 -3
  172. package/dist/llm/learned-capabilities.js.map +1 -1
  173. package/dist/llm/merge-gateway.js +18 -13
  174. package/dist/llm/merge-gateway.js.map +1 -1
  175. package/dist/llm/model-limit-store.d.ts +15 -0
  176. package/dist/llm/model-limit-store.js +121 -0
  177. package/dist/llm/model-limit-store.js.map +1 -0
  178. package/dist/llm/nvidia.d.ts +1 -0
  179. package/dist/llm/nvidia.js +15 -17
  180. package/dist/llm/nvidia.js.map +1 -1
  181. package/dist/llm/ollama-model-limits.d.ts +4 -0
  182. package/dist/llm/ollama-model-limits.js +91 -0
  183. package/dist/llm/ollama-model-limits.js.map +1 -0
  184. package/dist/llm/ollama.js +2 -1
  185. package/dist/llm/ollama.js.map +1 -1
  186. package/dist/llm/orcarouter.d.ts +1 -0
  187. package/dist/llm/orcarouter.js +8 -1
  188. package/dist/llm/orcarouter.js.map +1 -1
  189. package/dist/llm/provider-context-usage.d.ts +9 -0
  190. package/dist/llm/provider-context-usage.js +39 -0
  191. package/dist/llm/provider-context-usage.js.map +1 -0
  192. package/dist/llm/qwen-cloud.d.ts +1 -0
  193. package/dist/llm/qwen-cloud.js +44 -9
  194. package/dist/llm/qwen-cloud.js.map +1 -1
  195. package/dist/llm/reasoning-errors.d.ts +1 -0
  196. package/dist/llm/reasoning-errors.js +6 -0
  197. package/dist/llm/reasoning-errors.js.map +1 -1
  198. package/dist/llm/routing/attempt-complete.js +9 -2
  199. package/dist/llm/routing/attempt-complete.js.map +1 -1
  200. package/dist/llm/routing/attempt-request.d.ts +1 -0
  201. package/dist/llm/routing/attempt-request.js +23 -0
  202. package/dist/llm/routing/attempt-request.js.map +1 -1
  203. package/dist/llm/routing/attempt-stream.js +15 -2
  204. package/dist/llm/routing/attempt-stream.js.map +1 -1
  205. package/dist/llm/token-estimate-calibration.js +1 -2
  206. package/dist/llm/token-estimate-calibration.js.map +1 -1
  207. package/dist/llm/token-usage.d.ts +2 -0
  208. package/dist/llm/token-usage.js +1 -1
  209. package/dist/llm/token-usage.js.map +1 -1
  210. package/dist/llm/wire/anthropic-model-catalog.d.ts +1 -0
  211. package/dist/llm/wire/anthropic-model-catalog.js +30 -0
  212. package/dist/llm/wire/anthropic-model-catalog.js.map +1 -0
  213. package/dist/llm/wire/dashscope-model-catalog.d.ts +1 -0
  214. package/dist/llm/wire/dashscope-model-catalog.js +53 -0
  215. package/dist/llm/wire/dashscope-model-catalog.js.map +1 -0
  216. package/dist/llm/wire/orcarouter-model-catalog.d.ts +2 -0
  217. package/dist/llm/wire/orcarouter-model-catalog.js +91 -0
  218. package/dist/llm/wire/orcarouter-model-catalog.js.map +1 -0
  219. package/dist/noninteractive/stream-blocks.d.ts +0 -3
  220. package/dist/noninteractive/stream-blocks.js +0 -9
  221. package/dist/noninteractive/stream-blocks.js.map +1 -1
  222. package/dist/noninteractive/stream-renderer.js +1 -4
  223. package/dist/noninteractive/stream-renderer.js.map +1 -1
  224. package/dist/store/config/endpoints.d.ts +0 -6
  225. package/dist/store/config/endpoints.js +1 -35
  226. package/dist/store/config/endpoints.js.map +1 -1
  227. package/dist/store/context-limits.d.ts +4 -0
  228. package/dist/store/context-limits.js +29 -0
  229. package/dist/store/context-limits.js.map +1 -0
  230. package/dist/store/plan/jsonl-backend.js +1 -4
  231. package/dist/store/plan/jsonl-backend.js.map +1 -1
  232. package/dist/store/plan/mutation.js +31 -1
  233. package/dist/store/plan/mutation.js.map +1 -1
  234. package/dist/store/plan/sqlite-backend.js +27 -9
  235. package/dist/store/plan/sqlite-backend.js.map +1 -1
  236. package/dist/tui-v2/components/pager/pager-line.d.ts +2 -0
  237. package/dist/tui-v2/components/pager/pager-line.js +1 -2
  238. package/dist/tui-v2/components/pager/pager-line.js.map +1 -1
  239. package/dist/tui-v2/components/pager/pager.js +5 -2
  240. package/dist/tui-v2/components/pager/pager.js.map +1 -1
  241. package/dist/tui-v2/components/transcript/tool-card.js +1 -1
  242. package/dist/tui-v2/components/transcript/tool-card.js.map +1 -1
  243. package/dist/types.d.ts +3 -1
  244. package/dist/types.js.map +1 -1
  245. package/dist/ui-core/bootstrap/composition-root.js +8 -5
  246. package/dist/ui-core/bootstrap/composition-root.js.map +1 -1
  247. package/dist/ui-core/bootstrap/session-resume.js +1 -2
  248. package/dist/ui-core/bootstrap/session-resume.js.map +1 -1
  249. package/dist/ui-core/commands/key-commands.d.ts +2 -0
  250. package/dist/ui-core/commands/key-commands.js +76 -20
  251. package/dist/ui-core/commands/key-commands.js.map +1 -1
  252. package/dist/ui-core/commands/session-commands.js +33 -9
  253. package/dist/ui-core/commands/session-commands.js.map +1 -1
  254. package/dist/ui-core/rendering/batch-sections.js +10 -1
  255. package/dist/ui-core/rendering/batch-sections.js.map +1 -1
  256. package/dist/ui-core/rendering/context-limit.d.ts +0 -1
  257. package/dist/ui-core/rendering/context-limit.js +2 -2
  258. package/dist/ui-core/rendering/context-limit.js.map +1 -1
  259. package/dist/ui-core/rendering/subagent-presentation.d.ts +2 -0
  260. package/dist/ui-core/rendering/subagent-presentation.js +20 -7
  261. package/dist/ui-core/rendering/subagent-presentation.js.map +1 -1
  262. package/dist/ui-core/rendering/subagent-source.js +15 -8
  263. package/dist/ui-core/rendering/subagent-source.js.map +1 -1
  264. package/dist/ui-core/state/transcript/apply-app-event.js +0 -1
  265. package/dist/ui-core/state/transcript/apply-app-event.js.map +1 -1
  266. package/dist/ui-core/state/transcript-types.js +1 -1
  267. package/dist/ui-core/state/transcript-types.js.map +1 -1
  268. package/dist/version.generated.d.ts +2 -2
  269. package/dist/version.generated.js +2 -2
  270. package/package.json +1 -1
  271. package/dist/app/controllers/session-context-limits.d.ts +0 -8
  272. package/dist/app/controllers/session-context-limits.js +0 -35
  273. package/dist/app/controllers/session-context-limits.js.map +0 -1
  274. package/dist/store/config/compaction.d.ts +0 -1
  275. package/dist/store/config/compaction.js +0 -2
  276. package/dist/store/config/compaction.js.map +0 -1
@@ -3,6 +3,7 @@ import { join } from "node:path";
3
3
  import { readFile, readdir } from "node:fs/promises";
4
4
  import { randomBytes, createHash } from "node:crypto";
5
5
  import { createServer } from "node:http";
6
+ import { linkedSsoClientRegistration, normalizeIdpScope, parseStoredExpiry, preferKiroSsoTokenFiles, readKiroIdeProfileArn, validateExternalIdpTokenEndpoint, } from "./kiro-import-sources.js";
6
7
  export const KIRO_DEFAULT_REGION = "us-east-1";
7
8
  export const KIRO_AUTH_SERVICE = "https://prod.us-east-1.auth.desktop.kiro.dev";
8
9
  export const KIRO_START_URL = "https://view.awsapps.com/start";
@@ -16,6 +17,22 @@ export const KIRO_CLI_CALLBACK_PATH = "/oauth/callback";
16
17
  export const KIRO_CLI_SIGNIN_CALLBACK_PATH = "/signin/callback";
17
18
  export const KIRO_DESKTOP_UA_VERSION = "0.7.45";
18
19
  export const AWS_REGION_PATTERN = /^[a-z]{2}-[a-z]+-\d{1,2}$/;
20
+ const KIRO_API_REGION_MAP = {
21
+ "us-west-1": "us-east-1",
22
+ "us-west-2": "us-east-1",
23
+ "us-east-2": "us-east-1",
24
+ "ap-southeast-1": "us-east-1",
25
+ "ap-southeast-2": "us-east-1",
26
+ "ap-northeast-1": "us-east-1",
27
+ "ap-south-1": "us-east-1",
28
+ "eu-west-1": "eu-central-1",
29
+ "eu-west-2": "eu-central-1",
30
+ "eu-west-3": "eu-central-1",
31
+ "eu-north-1": "eu-central-1",
32
+ "eu-south-1": "eu-central-1",
33
+ "eu-south-2": "eu-central-1",
34
+ "eu-central-2": "eu-central-1",
35
+ };
19
36
  export function assertValidAwsRegion(region) {
20
37
  const value = (region ?? KIRO_DEFAULT_REGION).trim().toLowerCase();
21
38
  if (!AWS_REGION_PATTERN.test(value)) {
@@ -23,6 +40,16 @@ export function assertValidAwsRegion(region) {
23
40
  }
24
41
  return value;
25
42
  }
43
+ export function resolveKiroApiRegion(region) {
44
+ const value = assertValidAwsRegion(region);
45
+ return KIRO_API_REGION_MAP[value] ?? value;
46
+ }
47
+ export function resolveKiroRuntimeRegion(credential) {
48
+ const arnRegion = credential.profileArn?.split(":")[3];
49
+ if (arnRegion && AWS_REGION_PATTERN.test(arnRegion))
50
+ return arnRegion;
51
+ return resolveKiroApiRegion(credential.region);
52
+ }
26
53
  function asRecord(value) {
27
54
  return value && typeof value === "object" && !Array.isArray(value)
28
55
  ? value
@@ -59,6 +86,8 @@ export function encodeKiroKey(credential) {
59
86
  s: credential.clientSecret ?? "",
60
87
  u: credential.startUrl ?? "",
61
88
  k: credential.apiKey ?? "",
89
+ ...(credential.tokenEndpoint ? { t: credential.tokenEndpoint } : {}),
90
+ ...(credential.scope ? { o: credential.scope } : {}),
62
91
  };
63
92
  return (KIRO_KEY_PREFIX +
64
93
  Buffer.from(JSON.stringify(payload), "utf8").toString("base64url"));
@@ -82,6 +111,8 @@ export function decodeKiroKey(value) {
82
111
  clientSecret: typeof json?.s === "string" && json.s ? json.s : undefined,
83
112
  startUrl: typeof json?.u === "string" && json.u ? json.u : undefined,
84
113
  apiKey: typeof json?.k === "string" && json.k ? json.k : undefined,
114
+ ...(typeof json?.t === "string" && json.t ? { tokenEndpoint: json.t } : {}),
115
+ ...(typeof json?.o === "string" && json.o ? { scope: json.o } : {}),
85
116
  };
86
117
  }
87
118
  catch {
@@ -212,7 +243,7 @@ export async function pollKiroDeviceAuth(start, options = {}) {
212
243
  const expiresAt = Date.now() + expiresIn * 1000;
213
244
  let profileArn;
214
245
  try {
215
- profileArn = await listAvailableProfiles(accessToken, start.region);
246
+ profileArn = await discoverKiroProfileArn(accessToken, start.region);
216
247
  }
217
248
  catch {
218
249
  profileArn = undefined;
@@ -430,6 +461,8 @@ export function kiroDesktopUserAgent() {
430
461
  return `KiroIDE-${KIRO_DESKTOP_UA_VERSION}-${kiroMachineFingerprint()}`;
431
462
  }
432
463
  export function isHeadlessEnvironment() {
464
+ if (process.env.CLAI_NO_BROWSER || process.env.BROWSER === "none")
465
+ return true;
433
466
  if (process.env.SSH_CONNECTION || process.env.SSH_CLIENT || process.env.SSH_TTY) {
434
467
  return true;
435
468
  }
@@ -490,7 +523,7 @@ export function parseKiroCliCallback(input) {
490
523
  }
491
524
  const loginOption = pairs.get("login_option");
492
525
  if (!loginOption) {
493
- throw new Error("missing Kiro login_option");
526
+ throw new Error("missing Kiro login_option — paste the complete redirect URL from the browser address bar");
494
527
  }
495
528
  const state = pairs.get("state") || undefined;
496
529
  return { code, loginOption, state, path };
@@ -624,61 +657,65 @@ export function listenForKiroCliCallback(options) {
624
657
  },
625
658
  };
626
659
  }
627
- export async function refreshKiroToken(refreshToken, options = {}) {
628
- const { clientId, clientSecret, region } = options;
629
- if (clientId && clientSecret) {
630
- const safeRegion = assertValidAwsRegion(region);
631
- const endpoint = `https://oidc.${safeRegion}.amazonaws.com/token`;
632
- const response = await fetch(endpoint, {
633
- method: "POST",
634
- headers: {
635
- "Content-Type": "application/json",
636
- Accept: "application/json",
637
- },
638
- body: JSON.stringify({
639
- clientId,
640
- clientSecret,
641
- refreshToken,
642
- grantType: "refresh_token",
643
- }),
644
- signal: AbortSignal.timeout(30_000),
645
- });
646
- if (!response.ok) {
647
- const errorText = await response.text();
648
- throw new Error(`Kiro token refresh failed (${response.status}): ${errorText}`);
649
- }
650
- const json = asRecord(await response.json().catch(() => ({})));
651
- const accessToken = typeof json?.accessToken === "string" ? json.accessToken : "";
652
- if (!accessToken) {
653
- throw new Error("Invalid token refresh response from AWS OIDC");
654
- }
655
- const newRefreshToken = typeof json?.refreshToken === "string"
656
- ? json.refreshToken
657
- : refreshToken;
658
- const expiresIn = typeof json?.expiresIn === "number" ? json.expiresIn : 3600;
659
- let profileArn = options.profileArn;
660
- if (!profileArn) {
661
- try {
662
- profileArn = await listAvailableProfiles(accessToken, safeRegion);
663
- }
664
- catch {
665
- profileArn = undefined;
666
- }
667
- }
668
- return {
669
- accessToken,
670
- refreshToken: newRefreshToken,
671
- profileArn,
672
- expiresAt: Date.now() + expiresIn * 1000,
673
- authMethod: options.authMethod ?? "builder-id",
674
- region: safeRegion,
675
- clientId,
676
- clientSecret,
677
- startUrl: options.startUrl,
678
- };
660
+ export class KiroRefreshError extends Error {
661
+ status;
662
+ reauthRequired;
663
+ constructor(message, status, reauthRequired) {
664
+ super(message);
665
+ this.status = status;
666
+ this.reauthRequired = reauthRequired;
667
+ this.name = "KiroRefreshError";
679
668
  }
680
- const endpoint = `${KIRO_AUTH_SERVICE}/refreshToken`;
681
- const response = await fetch(endpoint, {
669
+ }
670
+ const UNRECOVERABLE_REFRESH = /invalid_grant|InvalidGrantException|ExpiredTokenException|invalid_client|InvalidClientException|UnauthorizedClientException/i;
671
+ async function refreshFailure(response) {
672
+ const errorText = await response.text().catch(() => "");
673
+ const reauthRequired = UNRECOVERABLE_REFRESH.test(errorText) ||
674
+ response.status === 400 ||
675
+ response.status === 401;
676
+ const hint = reauthRequired ? " — run `clai auth kiro` to sign in again" : "";
677
+ return new KiroRefreshError(`Kiro token refresh failed (${response.status})${hint}: ${errorText.slice(0, 300)}`, response.status, reauthRequired);
678
+ }
679
+ async function refreshViaOidc(refreshToken, options) {
680
+ const safeRegion = assertValidAwsRegion(options.region);
681
+ const response = await fetch(`https://oidc.${safeRegion}.amazonaws.com/token`, {
682
+ method: "POST",
683
+ headers: {
684
+ "Content-Type": "application/json",
685
+ Accept: "application/json",
686
+ },
687
+ body: JSON.stringify({
688
+ clientId: options.clientId,
689
+ clientSecret: options.clientSecret,
690
+ refreshToken,
691
+ grantType: "refresh_token",
692
+ }),
693
+ signal: AbortSignal.timeout(30_000),
694
+ });
695
+ if (!response.ok)
696
+ throw await refreshFailure(response);
697
+ const json = asRecord(await response.json().catch(() => ({})));
698
+ const accessToken = typeof json?.accessToken === "string" ? json.accessToken : "";
699
+ if (!accessToken) {
700
+ throw new Error("Invalid token refresh response from AWS OIDC");
701
+ }
702
+ const expiresIn = typeof json?.expiresIn === "number" ? json.expiresIn : 3600;
703
+ const profileArn = options.profileArn ??
704
+ await discoverKiroProfileArn(accessToken, safeRegion).catch(() => undefined);
705
+ return {
706
+ accessToken,
707
+ refreshToken: typeof json?.refreshToken === "string" ? json.refreshToken : refreshToken,
708
+ profileArn,
709
+ expiresAt: Date.now() + expiresIn * 1000,
710
+ authMethod: options.authMethod ?? "builder-id",
711
+ region: safeRegion,
712
+ clientId: options.clientId,
713
+ clientSecret: options.clientSecret,
714
+ startUrl: options.startUrl,
715
+ };
716
+ }
717
+ async function refreshViaSocial(refreshToken, options) {
718
+ const response = await fetch(`${KIRO_AUTH_SERVICE}/refreshToken`, {
682
719
  method: "POST",
683
720
  headers: {
684
721
  "Content-Type": "application/json",
@@ -688,29 +725,100 @@ export async function refreshKiroToken(refreshToken, options = {}) {
688
725
  body: JSON.stringify({ refreshToken }),
689
726
  signal: AbortSignal.timeout(30_000),
690
727
  });
691
- if (!response.ok) {
692
- const errorText = await response.text();
693
- throw new Error(`Kiro token refresh failed (${response.status}): ${errorText}`);
694
- }
728
+ if (!response.ok)
729
+ throw await refreshFailure(response);
695
730
  const json = asRecord(await response.json().catch(() => ({})));
696
731
  const accessToken = typeof json?.accessToken === "string" ? json.accessToken : "";
697
732
  if (!accessToken) {
698
733
  throw new Error("Invalid token refresh response from Kiro service");
699
734
  }
700
- const newRefreshToken = typeof json?.refreshToken === "string" ? json.refreshToken : refreshToken;
701
- const profileArn = typeof json?.profileArn === "string"
702
- ? json.profileArn
703
- : options.profileArn;
704
735
  const expiresIn = typeof json?.expiresIn === "number" ? json.expiresIn : 3600;
705
736
  return {
706
737
  accessToken,
707
- refreshToken: newRefreshToken,
708
- profileArn,
738
+ refreshToken: typeof json?.refreshToken === "string" ? json.refreshToken : refreshToken,
739
+ profileArn: typeof json?.profileArn === "string" ? json.profileArn : options.profileArn,
709
740
  expiresAt: Date.now() + expiresIn * 1000,
710
741
  authMethod: options.authMethod ?? "imported",
711
742
  region: options.region ?? KIRO_DEFAULT_REGION,
712
743
  };
713
744
  }
745
+ function usesSocialRefresh(authMethod) {
746
+ return authMethod === "google" || authMethod === "github";
747
+ }
748
+ async function refreshViaExternalIdp(refreshToken, options) {
749
+ if (!options.clientId || !options.tokenEndpoint) {
750
+ throw new KiroRefreshError("Kiro external IdP credential is missing clientId or tokenEndpoint — sign in again in Kiro and re-import", 400, true);
751
+ }
752
+ const endpoint = validateExternalIdpTokenEndpoint(options.tokenEndpoint);
753
+ const body = new URLSearchParams({
754
+ grant_type: "refresh_token",
755
+ client_id: options.clientId,
756
+ refresh_token: refreshToken,
757
+ ...(options.scope ? { scope: options.scope } : {}),
758
+ });
759
+ const response = await fetch(endpoint, {
760
+ method: "POST",
761
+ headers: {
762
+ "Content-Type": "application/x-www-form-urlencoded",
763
+ Accept: "application/json",
764
+ },
765
+ body: body.toString(),
766
+ signal: AbortSignal.timeout(30_000),
767
+ });
768
+ if (!response.ok)
769
+ throw await refreshFailure(response);
770
+ const json = asRecord(await response.json().catch(() => ({})));
771
+ const accessToken = typeof json?.access_token === "string" ? json.access_token : "";
772
+ if (!accessToken) {
773
+ throw new Error("Invalid token refresh response from external identity provider");
774
+ }
775
+ const expiresIn = typeof json?.expires_in === "number" ? json.expires_in : 3600;
776
+ return {
777
+ accessToken,
778
+ refreshToken: typeof json?.refresh_token === "string" ? json.refresh_token : refreshToken,
779
+ profileArn: options.profileArn,
780
+ expiresAt: Date.now() + expiresIn * 1000,
781
+ authMethod: "external_idp",
782
+ region: options.region ?? KIRO_DEFAULT_REGION,
783
+ clientId: options.clientId,
784
+ tokenEndpoint: endpoint,
785
+ scope: options.scope,
786
+ };
787
+ }
788
+ export async function refreshKiroToken(refreshToken, options = {}) {
789
+ if (options.authMethod === "external_idp") {
790
+ return refreshViaExternalIdp(refreshToken, options);
791
+ }
792
+ const { clientId, clientSecret } = options;
793
+ if (!clientId || !clientSecret || usesSocialRefresh(options.authMethod)) {
794
+ return refreshViaSocial(refreshToken, options);
795
+ }
796
+ try {
797
+ return await refreshViaOidc(refreshToken, { ...options, clientId, clientSecret });
798
+ }
799
+ catch (error) {
800
+ const canTrySocial = options.authMethod === "imported" &&
801
+ error instanceof KiroRefreshError &&
802
+ error.reauthRequired;
803
+ if (!canTrySocial)
804
+ throw error;
805
+ return refreshViaSocial(refreshToken, options);
806
+ }
807
+ }
808
+ const KIRO_PROFILE_REGIONS = ["us-east-1", "eu-central-1"];
809
+ export async function discoverKiroProfileArn(accessToken, region) {
810
+ const regions = [...new Set([
811
+ ...(region && AWS_REGION_PATTERN.test(region) ? [region] : []),
812
+ ...KIRO_PROFILE_REGIONS,
813
+ ])];
814
+ for (const candidate of regions) {
815
+ const profileArn = await listAvailableProfiles(accessToken, candidate)
816
+ .catch(() => undefined);
817
+ if (profileArn)
818
+ return profileArn;
819
+ }
820
+ return undefined;
821
+ }
714
822
  export async function listAvailableProfiles(accessToken, region = KIRO_DEFAULT_REGION) {
715
823
  const safeRegion = assertValidAwsRegion(region);
716
824
  const endpoint = `https://codewhisperer.${safeRegion}.amazonaws.com`;
@@ -742,36 +850,13 @@ export async function listAvailableProfiles(accessToken, region = KIRO_DEFAULT_R
742
850
  return arnOf(match) ?? undefined;
743
851
  }
744
852
  export async function listAvailableApiKeyModels(apiKey, region = KIRO_DEFAULT_REGION) {
745
- const safeRegion = assertValidAwsRegion(region);
746
- const params = new URLSearchParams({ origin: "AI_EDITOR" });
747
- const endpoint = `https://q.${safeRegion}.amazonaws.com/ListAvailableModels?${params.toString()}`;
748
- const response = await fetch(endpoint, {
749
- method: "GET",
750
- headers: {
751
- Authorization: `Bearer ${apiKey}`,
752
- TokenType: "API_KEY",
753
- Accept: "application/json",
754
- "User-Agent": "AWS-SDK-JS/3.0.0 kiro-ide/1.0.0",
755
- "X-Amz-User-Agent": "aws-sdk-js/3.0.0 kiro-ide/1.0.0",
756
- },
757
- signal: AbortSignal.timeout(20_000),
853
+ const models = await listKiroModelsDetailed({
854
+ accessToken: apiKey,
855
+ apiKey,
856
+ authMethod: "api_key",
857
+ region,
758
858
  });
759
- if (!response.ok) {
760
- const errorText = await response.text();
761
- throw new Error(`Failed to list API key models (${response.status}): ${errorText}`);
762
- }
763
- const json = asRecord(await response.json().catch(() => ({})));
764
- const models = Array.isArray(json?.models) ? json.models : [];
765
- return models
766
- .map((m) => {
767
- const rec = asRecord(m);
768
- return typeof rec?.modelId === "string"
769
- ? rec.modelId
770
- : typeof rec?.id === "string"
771
- ? rec.id
772
- : "";
773
- })
774
- .filter(Boolean);
859
+ return models.map((model) => model.modelId);
775
860
  }
776
861
  export async function validateKiroApiKey(apiKey, region = KIRO_DEFAULT_REGION) {
777
862
  try {
@@ -802,10 +887,8 @@ export async function candidateKiroCredentialPaths() {
802
887
  const ssoCacheDir = join(home, ".aws", "sso", "cache");
803
888
  try {
804
889
  const files = await readdir(ssoCacheDir);
805
- for (const file of files) {
806
- if (file.endsWith(".json")) {
807
- paths.push(join(ssoCacheDir, file));
808
- }
890
+ for (const file of preferKiroSsoTokenFiles(files)) {
891
+ paths.push(join(ssoCacheDir, file));
809
892
  }
810
893
  }
811
894
  catch { }
@@ -836,36 +919,44 @@ export async function readKiroStoredAuth() {
836
919
  : undefined;
837
920
  if (!accessToken && !refreshToken)
838
921
  continue;
839
- const clientId = typeof rec.clientId === "string"
840
- ? rec.clientId
841
- : typeof rec.client_id === "string"
842
- ? rec.client_id
843
- : undefined;
844
- const clientSecret = typeof rec.clientSecret === "string"
845
- ? rec.clientSecret
846
- : typeof rec.client_secret === "string"
847
- ? rec.client_secret
848
- : undefined;
849
- const region = typeof rec.region === "string" && rec.region
922
+ const provider = typeof rec.provider === "string" ? rec.provider.toLowerCase() : "";
923
+ const externalIdp = provider === "externalidp" ||
924
+ (typeof rec.authMethod === "string" &&
925
+ rec.authMethod.toLowerCase() === "external_idp");
926
+ const socialProvider = provider === "google" || provider === "github" ? provider : undefined;
927
+ const linked = externalIdp || socialProvider
928
+ ? undefined
929
+ : await linkedSsoClientRegistration(path, rec);
930
+ const clientId = linked?.clientId ??
931
+ (typeof rec.clientId === "string"
932
+ ? rec.clientId
933
+ : typeof rec.client_id === "string"
934
+ ? rec.client_id
935
+ : undefined);
936
+ const clientSecret = linked?.clientSecret ??
937
+ (typeof rec.clientSecret === "string"
938
+ ? rec.clientSecret
939
+ : typeof rec.client_secret === "string"
940
+ ? rec.client_secret
941
+ : undefined);
942
+ const region = typeof rec.region === "string" && AWS_REGION_PATTERN.test(rec.region)
850
943
  ? rec.region
851
944
  : KIRO_DEFAULT_REGION;
852
- const profileArn = typeof rec.profileArn === "string"
945
+ const storedProfileArn = typeof rec.profileArn === "string"
853
946
  ? rec.profileArn
854
947
  : typeof rec.profile_arn === "string"
855
948
  ? rec.profile_arn
856
949
  : undefined;
950
+ const profileArn = storedProfileArn ?? await readKiroIdeProfileArn();
857
951
  const startUrl = typeof rec.startUrl === "string"
858
952
  ? rec.startUrl
859
953
  : typeof rec.start_url === "string"
860
954
  ? rec.start_url
861
955
  : undefined;
862
- let expiresAt;
863
- if (typeof rec.expiresAt === "number") {
864
- expiresAt = rec.expiresAt;
865
- }
866
- else if (typeof rec.expires_at === "string") {
867
- expiresAt = Date.parse(rec.expires_at);
868
- }
956
+ const expiresAt = parseStoredExpiry(rec.expiresAt) ?? parseStoredExpiry(rec.expires_at);
957
+ const authMethod = externalIdp
958
+ ? "external_idp"
959
+ : socialProvider ?? (clientId && clientSecret ? "builder-id" : "imported");
869
960
  return {
870
961
  accessToken,
871
962
  refreshToken,
@@ -873,9 +964,13 @@ export async function readKiroStoredAuth() {
873
964
  expiresAt,
874
965
  region,
875
966
  clientId,
876
- clientSecret,
967
+ clientSecret: externalIdp ? undefined : clientSecret,
877
968
  startUrl,
878
- authMethod: clientId && clientSecret ? "builder-id" : "imported",
969
+ authMethod,
970
+ ...(externalIdp && typeof rec.tokenEndpoint === "string"
971
+ ? { tokenEndpoint: rec.tokenEndpoint }
972
+ : {}),
973
+ ...(externalIdp ? { scope: normalizeIdpScope(rec.scopes ?? rec.scope) } : {}),
879
974
  };
880
975
  }
881
976
  const home = homedir();
@@ -913,10 +1008,14 @@ export async function readKiroStoredAuth() {
913
1008
  "codewhisperer:oidc:device-registration",
914
1009
  "codewhisperer:odic:device-registration",
915
1010
  ];
916
- const tokenRaw = tokenKeys.map((k) => byKey.get(k)).find((v) => v);
917
- if (!tokenRaw)
1011
+ const tokenKey = tokenKeys.find((k) => byKey.get(k));
1012
+ if (!tokenKey)
918
1013
  continue;
919
- const deviceRaw = deviceKeys.map((k) => byKey.get(k)).find((v) => v);
1014
+ const tokenRaw = byKey.get(tokenKey);
1015
+ const socialToken = tokenKey.includes(":social:");
1016
+ const deviceRaw = socialToken
1017
+ ? undefined
1018
+ : deviceKeys.map((k) => byKey.get(k)).find((v) => v);
920
1019
  try {
921
1020
  const parsed = JSON.parse(tokenRaw);
922
1021
  const device = deviceRaw
@@ -964,13 +1063,14 @@ export async function readKiroStoredAuth() {
964
1063
  : clientId && clientSecret
965
1064
  ? "builder-id"
966
1065
  : "imported";
1066
+ const storedRegion = [parsed.region, device?.region].find((value) => typeof value === "string" && AWS_REGION_PATTERN.test(value));
967
1067
  return {
968
1068
  accessToken,
969
1069
  refreshToken,
970
1070
  expiresAt,
971
1071
  profileArn,
972
1072
  authMethod,
973
- region: KIRO_DEFAULT_REGION,
1073
+ region: storedRegion ?? KIRO_DEFAULT_REGION,
974
1074
  clientId,
975
1075
  clientSecret,
976
1076
  };
@@ -989,9 +1089,7 @@ export async function importExistingKiroAuth() {
989
1089
  const stored = await readKiroStoredAuth();
990
1090
  if (!stored)
991
1091
  return undefined;
992
- if (stored.refreshToken &&
993
- stored.expiresAt !== undefined &&
994
- stored.expiresAt < Date.now()) {
1092
+ if (stored.refreshToken && kiroCredentialNeedsRefresh(stored)) {
995
1093
  try {
996
1094
  return await refreshKiroToken(stored.refreshToken, stored);
997
1095
  }
@@ -1001,16 +1099,35 @@ export async function importExistingKiroAuth() {
1001
1099
  }
1002
1100
  return stored;
1003
1101
  }
1004
- export async function maybeRefreshKiroCredential(key, storedRefreshToken) {
1102
+ export const KIRO_REFRESH_SKEW_MS = 5 * 60 * 1000;
1103
+ export function kiroCredentialNeedsRefresh(credential, now = Date.now()) {
1104
+ return (credential.expiresAt !== undefined &&
1105
+ Number.isFinite(credential.expiresAt) &&
1106
+ credential.expiresAt - now < KIRO_REFRESH_SKEW_MS);
1107
+ }
1108
+ const inflightKiroRefreshes = new Map();
1109
+ export function maybeRefreshKiroCredential(key, storedRefreshToken) {
1005
1110
  const decoded = decodeKiroKey(key);
1006
1111
  const refreshToken = decoded?.refreshToken || storedRefreshToken;
1007
1112
  if (!refreshToken)
1008
- return undefined;
1113
+ return Promise.resolve(undefined);
1114
+ const inflight = inflightKiroRefreshes.get(refreshToken);
1115
+ if (inflight)
1116
+ return inflight;
1117
+ const pending = refreshKiroKey(refreshToken, decoded).finally(() => {
1118
+ inflightKiroRefreshes.delete(refreshToken);
1119
+ });
1120
+ inflightKiroRefreshes.set(refreshToken, pending);
1121
+ return pending;
1122
+ }
1123
+ async function refreshKiroKey(refreshToken, decoded) {
1009
1124
  try {
1010
1125
  const refreshed = await refreshKiroToken(refreshToken, decoded);
1011
1126
  return encodeKiroKey(refreshed);
1012
1127
  }
1013
- catch {
1128
+ catch (error) {
1129
+ if (error instanceof KiroRefreshError && error.reauthRequired)
1130
+ throw error;
1014
1131
  return undefined;
1015
1132
  }
1016
1133
  }
@@ -1032,6 +1149,8 @@ function parseModelInfo(value) {
1032
1149
  const inputTypes = Array.isArray(rec.supportedInputTypes)
1033
1150
  ? rec.supportedInputTypes
1034
1151
  : undefined;
1152
+ const promptCaching = asRecord(rec.promptCaching);
1153
+ const requestFieldsSchema = asRecord(rec.additionalModelRequestFieldsSchema);
1035
1154
  return {
1036
1155
  modelId,
1037
1156
  modelName: typeof rec.modelName === "string" ? rec.modelName : undefined,
@@ -1042,6 +1161,14 @@ function parseModelInfo(value) {
1042
1161
  supportsImages: inputTypes
1043
1162
  ? inputTypes.some((t) => typeof t === "string" && t.toUpperCase() === "IMAGE")
1044
1163
  : undefined,
1164
+ promptCaching: promptCaching && typeof promptCaching.supportsPromptCaching === "boolean"
1165
+ ? {
1166
+ supportsPromptCaching: promptCaching.supportsPromptCaching,
1167
+ maximumCacheCheckpointsPerRequest: optionalNumber(promptCaching.maximumCacheCheckpointsPerRequest),
1168
+ minimumTokensPerCacheCheckpoint: optionalNumber(promptCaching.minimumTokensPerCacheCheckpoint),
1169
+ }
1170
+ : undefined,
1171
+ additionalModelRequestFieldsSchema: requestFieldsSchema ?? undefined,
1045
1172
  };
1046
1173
  }
1047
1174
  function kiroCatalogHeaders(credential) {
@@ -1062,22 +1189,66 @@ function kiroCatalogHeaders(credential) {
1062
1189
  }
1063
1190
  return headers;
1064
1191
  }
1065
- export async function listKiroModelsDetailed(credential) {
1066
- const region = assertValidAwsRegion(credential.region);
1192
+ export async function discoverKiroModelsDetailed(credential) {
1193
+ const legacyRegion = assertValidAwsRegion(credential.region);
1194
+ const apiRegion = resolveKiroRuntimeRegion(credential);
1067
1195
  const params = new URLSearchParams({ origin: "AI_EDITOR" });
1068
- const response = await fetch(`https://q.${region}.amazonaws.com/ListAvailableModels?${params.toString()}`, {
1069
- method: "GET",
1070
- headers: kiroCatalogHeaders(credential),
1071
- signal: AbortSignal.timeout(15_000),
1072
- });
1073
- if (!response.ok) {
1074
- throw new Error(`ListAvailableModels failed (HTTP ${response.status})`);
1196
+ if (credential.profileArn)
1197
+ params.set("profileArn", credential.profileArn);
1198
+ const baseHeaders = kiroCatalogHeaders(credential);
1199
+ const requests = [
1200
+ {
1201
+ generation: "modern",
1202
+ url: `https://management.${apiRegion}.kiro.dev/`,
1203
+ init: {
1204
+ method: "POST",
1205
+ headers: {
1206
+ ...baseHeaders,
1207
+ "Content-Type": "application/x-amz-json-1.0",
1208
+ "X-Amz-Target": "AmazonCodeWhispererService.ListAvailableModels",
1209
+ },
1210
+ body: JSON.stringify({
1211
+ origin: "KIRO_CLI",
1212
+ ...(credential.profileArn ? { profileArn: credential.profileArn } : {}),
1213
+ }),
1214
+ },
1215
+ },
1216
+ {
1217
+ generation: "legacy",
1218
+ url: `https://q.${legacyRegion}.amazonaws.com/ListAvailableModels?${params.toString()}`,
1219
+ init: { method: "GET", headers: baseHeaders },
1220
+ },
1221
+ ];
1222
+ let lastStatus;
1223
+ let lastError;
1224
+ for (const request of requests) {
1225
+ try {
1226
+ const response = await fetch(request.url, {
1227
+ ...request.init,
1228
+ signal: AbortSignal.timeout(15_000),
1229
+ });
1230
+ if (!response.ok) {
1231
+ lastStatus = response.status;
1232
+ continue;
1233
+ }
1234
+ const json = asRecord(await response.json().catch(() => ({})));
1235
+ const models = Array.isArray(json?.models) ? json.models : [];
1236
+ return {
1237
+ models: models
1238
+ .map(parseModelInfo)
1239
+ .filter((info) => info !== undefined),
1240
+ generation: request.generation,
1241
+ };
1242
+ }
1243
+ catch (error) {
1244
+ lastError = error;
1245
+ }
1075
1246
  }
1076
- const json = asRecord(await response.json().catch(() => ({})));
1077
- const models = Array.isArray(json?.models) ? json.models : [];
1078
- return models
1079
- .map(parseModelInfo)
1080
- .filter((info) => info !== undefined);
1247
+ const detail = lastError instanceof Error ? `: ${lastError.message}` : "";
1248
+ throw new Error(`ListAvailableModels failed${lastStatus ? ` (HTTP ${lastStatus})` : ""}${detail}`);
1249
+ }
1250
+ export async function listKiroModelsDetailed(credential) {
1251
+ return (await discoverKiroModelsDetailed(credential)).models;
1081
1252
  }
1082
1253
  export async function getKiroUsageLimits(credential) {
1083
1254
  const region = assertValidAwsRegion(credential.region);