@pentoshi/clai 4.11.25 → 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 (307) hide show
  1. package/README.md +91 -34
  2. package/dist/agent/compaction/summary-execution.js +11 -1
  3. package/dist/agent/compaction/summary-execution.js.map +1 -1
  4. package/dist/agent/context-manager.d.ts +0 -1
  5. package/dist/agent/context-manager.js +0 -1
  6. package/dist/agent/context-manager.js.map +1 -1
  7. package/dist/agent/events.d.ts +1 -5
  8. package/dist/agent/plan/actions/plan-clear.js +1 -1
  9. package/dist/agent/plan/actions/plan-clear.js.map +1 -1
  10. package/dist/agent/plan/actions/plan-create.js +38 -17
  11. package/dist/agent/plan/actions/plan-create.js.map +1 -1
  12. package/dist/agent/plan/actions/task-add.js +15 -4
  13. package/dist/agent/plan/actions/task-add.js.map +1 -1
  14. package/dist/agent/plan/actions/task-move.js +16 -5
  15. package/dist/agent/plan/actions/task-move.js.map +1 -1
  16. package/dist/agent/plan/handle-plan-tool.js +20 -6
  17. package/dist/agent/plan/handle-plan-tool.js.map +1 -1
  18. package/dist/agent/reliability-policy.d.ts +0 -10
  19. package/dist/agent/reliability-policy.js +0 -23
  20. package/dist/agent/reliability-policy.js.map +1 -1
  21. package/dist/agent/request-accounting.js +10 -10
  22. package/dist/agent/request-accounting.js.map +1 -1
  23. package/dist/agent/request-budget.d.ts +11 -18
  24. package/dist/agent/request-budget.js +14 -53
  25. package/dist/agent/request-budget.js.map +1 -1
  26. package/dist/agent/runner.js +16 -12
  27. package/dist/agent/runner.js.map +1 -1
  28. package/dist/agent/stream-recovery.d.ts +1 -2
  29. package/dist/agent/stream-recovery.js +5 -10
  30. package/dist/agent/stream-recovery.js.map +1 -1
  31. package/dist/agent/subagents/worker.js +3 -2
  32. package/dist/agent/subagents/worker.js.map +1 -1
  33. package/dist/agent/turn/automatic-compaction-execution.js +2 -2
  34. package/dist/agent/turn/automatic-compaction-execution.js.map +1 -1
  35. package/dist/agent/turn/compaction-admission.d.ts +2 -2
  36. package/dist/agent/turn/compaction-admission.js +14 -17
  37. package/dist/agent/turn/compaction-admission.js.map +1 -1
  38. package/dist/agent/turn/compaction-coordinator.d.ts +2 -1
  39. package/dist/agent/turn/compaction-coordinator.js +12 -7
  40. package/dist/agent/turn/compaction-coordinator.js.map +1 -1
  41. package/dist/agent/turn/compaction-durable-envelope.js +1 -1
  42. package/dist/agent/turn/compaction-durable-envelope.js.map +1 -1
  43. package/dist/agent/turn/compaction-replay-selection.js +2 -3
  44. package/dist/agent/turn/compaction-replay-selection.js.map +1 -1
  45. package/dist/agent/turn/compaction-summarizer.js +1 -1
  46. package/dist/agent/turn/compaction-summarizer.js.map +1 -1
  47. package/dist/agent/turn/loop/answer-path.js +2 -2
  48. package/dist/agent/turn/loop/answer-path.js.map +1 -1
  49. package/dist/agent/turn/loop/completion-interpretation.d.ts +0 -1
  50. package/dist/agent/turn/loop/completion-interpretation.js +2 -7
  51. package/dist/agent/turn/loop/completion-interpretation.js.map +1 -1
  52. package/dist/agent/turn/loop/deps.d.ts +1 -0
  53. package/dist/agent/turn/loop/empty-response.d.ts +1 -1
  54. package/dist/agent/turn/loop/empty-response.js +2 -4
  55. package/dist/agent/turn/loop/empty-response.js.map +1 -1
  56. package/dist/agent/turn/loop/output-budget.d.ts +0 -1
  57. package/dist/agent/turn/loop/output-budget.js +4 -9
  58. package/dist/agent/turn/loop/output-budget.js.map +1 -1
  59. package/dist/agent/turn/loop/request-assembly.d.ts +1 -3
  60. package/dist/agent/turn/loop/request-assembly.js +15 -15
  61. package/dist/agent/turn/loop/request-assembly.js.map +1 -1
  62. package/dist/agent/turn/loop/round-request.js +1 -6
  63. package/dist/agent/turn/loop/round-request.js.map +1 -1
  64. package/dist/agent/turn/loop/run-rounds.js +9 -15
  65. package/dist/agent/turn/loop/run-rounds.js.map +1 -1
  66. package/dist/agent/turn/loop/state.d.ts +2 -3
  67. package/dist/agent/turn/loop/state.js +1 -2
  68. package/dist/agent/turn/loop/state.js.map +1 -1
  69. package/dist/agent/turn/loop/stream-failure.d.ts +0 -1
  70. package/dist/agent/turn/loop/stream-failure.js +5 -5
  71. package/dist/agent/turn/loop/stream-failure.js.map +1 -1
  72. package/dist/agent/turn/loop/stream-request.d.ts +0 -1
  73. package/dist/agent/turn/loop/stream-request.js +1 -3
  74. package/dist/agent/turn/loop/stream-request.js.map +1 -1
  75. package/dist/agent/turn/plan-persistence.d.ts +4 -5
  76. package/dist/agent/turn/plan-persistence.js +13 -4
  77. package/dist/agent/turn/plan-persistence.js.map +1 -1
  78. package/dist/agent/turn/provider-measurement.d.ts +6 -0
  79. package/dist/agent/turn/provider-measurement.js +19 -0
  80. package/dist/agent/turn/provider-measurement.js.map +1 -0
  81. package/dist/agent/turn/responder-job-linkage.js +2 -2
  82. package/dist/agent/turn/responder-job-linkage.js.map +1 -1
  83. package/dist/agent/turn/setup/compaction-services.d.ts +2 -1
  84. package/dist/agent/turn/setup/compaction-services.js +2 -1
  85. package/dist/agent/turn/setup/compaction-services.js.map +1 -1
  86. package/dist/agent/turn/setup/task-gate-setup.d.ts +3 -3
  87. package/dist/agent/turn/setup/task-gate-setup.js.map +1 -1
  88. package/dist/agent/turn/task-autostart.d.ts +2 -2
  89. package/dist/agent/turn/task-autostart.js +7 -9
  90. package/dist/agent/turn/task-autostart.js.map +1 -1
  91. package/dist/agent/turn/task-credit.d.ts +2 -2
  92. package/dist/agent/turn/task-credit.js +8 -5
  93. package/dist/agent/turn/task-credit.js.map +1 -1
  94. package/dist/agent/turn/tool-execution/deps.d.ts +3 -3
  95. package/dist/agent/turn/tool-execution/dispatch.js +3 -5
  96. package/dist/agent/turn/tool-execution/dispatch.js.map +1 -1
  97. package/dist/agent/turn/tool-execution/meta-tools.d.ts +1 -1
  98. package/dist/agent/turn/tool-execution/meta-tools.js +13 -11
  99. package/dist/agent/turn/tool-execution/meta-tools.js.map +1 -1
  100. package/dist/agent/turn/tool-execution/plan-tool-ledger.d.ts +2 -2
  101. package/dist/agent/turn/tool-execution/plan-tool-ledger.js +6 -6
  102. package/dist/agent/turn/tool-execution/plan-tool-ledger.js.map +1 -1
  103. package/dist/agent/turn/tool-execution/result-framing.d.ts +1 -1
  104. package/dist/agent/turn/tool-execution/result-framing.js +1 -2
  105. package/dist/agent/turn/tool-execution/result-framing.js.map +1 -1
  106. package/dist/agent/turn/tool-execution/single-tool.js +28 -16
  107. package/dist/agent/turn/tool-execution/single-tool.js.map +1 -1
  108. package/dist/app/adapters/agent-event-adapter.js +8 -7
  109. package/dist/app/adapters/agent-event-adapter.js.map +1 -1
  110. package/dist/app/commands/catalog.js +1 -1
  111. package/dist/app/commands/catalog.js.map +1 -1
  112. package/dist/app/controllers/plan-controller.js +4 -1
  113. package/dist/app/controllers/plan-controller.js.map +1 -1
  114. package/dist/app/controllers/session-compact-helper.js +11 -8
  115. package/dist/app/controllers/session-compact-helper.js.map +1 -1
  116. package/dist/app/controllers/session-context-usage.d.ts +6 -4
  117. package/dist/app/controllers/session-context-usage.js +94 -33
  118. package/dist/app/controllers/session-context-usage.js.map +1 -1
  119. package/dist/app/controllers/session-controller.d.ts +6 -6
  120. package/dist/app/controllers/session-controller.js +51 -40
  121. package/dist/app/controllers/session-controller.js.map +1 -1
  122. package/dist/app/controllers/session-responder.d.ts +2 -0
  123. package/dist/app/controllers/session-responder.js +18 -0
  124. package/dist/app/controllers/session-responder.js.map +1 -1
  125. package/dist/app/controllers/session-usage-ledger.d.ts +7 -1
  126. package/dist/app/controllers/session-usage-ledger.js +76 -2
  127. package/dist/app/controllers/session-usage-ledger.js.map +1 -1
  128. package/dist/app/events/app-event.d.ts +5 -6
  129. package/dist/app/events/app-event.js.map +1 -1
  130. package/dist/classic/panels/pager-panel.js +3 -2
  131. package/dist/classic/panels/pager-panel.js.map +1 -1
  132. package/dist/commands/providers.d.ts +2 -0
  133. package/dist/commands/providers.js +49 -7
  134. package/dist/commands/providers.js.map +1 -1
  135. package/dist/llm/anthropic.d.ts +1 -0
  136. package/dist/llm/anthropic.js +20 -21
  137. package/dist/llm/anthropic.js.map +1 -1
  138. package/dist/llm/capabilities.d.ts +1 -4
  139. package/dist/llm/capabilities.js +24 -19
  140. package/dist/llm/capabilities.js.map +1 -1
  141. package/dist/llm/capability/state.js +0 -17
  142. package/dist/llm/capability/state.js.map +1 -1
  143. package/dist/llm/catalog-facts.js +40 -11
  144. package/dist/llm/catalog-facts.js.map +1 -1
  145. package/dist/llm/catalog-prefetch.js +9 -6
  146. package/dist/llm/catalog-prefetch.js.map +1 -1
  147. package/dist/llm/context-snapshot.d.ts +7 -3
  148. package/dist/llm/context-snapshot.js +35 -14
  149. package/dist/llm/context-snapshot.js.map +1 -1
  150. package/dist/llm/context-windows.d.ts +16 -0
  151. package/dist/llm/context-windows.js +54 -19
  152. package/dist/llm/context-windows.js.map +1 -1
  153. package/dist/llm/custom-providers.js +5 -4
  154. package/dist/llm/custom-providers.js.map +1 -1
  155. package/dist/llm/explabs.js +35 -26
  156. package/dist/llm/explabs.js.map +1 -1
  157. package/dist/llm/free.d.ts +1 -1
  158. package/dist/llm/free.js +26 -18
  159. package/dist/llm/free.js.map +1 -1
  160. package/dist/llm/gemini.js +25 -12
  161. package/dist/llm/gemini.js.map +1 -1
  162. package/dist/llm/kiro-auth.d.ts +65 -2
  163. package/dist/llm/kiro-auth.js +463 -130
  164. package/dist/llm/kiro-auth.js.map +1 -1
  165. package/dist/llm/kiro-import-sources.d.ts +10 -0
  166. package/dist/llm/kiro-import-sources.js +117 -0
  167. package/dist/llm/kiro-import-sources.js.map +1 -0
  168. package/dist/llm/kiro-reasoning.d.ts +28 -0
  169. package/dist/llm/kiro-reasoning.js +150 -0
  170. package/dist/llm/kiro-reasoning.js.map +1 -0
  171. package/dist/llm/kiro-social-device.d.ts +21 -0
  172. package/dist/llm/kiro-social-device.js +101 -0
  173. package/dist/llm/kiro-social-device.js.map +1 -0
  174. package/dist/llm/kiro.d.ts +7 -1
  175. package/dist/llm/kiro.js +757 -228
  176. package/dist/llm/kiro.js.map +1 -1
  177. package/dist/llm/learned-capabilities.js +1 -3
  178. package/dist/llm/learned-capabilities.js.map +1 -1
  179. package/dist/llm/merge-gateway.js +18 -13
  180. package/dist/llm/merge-gateway.js.map +1 -1
  181. package/dist/llm/model-layers/model-rules.js +22 -1
  182. package/dist/llm/model-layers/model-rules.js.map +1 -1
  183. package/dist/llm/model-limit-store.d.ts +15 -0
  184. package/dist/llm/model-limit-store.js +121 -0
  185. package/dist/llm/model-limit-store.js.map +1 -0
  186. package/dist/llm/nvidia.d.ts +1 -0
  187. package/dist/llm/nvidia.js +15 -17
  188. package/dist/llm/nvidia.js.map +1 -1
  189. package/dist/llm/ollama-model-limits.d.ts +4 -0
  190. package/dist/llm/ollama-model-limits.js +91 -0
  191. package/dist/llm/ollama-model-limits.js.map +1 -0
  192. package/dist/llm/ollama.js +2 -1
  193. package/dist/llm/ollama.js.map +1 -1
  194. package/dist/llm/orcarouter.d.ts +1 -0
  195. package/dist/llm/orcarouter.js +8 -1
  196. package/dist/llm/orcarouter.js.map +1 -1
  197. package/dist/llm/provider-balance.d.ts +31 -0
  198. package/dist/llm/provider-balance.js +118 -0
  199. package/dist/llm/provider-balance.js.map +1 -0
  200. package/dist/llm/provider-context-usage.d.ts +9 -0
  201. package/dist/llm/provider-context-usage.js +39 -0
  202. package/dist/llm/provider-context-usage.js.map +1 -0
  203. package/dist/llm/provider-info-text.js +19 -7
  204. package/dist/llm/provider-info-text.js.map +1 -1
  205. package/dist/llm/provider-profile-layers.js +1 -1
  206. package/dist/llm/provider-profile-layers.js.map +1 -1
  207. package/dist/llm/provider.d.ts +1 -0
  208. package/dist/llm/provider.js.map +1 -1
  209. package/dist/llm/qwen-cloud.d.ts +1 -0
  210. package/dist/llm/qwen-cloud.js +44 -9
  211. package/dist/llm/qwen-cloud.js.map +1 -1
  212. package/dist/llm/reasoning-errors.d.ts +2 -0
  213. package/dist/llm/reasoning-errors.js +16 -1
  214. package/dist/llm/reasoning-errors.js.map +1 -1
  215. package/dist/llm/routing/attempt-complete.js +9 -2
  216. package/dist/llm/routing/attempt-complete.js.map +1 -1
  217. package/dist/llm/routing/attempt-request.d.ts +1 -0
  218. package/dist/llm/routing/attempt-request.js +23 -0
  219. package/dist/llm/routing/attempt-request.js.map +1 -1
  220. package/dist/llm/routing/attempt-stream.js +15 -2
  221. package/dist/llm/routing/attempt-stream.js.map +1 -1
  222. package/dist/llm/token-estimate-calibration.js +1 -2
  223. package/dist/llm/token-estimate-calibration.js.map +1 -1
  224. package/dist/llm/token-usage.d.ts +2 -0
  225. package/dist/llm/token-usage.js +1 -1
  226. package/dist/llm/token-usage.js.map +1 -1
  227. package/dist/llm/wire/anthropic-model-catalog.d.ts +1 -0
  228. package/dist/llm/wire/anthropic-model-catalog.js +30 -0
  229. package/dist/llm/wire/anthropic-model-catalog.js.map +1 -0
  230. package/dist/llm/wire/dashscope-model-catalog.d.ts +1 -0
  231. package/dist/llm/wire/dashscope-model-catalog.js +53 -0
  232. package/dist/llm/wire/dashscope-model-catalog.js.map +1 -0
  233. package/dist/llm/wire/orcarouter-model-catalog.d.ts +2 -0
  234. package/dist/llm/wire/orcarouter-model-catalog.js +91 -0
  235. package/dist/llm/wire/orcarouter-model-catalog.js.map +1 -0
  236. package/dist/noninteractive/stream-blocks.d.ts +0 -3
  237. package/dist/noninteractive/stream-blocks.js +0 -9
  238. package/dist/noninteractive/stream-blocks.js.map +1 -1
  239. package/dist/noninteractive/stream-renderer.js +1 -4
  240. package/dist/noninteractive/stream-renderer.js.map +1 -1
  241. package/dist/store/config/endpoints.d.ts +0 -6
  242. package/dist/store/config/endpoints.js +1 -35
  243. package/dist/store/config/endpoints.js.map +1 -1
  244. package/dist/store/context-limits.d.ts +4 -0
  245. package/dist/store/context-limits.js +29 -0
  246. package/dist/store/context-limits.js.map +1 -0
  247. package/dist/store/history/jsonl-backend.js +2 -1
  248. package/dist/store/history/jsonl-backend.js.map +1 -1
  249. package/dist/store/history/jsonl-lock.js +23 -3
  250. package/dist/store/history/jsonl-lock.js.map +1 -1
  251. package/dist/store/history/lifecycle.js +3 -2
  252. package/dist/store/history/lifecycle.js.map +1 -1
  253. package/dist/store/plan/jsonl-backend.js +1 -4
  254. package/dist/store/plan/jsonl-backend.js.map +1 -1
  255. package/dist/store/plan/mutation.js +31 -1
  256. package/dist/store/plan/mutation.js.map +1 -1
  257. package/dist/store/plan/sqlite-backend.js +27 -9
  258. package/dist/store/plan/sqlite-backend.js.map +1 -1
  259. package/dist/tui-v2/components/pager/pager-line.d.ts +2 -0
  260. package/dist/tui-v2/components/pager/pager-line.js +1 -2
  261. package/dist/tui-v2/components/pager/pager-line.js.map +1 -1
  262. package/dist/tui-v2/components/pager/pager.js +5 -2
  263. package/dist/tui-v2/components/pager/pager.js.map +1 -1
  264. package/dist/tui-v2/components/transcript/tool-card.js +1 -1
  265. package/dist/tui-v2/components/transcript/tool-card.js.map +1 -1
  266. package/dist/types.d.ts +10 -1
  267. package/dist/types.js.map +1 -1
  268. package/dist/ui-core/bootstrap/composition-root.js +9 -5
  269. package/dist/ui-core/bootstrap/composition-root.js.map +1 -1
  270. package/dist/ui-core/bootstrap/session-resume.js +1 -2
  271. package/dist/ui-core/bootstrap/session-resume.js.map +1 -1
  272. package/dist/ui-core/commands/key-commands.d.ts +2 -0
  273. package/dist/ui-core/commands/key-commands.js +76 -20
  274. package/dist/ui-core/commands/key-commands.js.map +1 -1
  275. package/dist/ui-core/commands/picker-commands.js +3 -0
  276. package/dist/ui-core/commands/picker-commands.js.map +1 -1
  277. package/dist/ui-core/commands/pickers/search-reasoning.d.ts +2 -0
  278. package/dist/ui-core/commands/pickers/search-reasoning.js +1 -1
  279. package/dist/ui-core/commands/pickers/search-reasoning.js.map +1 -1
  280. package/dist/ui-core/commands/session-commands.d.ts +4 -0
  281. package/dist/ui-core/commands/session-commands.js +127 -11
  282. package/dist/ui-core/commands/session-commands.js.map +1 -1
  283. package/dist/ui-core/rendering/batch-sections.js +10 -1
  284. package/dist/ui-core/rendering/batch-sections.js.map +1 -1
  285. package/dist/ui-core/rendering/context-limit.d.ts +0 -1
  286. package/dist/ui-core/rendering/context-limit.js +2 -2
  287. package/dist/ui-core/rendering/context-limit.js.map +1 -1
  288. package/dist/ui-core/rendering/format-usage.js +29 -6
  289. package/dist/ui-core/rendering/format-usage.js.map +1 -1
  290. package/dist/ui-core/rendering/subagent-presentation.d.ts +2 -0
  291. package/dist/ui-core/rendering/subagent-presentation.js +20 -7
  292. package/dist/ui-core/rendering/subagent-presentation.js.map +1 -1
  293. package/dist/ui-core/rendering/subagent-source.js +15 -8
  294. package/dist/ui-core/rendering/subagent-source.js.map +1 -1
  295. package/dist/ui-core/state/transcript/apply-app-event.js +0 -1
  296. package/dist/ui-core/state/transcript/apply-app-event.js.map +1 -1
  297. package/dist/ui-core/state/transcript-types.js +1 -1
  298. package/dist/ui-core/state/transcript-types.js.map +1 -1
  299. package/dist/version.generated.d.ts +2 -2
  300. package/dist/version.generated.js +2 -2
  301. package/package.json +1 -1
  302. package/dist/app/controllers/session-context-limits.d.ts +0 -8
  303. package/dist/app/controllers/session-context-limits.js +0 -35
  304. package/dist/app/controllers/session-context-limits.js.map +0 -1
  305. package/dist/store/config/compaction.d.ts +0 -1
  306. package/dist/store/config/compaction.js +0 -2
  307. package/dist/store/config/compaction.js.map +0 -1
package/dist/llm/kiro.js CHANGED
@@ -1,23 +1,43 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { defaultModels, } from "./provider.js";
3
3
  import { ProviderError } from "./http.js";
4
- import { assertValidAwsRegion, decodeKiroKey, isKiroOAuthToken, maybeRefreshKiroCredential, } from "./kiro-auth.js";
4
+ import { decodeKiroKey, discoverKiroModelsDetailed, getKiroUsageLimits, isKiroOAuthToken, KiroRefreshError, kiroCredentialNeedsRefresh, maybeRefreshKiroCredential, resolveKiroRuntimeRegion, } from "./kiro-auth.js";
5
5
  import { currentSessionAffinity } from "./session-affinity.js";
6
+ import { nearestAcceptedEffort } from "./reasoning-controls.js";
7
+ import { completeGenerationAttempt, generationFetch, } from "./operation-usage.js";
8
+ import { learnModelVisionCapability } from "./capability/vision-registry.js";
9
+ import { emitStreamReasoningDelta } from "./stream-events.js";
10
+ import { createReasoningArtifactProvenance, createReasoningArtifactReplayTarget, reasoningArtifactSignature, reasoningArtifactText, reasoningArtifactsForMessage, selectReasoningArtifactsForReplay, } from "./reasoning-artifacts.js";
11
+ import { createKiroReasoningAccumulator, kiroPrivateReasoningNote, kiroCatalogEfforts, kiroReasoningResult, sentKiroReasoningEffort, } from "./kiro-reasoning.js";
12
+ import { registerModelCatalogLimits, registerModelReasoningEfforts, } from "./capabilities.js";
13
+ import { modelContextWindow } from "./context-windows.js";
14
+ import { providerRatioPromptTokens, providerRatioUsage, } from "./provider-context-usage.js";
15
+ import { inBandBadRequestStatus } from "./reasoning-errors.js";
6
16
  import { replaceProviderKey } from "../store/keys.js";
7
- export const kiroFallbackModels = [
17
+ const KIRO_FALLBACK_BASE_MODELS = [
18
+ "auto",
8
19
  "claude-sonnet-4.5",
9
- "claude-sonnet-4.5-thinking",
10
- "claude-sonnet-4.5-agentic",
11
- "claude-sonnet-4.5-thinking-agentic",
12
- "claude-opus-5",
13
- "claude-opus-5-thinking",
14
- "claude-opus-5-agentic",
15
- "claude-opus-5-thinking-agentic",
20
+ "claude-sonnet-4",
16
21
  "claude-haiku-4.5",
17
- "claude-haiku-4.5-thinking",
18
- "claude-haiku-4.5-agentic",
19
- "claude-haiku-4.5-thinking-agentic",
22
+ "deepseek-3.2",
23
+ "minimax-m2.5",
24
+ "minimax-m2.1",
25
+ "glm-5",
26
+ "qwen3-coder-next",
20
27
  ];
28
+ function withKiroVariants(baseIds) {
29
+ const variants = [];
30
+ for (const id of baseIds) {
31
+ variants.push(id);
32
+ if (id === "auto")
33
+ continue;
34
+ variants.push(`${id}-thinking`);
35
+ variants.push(`${id}-agentic`);
36
+ variants.push(`${id}-thinking-agentic`);
37
+ }
38
+ return variants;
39
+ }
40
+ export const kiroFallbackModels = withKiroVariants(KIRO_FALLBACK_BASE_MODELS);
21
41
  const CRC32_TABLE = new Uint32Array(256);
22
42
  for (let i = 0; i < 256; i++) {
23
43
  let c = i;
@@ -26,6 +46,8 @@ for (let i = 0; i < 256; i++) {
26
46
  }
27
47
  CRC32_TABLE[i] = c >>> 0;
28
48
  }
49
+ const KIRO_EVENTSTREAM_MAX_MESSAGE_BYTES = 24 * 1024 * 1024;
50
+ const KIRO_EVENTSTREAM_MAX_HEADERS_BYTES = 128 * 1024;
29
51
  function crc32(buf, start = 0, end = buf.length) {
30
52
  let crc = 0xffffffff;
31
53
  for (let i = start; i < end; i++) {
@@ -35,59 +57,73 @@ function crc32(buf, start = 0, end = buf.length) {
35
57
  }
36
58
  function parseHeaders(buf) {
37
59
  const headers = {};
60
+ const names = new Set();
38
61
  let offset = 0;
62
+ const requireBytes = (count) => {
63
+ if (offset + count > buf.length) {
64
+ throw new ProviderError("Kiro event-stream header exceeds its declared bounds", 502);
65
+ }
66
+ };
39
67
  while (offset < buf.length) {
68
+ requireBytes(1);
40
69
  const nameLen = buf.readUInt8(offset);
41
70
  offset += 1;
71
+ requireBytes(nameLen + 1);
42
72
  const name = buf.toString("utf8", offset, offset + nameLen);
43
73
  offset += nameLen;
74
+ if (names.has(name)) {
75
+ throw new ProviderError(`Duplicate Kiro event-stream header: ${name}`, 502);
76
+ }
77
+ names.add(name);
44
78
  const type = buf.readUInt8(offset);
45
79
  offset += 1;
46
- if (type === 0) {
47
- headers[name] = true;
80
+ if (type === 0 || type === 1) {
81
+ headers[name] = type === 0;
82
+ continue;
48
83
  }
49
- else if (type === 1) {
50
- headers[name] = false;
51
- }
52
- else if (type === 2) {
84
+ if (type === 2) {
85
+ requireBytes(1);
53
86
  headers[name] = buf.readInt8(offset);
54
87
  offset += 1;
88
+ continue;
55
89
  }
56
- else if (type === 3) {
90
+ if (type === 3) {
91
+ requireBytes(2);
57
92
  headers[name] = buf.readInt16BE(offset);
58
93
  offset += 2;
94
+ continue;
59
95
  }
60
- else if (type === 4) {
96
+ if (type === 4) {
97
+ requireBytes(4);
61
98
  headers[name] = buf.readInt32BE(offset);
62
99
  offset += 4;
100
+ continue;
63
101
  }
64
- else if (type === 5) {
65
- headers[name] = buf.readBigInt64BE(offset);
102
+ if (type === 5 || type === 8) {
103
+ requireBytes(8);
104
+ const value = buf.readBigInt64BE(offset);
105
+ headers[name] = type === 5 ? value : new Date(Number(value));
66
106
  offset += 8;
107
+ continue;
67
108
  }
68
- else if (type === 6) {
109
+ if (type === 6 || type === 7) {
110
+ requireBytes(2);
69
111
  const len = buf.readUInt16BE(offset);
70
112
  offset += 2;
71
- headers[name] = buf.subarray(offset, offset + len);
113
+ requireBytes(len);
114
+ headers[name] = type === 7
115
+ ? buf.toString("utf8", offset, offset + len)
116
+ : buf.subarray(offset, offset + len);
72
117
  offset += len;
118
+ continue;
73
119
  }
74
- else if (type === 7) {
75
- const len = buf.readUInt16BE(offset);
76
- offset += 2;
77
- headers[name] = buf.toString("utf8", offset, offset + len);
78
- offset += len;
79
- }
80
- else if (type === 8) {
81
- headers[name] = new Date(Number(buf.readBigInt64BE(offset)));
82
- offset += 8;
83
- }
84
- else if (type === 9) {
120
+ if (type === 9) {
121
+ requireBytes(16);
85
122
  headers[name] = buf.subarray(offset, offset + 16).toString("hex");
86
123
  offset += 16;
124
+ continue;
87
125
  }
88
- else {
89
- break;
90
- }
126
+ throw new ProviderError(`Unknown Kiro event-stream header type ${type}`, 502);
91
127
  }
92
128
  return headers;
93
129
  }
@@ -105,15 +141,20 @@ async function* parseEventStream(stream) {
105
141
  while (buffer.length >= 12) {
106
142
  const totalLength = buffer.readUInt32BE(0);
107
143
  if (totalLength < 16) {
108
- buffer = buffer.subarray(1);
109
- continue;
144
+ throw new ProviderError("Invalid Kiro event-stream frame length", 502);
145
+ }
146
+ if (totalLength > KIRO_EVENTSTREAM_MAX_MESSAGE_BYTES) {
147
+ throw new ProviderError(`Kiro event-stream frame length exceeds the ${KIRO_EVENTSTREAM_MAX_MESSAGE_BYTES}-byte protocol bound`, 502);
110
148
  }
111
149
  const headersLength = buffer.readUInt32BE(4);
150
+ if (headersLength > KIRO_EVENTSTREAM_MAX_HEADERS_BYTES ||
151
+ headersLength > totalLength - 16) {
152
+ throw new ProviderError("Invalid Kiro event-stream header length", 502);
153
+ }
112
154
  const preludeCrc = buffer.readUInt32BE(8);
113
155
  const expectedPreludeCrc = crc32(buffer, 0, 8);
114
156
  if (preludeCrc !== expectedPreludeCrc) {
115
- buffer = buffer.subarray(1);
116
- continue;
157
+ throw new ProviderError("Kiro event-stream prelude checksum failed", 502);
117
158
  }
118
159
  if (buffer.length < totalLength) {
119
160
  break;
@@ -121,8 +162,7 @@ async function* parseEventStream(stream) {
121
162
  const messageCrc = buffer.readUInt32BE(totalLength - 4);
122
163
  const expectedMessageCrc = crc32(buffer, 0, totalLength - 4);
123
164
  if (messageCrc !== expectedMessageCrc) {
124
- buffer = buffer.subarray(totalLength);
125
- continue;
165
+ throw new ProviderError("Kiro event-stream message checksum failed", 502);
126
166
  }
127
167
  const headersBuffer = buffer.subarray(12, 12 + headersLength);
128
168
  const payloadBuffer = buffer.subarray(12 + headersLength, totalLength - 4);
@@ -131,16 +171,27 @@ async function* parseEventStream(stream) {
131
171
  let payload = {};
132
172
  if (payloadBuffer.length > 0) {
133
173
  try {
134
- const raw = payloadBuffer.toString("utf8");
135
- payload = JSON.parse(raw);
174
+ const decoded = JSON.parse(payloadBuffer.toString("utf8"));
175
+ if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) {
176
+ throw new Error("payload is not an object");
177
+ }
178
+ payload = decoded;
136
179
  }
137
- catch {
138
- payload = { raw: payloadBuffer.toString("utf8") };
180
+ catch (error) {
181
+ const detail = error instanceof Error ? `: ${error.message}` : "";
182
+ throw new ProviderError(`Invalid Kiro event-stream JSON payload${detail}`, 502);
139
183
  }
140
184
  }
141
185
  yield { headers, payload };
142
186
  }
143
187
  }
188
+ if (buffer.length > 0) {
189
+ throw new ProviderError("Kiro event stream ended with a partial frame", 502);
190
+ }
191
+ }
192
+ catch (error) {
193
+ await reader.cancel(error).catch(() => { });
194
+ throw error;
144
195
  }
145
196
  finally {
146
197
  reader.releaseLock();
@@ -170,25 +221,169 @@ export function resolveKiroModel(model) {
170
221
  }
171
222
  return { upstream, agentic, thinking };
172
223
  }
173
- function isNativeReasoningModel(model) {
174
- return /^(gpt|o\d)/i.test(model);
175
- }
176
- function isAdaptiveThinkingModel(model) {
177
- return /^claude/i.test(model);
224
+ function isLegacyThinkingDirectiveModel(model) {
225
+ return /^claude-(?:opus-4\.5|sonnet-4(?:\.5)?|haiku-4\.5)$/i.test(model);
178
226
  }
179
227
  function kiroEffortString(effort) {
180
228
  if (!effort || effort === "none")
181
229
  return "";
182
- if (effort === "minimal" || effort === "low")
230
+ if (effort === "minimal")
231
+ return "low";
232
+ if (effort === "low")
183
233
  return "low";
184
234
  if (effort === "medium")
185
235
  return "medium";
186
- return "high";
236
+ if (effort === "high")
237
+ return "high";
238
+ if (effort === "xhigh")
239
+ return "xhigh";
240
+ return "max";
241
+ }
242
+ function asKiroRecord(value) {
243
+ return value && typeof value === "object" && !Array.isArray(value)
244
+ ? value
245
+ : undefined;
246
+ }
247
+ function normalizeKiroStopReason(value) {
248
+ const reason = typeof value === "string" ? value.trim() : "";
249
+ const normalized = reason
250
+ .replace(/([a-z])([A-Z])/g, "$1_$2")
251
+ .toLowerCase()
252
+ .replace(/[\s-]+/g, "_");
253
+ if (["endturn", "end_turn", "stop", "stop_sequence", "completed"].includes(normalized)) {
254
+ return "end_turn";
255
+ }
256
+ if (["tooluse", "tool_use", "tool_calls"].includes(normalized))
257
+ return "tool_use";
258
+ if (["maxtokens", "max_tokens", "max_output_tokens", "length"].includes(normalized)) {
259
+ return "max_tokens";
260
+ }
261
+ return normalized;
262
+ }
263
+ function kiroStopDisposition(reason, hasToolCalls) {
264
+ if (["malformed_model_output", "invalid_model_output"].includes(reason))
265
+ return "retryable";
266
+ if (reason === "refusal" || /(?:content.*filter|guardrail|safety|policy|blocked)/u.test(reason)) {
267
+ return "blocked";
268
+ }
269
+ if (["cancelled", "pause_turn"].includes(reason))
270
+ return "incomplete";
271
+ if (["max_tokens", "model_context_window_exceeded"].includes(reason)) {
272
+ return hasToolCalls ? "incomplete" : "length";
273
+ }
274
+ if (hasToolCalls || reason === "tool_use")
275
+ return "tool_calls";
276
+ if (!reason || reason === "end_turn")
277
+ return "complete";
278
+ return "unknown";
279
+ }
280
+ function mergeKiroStopReason(current, incoming) {
281
+ if (!incoming)
282
+ return current;
283
+ if (!current)
284
+ return incoming;
285
+ const severity = (reason) => {
286
+ const disposition = kiroStopDisposition(reason, false);
287
+ if (disposition === "blocked")
288
+ return 6;
289
+ if (disposition === "incomplete" || disposition === "unknown")
290
+ return 5;
291
+ if (disposition === "retryable")
292
+ return 4;
293
+ if (disposition === "length")
294
+ return 3;
295
+ if (disposition === "tool_calls")
296
+ return 2;
297
+ return 1;
298
+ };
299
+ return severity(incoming) > severity(current) ? incoming : current;
300
+ }
301
+ function firstKiroMetric(sources, names) {
302
+ for (const source of sources) {
303
+ for (const name of names) {
304
+ const value = source[name];
305
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
306
+ return value;
307
+ }
308
+ }
309
+ }
310
+ return undefined;
311
+ }
312
+ function schemaEnum(schema) {
313
+ return Array.isArray(schema?.enum)
314
+ ? schema.enum.filter((value) => typeof value === "string")
315
+ : [];
316
+ }
317
+ function schemaProperty(schema, name) {
318
+ return asKiroRecord(asKiroRecord(schema?.properties)?.[name]);
319
+ }
320
+ function supportedEffort(requested, schema) {
321
+ const efforts = schemaEnum(schema);
322
+ if (efforts.length === 0)
323
+ return undefined;
324
+ const normalized = requested === "minimal" ? "low" : requested;
325
+ return nearestAcceptedEffort(normalized, efforts);
326
+ }
327
+ function schemaEffortFields(schema) {
328
+ const output = schemaProperty(schema, "output_config");
329
+ const outputEffort = schemaProperty(output, "effort");
330
+ if (outputEffort)
331
+ return { key: "output_config", schema: outputEffort };
332
+ const reasoning = schemaProperty(schema, "reasoning");
333
+ const reasoningEffort = schemaProperty(reasoning, "effort");
334
+ return reasoningEffort
335
+ ? { key: "reasoning", schema: reasoningEffort }
336
+ : undefined;
337
+ }
338
+ function kiroAdditionalModelFields(request, info, wantsThinking) {
339
+ const schema = info?.additionalModelRequestFieldsSchema;
340
+ const thinkingSchema = schemaProperty(schema, "thinking");
341
+ const effortFields = schemaEffortFields(schema);
342
+ if (!wantsThinking && !request.thinking)
343
+ return undefined;
344
+ const requestedEffort = wantsThinking
345
+ ? request.thinking?.effort ?? "high"
346
+ : "none";
347
+ if (!thinkingSchema && !effortFields)
348
+ return undefined;
349
+ const fields = {};
350
+ if (thinkingSchema) {
351
+ const types = schemaEnum(schemaProperty(thinkingSchema, "type"));
352
+ if (!wantsThinking && types.includes("disabled")) {
353
+ return { thinking: { type: "disabled" } };
354
+ }
355
+ if (types.includes("adaptive")) {
356
+ fields.thinking = { type: "adaptive", display: "summarized" };
357
+ }
358
+ else if (wantsThinking) {
359
+ return undefined;
360
+ }
361
+ }
362
+ if (effortFields) {
363
+ const effort = supportedEffort(requestedEffort, effortFields.schema);
364
+ if (effort)
365
+ fields[effortFields.key] = { effort };
366
+ }
367
+ const maxTokensSchema = schemaProperty(schema, "max_tokens");
368
+ if (wantsThinking && request.maxTokens && maxTokensSchema) {
369
+ const minimum = typeof maxTokensSchema.minimum === "number" ? maxTokensSchema.minimum : 1;
370
+ const maximum = typeof maxTokensSchema.maximum === "number"
371
+ ? maxTokensSchema.maximum
372
+ : info?.maxOutputTokens ?? request.maxTokens;
373
+ fields.max_tokens = Math.min(maximum, Math.max(minimum, request.maxTokens));
374
+ }
375
+ return Object.keys(fields).length > 0 ? fields : undefined;
376
+ }
377
+ function legacyThinkingDirective(effort) {
378
+ const budget = KIRO_THINKING_BUDGET[kiroEffortString(effort) || "high"] ?? 16000;
379
+ return `<thinking_mode>enabled</thinking_mode><max_thinking_length>${budget}</max_thinking_length>`;
187
380
  }
188
381
  const KIRO_THINKING_BUDGET = {
189
382
  low: 4000,
190
383
  medium: 8000,
191
384
  high: 16000,
385
+ xhigh: 32000,
386
+ max: 64000,
192
387
  };
193
388
  function resolveKiroCredential(auth) {
194
389
  const raw = auth.apiKey?.trim();
@@ -221,7 +416,12 @@ function resolveKiroCredential(auth) {
221
416
  };
222
417
  }
223
418
  async function withKiroCredential(auth, run, onStatus) {
224
- const credential = resolveKiroCredential(auth);
419
+ let credential = resolveKiroCredential(auth);
420
+ if (credential.refreshToken && kiroCredentialNeedsRefresh(credential)) {
421
+ const refreshed = await refreshStoredKiroKey(auth).catch(() => undefined);
422
+ if (refreshed)
423
+ credential = refreshed;
424
+ }
225
425
  try {
226
426
  return await run(credential);
227
427
  }
@@ -230,27 +430,51 @@ async function withKiroCredential(auth, run, onStatus) {
230
430
  if (status !== 401 && status !== 403)
231
431
  throw error;
232
432
  onStatus?.("ℹ Kiro authentication expired — attempting token refresh");
233
- const oldKey = auth.apiKey ?? "";
234
- const refreshedKey = await maybeRefreshKiroCredential(oldKey, auth.refreshToken);
235
- if (!refreshedKey || refreshedKey === oldKey) {
433
+ let newCredential;
434
+ try {
435
+ newCredential = await refreshStoredKiroKey(auth);
436
+ }
437
+ catch (refreshError) {
438
+ if (refreshError instanceof KiroRefreshError) {
439
+ throw new ProviderError(refreshError.message, status);
440
+ }
236
441
  throw error;
237
442
  }
238
- await replaceProviderKey("kiro", oldKey, refreshedKey).catch(() => { });
239
- auth.apiKey = refreshedKey;
240
- const newCredential = resolveKiroCredential({ ...auth, apiKey: refreshedKey });
443
+ if (!newCredential)
444
+ throw error;
241
445
  onStatus?.("ℹ Kiro token refreshed — retrying request");
242
446
  return run(newCredential);
243
447
  }
244
448
  }
245
- function getCandidateEndpoints(credential) {
246
- const region = assertValidAwsRegion(credential.region);
247
- const qUrl = `https://q.${region}.amazonaws.com/generateAssistantResponse`;
248
- const cwUrl = `https://codewhisperer.${region}.amazonaws.com/generateAssistantResponse`;
249
- const rtUrl = `https://runtime.${region}.kiro.dev/generateAssistantResponse`;
250
- if (credential.authMethod === "api_key") {
251
- return [qUrl, cwUrl, rtUrl];
449
+ async function refreshStoredKiroKey(auth) {
450
+ const oldKey = auth.apiKey ?? "";
451
+ const refreshedKey = await maybeRefreshKiroCredential(oldKey, auth.refreshToken);
452
+ if (!refreshedKey || refreshedKey === oldKey)
453
+ return undefined;
454
+ await replaceProviderKey("kiro", oldKey, refreshedKey).catch(() => { });
455
+ auth.apiKey = refreshedKey;
456
+ return resolveKiroCredential({ ...auth, apiKey: refreshedKey });
457
+ }
458
+ function getCandidateEndpoints(credential, generation) {
459
+ const region = resolveKiroRuntimeRegion(credential);
460
+ const apiRegion = region;
461
+ const modern = { url: `https://runtime.${apiRegion}.kiro.dev/`, awsTarget: true };
462
+ const q = {
463
+ url: `https://q.${region}.amazonaws.com/generateAssistantResponse`,
464
+ awsTarget: false,
465
+ };
466
+ const codewhisperer = {
467
+ url: `https://codewhisperer.${region}.amazonaws.com/generateAssistantResponse`,
468
+ awsTarget: true,
469
+ };
470
+ if (generation === "modern") {
471
+ return credential.authMethod === "api_key"
472
+ ? [modern, q, codewhisperer]
473
+ : [modern, codewhisperer, q];
252
474
  }
253
- return [cwUrl, rtUrl, qUrl];
475
+ return credential.authMethod === "api_key"
476
+ ? [q, modern, codewhisperer]
477
+ : [codewhisperer, modern, q];
254
478
  }
255
479
  function uuidFromHex(hex) {
256
480
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
@@ -272,12 +496,12 @@ function sanitizeToolName(name) {
272
496
  function formatKiroTools(tools) {
273
497
  if (!tools || tools.length === 0)
274
498
  return [];
275
- return tools.map((t) => ({
499
+ return tools.map((tool) => ({
276
500
  toolSpecification: {
277
- name: sanitizeToolName(t.wireName || t.name),
278
- description: (t.description || "").slice(0, 10237),
501
+ name: sanitizeToolName(tool.wireName || tool.name),
502
+ description: (tool.description || `Tool: ${tool.name}`).slice(0, 10237),
279
503
  inputSchema: {
280
- json: t.parameters,
504
+ json: tool.parameters,
281
505
  },
282
506
  },
283
507
  }));
@@ -294,21 +518,34 @@ function imageFormatFromMediaType(mediaType) {
294
518
  return "webp";
295
519
  return "png";
296
520
  }
297
- function buildKiroRequestBody(request, credential, upstreamModel, agentic, thinking) {
521
+ export function buildKiroRequestBody(request, credential, upstreamModel, agentic, thinking) {
298
522
  const agenticInstruction = agentic
299
523
  ? "When modifying or creating files, keep writes concise:\n- Maximum 350 lines per file write/tool call (recommended 300 lines or fewer).\n- Prefer targeted, incremental edits instead of rewriting entire files.\n- For large files, write or append in chunks across multiple tool calls."
300
524
  : "";
301
525
  const systemParts = [];
302
526
  if (agenticInstruction)
303
527
  systemParts.push(agenticInstruction);
528
+ let sawLeadingSystem = false;
304
529
  const nonSystemMessages = [];
305
530
  for (const msg of request.messages) {
306
- if (msg.role === "system") {
531
+ if (msg.role !== "system") {
532
+ nonSystemMessages.push(msg);
533
+ continue;
534
+ }
535
+ if (!sawLeadingSystem) {
536
+ sawLeadingSystem = true;
307
537
  if (msg.content.trim())
308
538
  systemParts.push(msg.content.trim());
539
+ continue;
309
540
  }
310
- else {
311
- nonSystemMessages.push(msg);
541
+ if (msg.content.trim()) {
542
+ nonSystemMessages.push({
543
+ ...msg,
544
+ role: "user",
545
+ content: msg.content.startsWith("[SYSTEM]")
546
+ ? msg.content
547
+ : `[SYSTEM]\n${msg.content}`,
548
+ });
312
549
  }
313
550
  }
314
551
  const systemPrompt = systemParts.join("\n\n").trim();
@@ -339,6 +576,40 @@ function buildKiroRequestBody(request, credential, upstreamModel, agentic, think
339
576
  }
340
577
  }
341
578
  else if (msg.role === "assistant") {
579
+ const replayTarget = createReasoningArtifactReplayTarget({
580
+ provider: "kiro",
581
+ model: upstreamModel,
582
+ dialect: "kiro-eventstream",
583
+ });
584
+ const replayedReasoning = request.forceReasoningReplay === false
585
+ ? []
586
+ : selectReasoningArtifactsForReplay({
587
+ artifacts: reasoningArtifactsForMessage(msg),
588
+ target: replayTarget,
589
+ context: {
590
+ hasToolCalls: Boolean(msg.toolCalls?.length),
591
+ forceScope: request.forceReasoningReplay === true,
592
+ },
593
+ observe: request.onReasoningArtifactReplayDecision,
594
+ });
595
+ const signedReasoning = replayedReasoning.find((artifact) => reasoningArtifactText(artifact) && reasoningArtifactSignature(artifact));
596
+ const redactedReasoning = replayedReasoning.find((artifact) => {
597
+ const raw = asKiroRecord(artifact.raw);
598
+ return artifact.kind === "encrypted" && typeof raw?.redactedContent === "string";
599
+ });
600
+ const redactedRaw = redactedReasoning
601
+ ? asKiroRecord(redactedReasoning.raw)
602
+ : undefined;
603
+ const reasoningContent = signedReasoning
604
+ ? {
605
+ reasoningText: {
606
+ text: reasoningArtifactText(signedReasoning),
607
+ signature: reasoningArtifactSignature(signedReasoning),
608
+ },
609
+ }
610
+ : typeof redactedRaw?.redactedContent === "string"
611
+ ? { redactedContent: redactedRaw.redactedContent }
612
+ : undefined;
342
613
  const toolUses = (msg.toolCalls ?? [])
343
614
  .filter((tc) => tc.id)
344
615
  .map((tc) => ({
@@ -353,6 +624,9 @@ function buildKiroRequestBody(request, credential, upstreamModel, agentic, think
353
624
  ? `${last.content}\n\n${msg.content}`
354
625
  : msg.content;
355
626
  }
627
+ if (reasoningContent && !last.reasoningContent) {
628
+ last.reasoningContent = reasoningContent;
629
+ }
356
630
  if (toolUses.length > 0) {
357
631
  last.toolUses = [...(last.toolUses ?? []), ...toolUses];
358
632
  }
@@ -361,6 +635,7 @@ function buildKiroRequestBody(request, credential, upstreamModel, agentic, think
361
635
  turns.push({
362
636
  role: "assistant",
363
637
  content: msg.content || "",
638
+ reasoningContent,
364
639
  toolUses: toolUses.length > 0 ? toolUses : undefined,
365
640
  });
366
641
  }
@@ -403,7 +678,24 @@ function buildKiroRequestBody(request, credential, upstreamModel, agentic, think
403
678
  : "continue";
404
679
  }
405
680
  }
681
+ const modelInfo = kiroModelCatalog().find((info) => info.modelId === upstreamModel);
406
682
  const kiroTools = formatKiroTools(request.tools);
683
+ const wantsThinking = (request.thinking?.enabled ?? thinking) &&
684
+ request.thinking?.effort !== "none";
685
+ const additionalFields = kiroAdditionalModelFields(request, modelInfo, wantsThinking);
686
+ const thinkingDirective = !additionalFields &&
687
+ wantsThinking &&
688
+ isLegacyThinkingDirectiveModel(upstreamModel) &&
689
+ !modelInfo?.additionalModelRequestFieldsSchema
690
+ ? legacyThinkingDirective(request.thinking?.effort)
691
+ : "";
692
+ if (thinkingDirective) {
693
+ for (const turn of turns) {
694
+ if (turn.role === "user") {
695
+ turn.content = `${thinkingDirective}\n\n${turn.content}`;
696
+ }
697
+ }
698
+ }
407
699
  const history = [];
408
700
  for (let i = 0; i < turns.length - 1; i++) {
409
701
  const turn = turns[i];
@@ -428,6 +720,9 @@ function buildKiroRequestBody(request, credential, upstreamModel, agentic, think
428
720
  history.push({
429
721
  assistantResponseMessage: {
430
722
  content: turn.content,
723
+ ...(turn.reasoningContent
724
+ ? { reasoningContent: turn.reasoningContent }
725
+ : {}),
431
726
  ...(turn.toolUses && turn.toolUses.length > 0
432
727
  ? { toolUses: turn.toolUses }
433
728
  : {}),
@@ -467,33 +762,89 @@ function buildKiroRequestBody(request, credential, upstreamModel, agentic, think
467
762
  if (credential.profileArn) {
468
763
  payload.profileArn = credential.profileArn;
469
764
  }
470
- const wantsThinking = thinking || request.thinking?.enabled === true;
471
- const effort = kiroEffortString(request.thinking?.effort) || "high";
472
- if (wantsThinking && isNativeReasoningModel(upstreamModel)) {
473
- payload.additionalModelRequestFields = { reasoning: { effort } };
474
- }
475
- else if (wantsThinking && isAdaptiveThinkingModel(upstreamModel)) {
476
- const budget = KIRO_THINKING_BUDGET[effort] ?? 16000;
477
- const directive = `<thinking_mode>enabled</thinking_mode><max_thinking_length>${budget}</max_thinking_length>`;
478
- const uim = payload.conversationState
479
- .currentMessage;
480
- const msg = uim.userInputMessage;
481
- msg.content = `${directive}\n\n${String(msg.content ?? "")}`;
482
- payload.additionalModelRequestFields = {
483
- output_config: { effort },
484
- thinking: { type: "adaptive", display: "summarized" },
485
- };
765
+ if (additionalFields) {
766
+ payload.additionalModelRequestFields = additionalFields;
486
767
  }
487
768
  return payload;
488
769
  }
489
770
  let cachedKiroModels = null;
771
+ let cachedKiroModelInfo = [];
772
+ let cachedKiroApiGeneration;
773
+ let cachedKiroCredentialId = "";
490
774
  let lastKiroModelFetch = 0;
491
775
  const MODEL_CACHE_TTL_MS = 30 * 60 * 1000;
776
+ function kiroCredentialId(credential) {
777
+ return createHash("sha256")
778
+ .update(credential.apiKey || credential.accessToken || credential.refreshToken || "")
779
+ .digest("hex");
780
+ }
781
+ function hasFreshKiroCatalog(credentialId) {
782
+ return (cachedKiroCredentialId === credentialId &&
783
+ cachedKiroModels !== null &&
784
+ Date.now() - lastKiroModelFetch < MODEL_CACHE_TTL_MS);
785
+ }
786
+ function kiroCatalogLimits(modelId, info) {
787
+ if (info.maxInputTokens === undefined && info.maxOutputTokens === undefined)
788
+ return [];
789
+ return [{
790
+ id: modelId,
791
+ ...(info.maxInputTokens !== undefined ? { contextTokens: info.maxInputTokens } : {}),
792
+ ...(info.maxOutputTokens !== undefined ? { maxOutputTokens: info.maxOutputTokens } : {}),
793
+ ...(info.supportsImages !== undefined ? { vision: info.supportsImages } : {}),
794
+ }];
795
+ }
796
+ async function ensureKiroModelCatalog(credential) {
797
+ const credentialId = kiroCredentialId(credential);
798
+ if (hasFreshKiroCatalog(credentialId))
799
+ return;
800
+ const catalog = await discoverKiroModelsDetailed(credential);
801
+ cachedKiroCredentialId = credentialId;
802
+ cachedKiroModelInfo = catalog.models;
803
+ cachedKiroApiGeneration = catalog.generation;
804
+ cachedKiroModels = withKiroVariants(catalog.models.map((info) => info.modelId));
805
+ lastKiroModelFetch = Date.now();
806
+ const limits = [];
807
+ for (const info of catalog.models) {
808
+ if (info.supportsImages !== undefined) {
809
+ learnModelVisionCapability("kiro", info.modelId, info.supportsImages);
810
+ }
811
+ const efforts = kiroCatalogEfforts(info.additionalModelRequestFieldsSchema);
812
+ for (const variant of withKiroVariants([info.modelId])) {
813
+ registerModelReasoningEfforts("kiro", variant, efforts);
814
+ limits.push(...kiroCatalogLimits(variant, info));
815
+ }
816
+ }
817
+ registerModelCatalogLimits("kiro", limits);
818
+ }
819
+ export function kiroModelCatalog() {
820
+ return cachedKiroModelInfo;
821
+ }
822
+ export function resetKiroModelCacheForTesting() {
823
+ cachedKiroModels = null;
824
+ cachedKiroModelInfo = [];
825
+ cachedKiroApiGeneration = undefined;
826
+ cachedKiroCredentialId = "";
827
+ lastKiroModelFetch = 0;
828
+ }
829
+ export async function fetchKiroUsageLimits(auth, onStatus) {
830
+ return withKiroCredential(auth, getKiroUsageLimits, onStatus);
831
+ }
832
+ const KIRO_MODEL_RANK = (id) => {
833
+ const base = resolveKiroModel(id).upstream;
834
+ if (base === "auto")
835
+ return 0;
836
+ if (/^claude-/.test(base))
837
+ return 1;
838
+ return 2;
839
+ };
492
840
  export const kiroProvider = {
493
841
  id: "kiro",
494
842
  displayName: "Kiro AI",
495
843
  defaultModel: defaultModels.kiro,
496
844
  envVar: "KIRO_API_KEY",
845
+ sortModels(models) {
846
+ return [...models].sort((a, b) => KIRO_MODEL_RANK(a) - KIRO_MODEL_RANK(b) || a.localeCompare(b));
847
+ },
497
848
  validateKey(key) {
498
849
  const trimmed = key.trim();
499
850
  if (!trimmed)
@@ -507,85 +858,35 @@ export const kiroProvider = {
507
858
  return trimmed.length >= 8;
508
859
  },
509
860
  async listModels(auth) {
510
- const now = Date.now();
511
- if (cachedKiroModels && now - lastKiroModelFetch < MODEL_CACHE_TTL_MS) {
861
+ const credential = resolveKiroCredential(auth);
862
+ const credentialId = kiroCredentialId(credential);
863
+ const cacheIsFresh = cachedKiroCredentialId === credentialId &&
864
+ cachedKiroModels !== null &&
865
+ Date.now() - lastKiroModelFetch < MODEL_CACHE_TTL_MS;
866
+ if (cacheIsFresh)
512
867
  return cachedKiroModels;
513
- }
514
868
  try {
515
- const credential = resolveKiroCredential(auth);
516
- const region = assertValidAwsRegion(credential.region);
517
- const params = new URLSearchParams({ origin: "AI_EDITOR" });
518
- const endpoint = `https://q.${region}.amazonaws.com/ListAvailableModels?${params.toString()}`;
519
- const headers = {
520
- Accept: "application/json",
521
- "User-Agent": "AWS-SDK-JS/3.0.0 kiro-ide/1.0.0",
522
- "X-Amz-User-Agent": "aws-sdk-js/3.0.0 kiro-ide/1.0.0",
523
- };
524
- if (credential.authMethod === "api_key" && credential.apiKey) {
525
- headers["Authorization"] = `Bearer ${credential.apiKey}`;
526
- headers["TokenType"] = "API_KEY";
527
- }
528
- else if (credential.accessToken) {
529
- headers["Authorization"] = `Bearer ${credential.accessToken}`;
530
- if (credential.authMethod === "external_idp") {
531
- headers["TokenType"] = "EXTERNAL_IDP";
532
- }
533
- }
534
- const response = await fetch(endpoint, {
535
- method: "GET",
536
- headers,
537
- signal: AbortSignal.timeout(15_000),
869
+ const refreshedId = await withKiroCredential(auth, async (current) => {
870
+ await ensureKiroModelCatalog(current);
871
+ return kiroCredentialId(current);
538
872
  });
539
- if (response.ok) {
540
- const json = (await response.json());
541
- const models = Array.isArray(json?.models) ? json.models : [];
542
- const baseIds = models
543
- .map((m) => m.modelId || m.id || "")
544
- .filter(Boolean);
545
- if (baseIds.length > 0) {
546
- const variants = [];
547
- for (const id of baseIds) {
548
- variants.push(id);
549
- variants.push(`${id}-thinking`);
550
- variants.push(`${id}-agentic`);
551
- variants.push(`${id}-thinking-agentic`);
552
- }
553
- cachedKiroModels = variants;
554
- lastKiroModelFetch = now;
555
- return variants;
556
- }
873
+ if ((cachedKiroCredentialId === credentialId ||
874
+ cachedKiroCredentialId === refreshedId) &&
875
+ cachedKiroModels) {
876
+ return cachedKiroModels;
557
877
  }
558
878
  }
559
879
  catch { }
560
- return cachedKiroModels ?? [...kiroFallbackModels];
880
+ return [...kiroFallbackModels];
561
881
  },
562
882
  async ping(auth) {
563
883
  const credential = resolveKiroCredential(auth);
564
- const region = assertValidAwsRegion(credential.region);
565
- const params = new URLSearchParams({ origin: "AI_EDITOR" });
566
- const endpoint = `https://q.${region}.amazonaws.com/ListAvailableModels?${params.toString()}`;
567
- const headers = {
568
- Accept: "application/json",
569
- "User-Agent": "AWS-SDK-JS/3.0.0 kiro-ide/1.0.0",
570
- "X-Amz-User-Agent": "aws-sdk-js/3.0.0 kiro-ide/1.0.0",
571
- };
572
- if (credential.authMethod === "api_key" && credential.apiKey) {
573
- headers["Authorization"] = `Bearer ${credential.apiKey}`;
574
- headers["TokenType"] = "API_KEY";
575
- }
576
- else if (credential.accessToken) {
577
- headers["Authorization"] = `Bearer ${credential.accessToken}`;
578
- if (credential.authMethod === "external_idp") {
579
- headers["TokenType"] = "EXTERNAL_IDP";
580
- }
884
+ try {
885
+ await discoverKiroModelsDetailed(credential);
581
886
  }
582
- const response = await fetch(endpoint, {
583
- method: "GET",
584
- headers,
585
- signal: AbortSignal.timeout(15_000),
586
- });
587
- if (!response.ok) {
588
- throw new ProviderError(`Kiro AI ping failed (HTTP ${response.status}). Run \`clai auth kiro\` to sign in.`, response.status);
887
+ catch (error) {
888
+ const detail = error instanceof Error ? `: ${error.message}` : "";
889
+ throw new ProviderError(`Kiro AI ping failed${detail}. Run \`clai auth kiro\` to sign in.`);
589
890
  }
590
891
  },
591
892
  async complete(request, auth, onStatus) {
@@ -595,20 +896,27 @@ export const kiroProvider = {
595
896
  const requestedModel = request.model ?? defaultModels.kiro;
596
897
  const { upstream, agentic, thinking } = resolveKiroModel(requestedModel);
597
898
  return withKiroCredential(auth, async (credential) => {
899
+ await ensureKiroModelCatalog(credential).catch(() => { });
598
900
  const body = buildKiroRequestBody(request, credential, upstream, agentic, thinking);
599
- const endpoints = getCandidateEndpoints(credential);
901
+ const catalogIsFresh = hasFreshKiroCatalog(kiroCredentialId(credential));
902
+ const endpoints = getCandidateEndpoints(credential, catalogIsFresh ? cachedKiroApiGeneration : undefined);
600
903
  let lastError;
601
904
  for (const endpoint of endpoints) {
602
905
  try {
603
906
  const headers = {
604
- "Content-Type": "application/json",
907
+ "Content-Type": endpoint.awsTarget
908
+ ? "application/x-amz-json-1.0"
909
+ : "application/json",
605
910
  Accept: "application/vnd.amazon.eventstream",
606
911
  "Amz-Sdk-Request": "attempt=1; max=3",
607
912
  "Amz-Sdk-Invocation-Id": randomUUID(),
608
- "x-amzn-kiro-agent-mode": "spec",
913
+ "User-Agent": "aws-sdk-js/3.0.0 kiro-ide/1.0.0",
914
+ "X-Amz-User-Agent": "aws-sdk-js/3.0.0 kiro-ide/1.0.0",
915
+ "x-amzn-kiro-agent-mode": "vibe",
916
+ "x-amzn-codewhisperer-optout": "true",
609
917
  "x-amzn-codewhisperer-machine-id": "kiro-desktop",
610
918
  };
611
- if (endpoint.includes("codewhisperer")) {
919
+ if (endpoint.awsTarget) {
612
920
  headers["X-Amz-Target"] =
613
921
  "AmazonCodeWhispererStreamingService.GenerateAssistantResponse";
614
922
  }
@@ -626,7 +934,7 @@ export const kiroProvider = {
626
934
  headers["x-amzn-codewhisperer-profile-arn"] = credential.profileArn;
627
935
  }
628
936
  }
629
- const response = await fetch(endpoint, {
937
+ const response = await generationFetch(endpoint.url, {
630
938
  method: "POST",
631
939
  headers,
632
940
  body: JSON.stringify(body),
@@ -635,10 +943,15 @@ export const kiroProvider = {
635
943
  if (!response.ok) {
636
944
  const errorText = await response.text().catch(() => "");
637
945
  if (response.status === 401 || response.status === 403) {
638
- throw new ProviderError(`Kiro authentication failed (${response.status}): ${errorText}`, response.status);
946
+ const authError = new ProviderError(`Kiro authentication failed (${response.status}): ${errorText}`, response.status);
947
+ if (response.status === 401 && credential.refreshToken) {
948
+ throw authError;
949
+ }
950
+ lastError = authError;
951
+ continue;
639
952
  }
640
953
  if (response.status === 400) {
641
- throw new ProviderError(`Kiro request invalid (${response.status}): ${errorText}`, response.status);
954
+ throw new ProviderError(`Kiro request invalid (${response.status}): ${errorText}`, response.status, errorText);
642
955
  }
643
956
  lastError = new ProviderError(`Kiro endpoint error (${response.status}): ${errorText}`, response.status);
644
957
  continue;
@@ -647,30 +960,76 @@ export const kiroProvider = {
647
960
  throw new ProviderError("Empty response body from Kiro endpoint", 500);
648
961
  }
649
962
  let fullText = "";
650
- let reasoningText = "";
963
+ const pendingStreamEvents = [];
964
+ const reasoning = createKiroReasoningAccumulator((text) => pendingStreamEvents.push({ kind: "reasoning", text }));
651
965
  let insideThinkingTag = false;
652
966
  let finishReason = "stop";
967
+ let stopReason = "";
653
968
  const accumulatedTools = [];
654
969
  let currentToolIndex = -1;
655
970
  let promptTokens = 0;
656
971
  let completionTokens = 0;
657
972
  let cachedTokens = 0;
658
973
  let cacheCreationTokens = 0;
974
+ const usageCharges = [];
975
+ let contextUsagePercentage;
659
976
  for await (const frame of parseEventStream(response.body)) {
977
+ const messageType = typeof frame.headers[":message-type"] === "string"
978
+ ? frame.headers[":message-type"]
979
+ : "event";
980
+ const headerExceptionType = typeof frame.headers[":exception-type"] === "string"
981
+ ? frame.headers[":exception-type"]
982
+ : "";
660
983
  const eventType = (typeof frame.headers[":event-type"] === "string"
661
984
  ? frame.headers[":event-type"]
662
985
  : undefined) ||
663
- Object.keys(frame.payload).find((k) => k.endsWith("Event")) ||
986
+ headerExceptionType ||
987
+ Object.keys(frame.payload).find((key) => key.endsWith("Event") || key.endsWith("Exception")) ||
664
988
  "";
665
- const eventPayload = frame.payload[eventType] ||
666
- frame.payload;
989
+ const nestedPayload = asKiroRecord(frame.payload[eventType]);
990
+ const eventPayload = nestedPayload ?? frame.payload;
991
+ if (messageType === "exception" || headerExceptionType || eventType.endsWith("Exception")) {
992
+ const message = typeof eventPayload.message === "string"
993
+ ? eventPayload.message
994
+ : typeof eventPayload.reason === "string"
995
+ ? eventPayload.reason
996
+ : "Kiro stream exception";
997
+ throw new ProviderError(`${headerExceptionType || eventType || "Kiro stream exception"}: ${message}`, inBandBadRequestStatus({
998
+ type: headerExceptionType || eventType,
999
+ code: eventPayload.code,
1000
+ message,
1001
+ }) ??
1002
+ (/THINKING_SIGNATURE_INVALID|THINKING_SIGNATURE_MISMATCH/i.test(`${headerExceptionType} ${eventType} ${message}`)
1003
+ ? 400
1004
+ : 502), JSON.stringify(eventPayload));
1005
+ }
1006
+ if (typeof eventPayload.usage === "number" &&
1007
+ Number.isFinite(eventPayload.usage) &&
1008
+ eventPayload.usage >= 0 &&
1009
+ typeof eventPayload.unit === "string" &&
1010
+ eventPayload.unit.trim()) {
1011
+ usageCharges.push({
1012
+ amount: eventPayload.usage,
1013
+ unit: typeof eventPayload.unitPlural === "string" &&
1014
+ eventPayload.unitPlural.trim()
1015
+ ? eventPayload.unitPlural.trim()
1016
+ : eventPayload.unit.trim(),
1017
+ ...(typeof eventPayload.currency === "string"
1018
+ ? { currency: eventPayload.currency }
1019
+ : {}),
1020
+ });
1021
+ }
667
1022
  if (eventType === "assistantResponseEvent" ||
668
1023
  eventType.includes("Response")) {
1024
+ if (typeof eventPayload.contextUsagePercentage === "number") {
1025
+ contextUsagePercentage = eventPayload.contextUsagePercentage;
1026
+ }
669
1027
  const chunk = typeof eventPayload.content === "string"
670
1028
  ? eventPayload.content
671
1029
  : "";
672
1030
  if (chunk) {
673
1031
  let visibleChunk = "";
1032
+ let reasoningChunk = "";
674
1033
  let i = 0;
675
1034
  while (i < chunk.length) {
676
1035
  if (!insideThinkingTag) {
@@ -688,30 +1047,51 @@ export const kiroProvider = {
688
1047
  else {
689
1048
  const tagEnd = chunk.indexOf("</thinking>", i);
690
1049
  if (tagEnd !== -1) {
691
- reasoningText += chunk.slice(i, tagEnd);
1050
+ reasoningChunk += chunk.slice(i, tagEnd);
692
1051
  insideThinkingTag = false;
693
1052
  i = tagEnd + 11;
694
1053
  }
695
1054
  else {
696
- reasoningText += chunk.slice(i);
1055
+ reasoningChunk += chunk.slice(i);
697
1056
  break;
698
1057
  }
699
1058
  }
700
1059
  }
1060
+ reasoning.push(reasoningChunk);
701
1061
  if (visibleChunk) {
702
1062
  fullText += visibleChunk;
703
- onToken(visibleChunk);
1063
+ pendingStreamEvents.push({ kind: "token", text: visibleChunk });
704
1064
  }
705
1065
  }
706
1066
  }
707
1067
  else if (eventType === "reasoningContentEvent" ||
708
- eventType.includes("Reasoning")) {
709
- const chunk = typeof eventPayload.content === "string"
710
- ? eventPayload.content
711
- : "";
712
- if (chunk) {
713
- reasoningText += chunk;
714
- }
1068
+ /reasoning/i.test(eventType) ||
1069
+ eventPayload.reasoningText !== undefined) {
1070
+ const reasoningPayload = asKiroRecord(eventPayload.reasoningText);
1071
+ const chunk = typeof eventPayload.text === "string"
1072
+ ? eventPayload.text
1073
+ : typeof eventPayload.content === "string"
1074
+ ? eventPayload.content
1075
+ : typeof eventPayload.reasoningText === "string"
1076
+ ? eventPayload.reasoningText
1077
+ : typeof reasoningPayload?.text === "string"
1078
+ ? reasoningPayload.text
1079
+ : typeof reasoningPayload?.Text === "string"
1080
+ ? reasoningPayload.Text
1081
+ : "";
1082
+ reasoning.sign(typeof eventPayload.signature === "string"
1083
+ ? eventPayload.signature
1084
+ : typeof eventPayload.Signature === "string"
1085
+ ? eventPayload.Signature
1086
+ : typeof reasoningPayload?.signature === "string"
1087
+ ? reasoningPayload.signature
1088
+ : typeof reasoningPayload?.Signature === "string"
1089
+ ? reasoningPayload.Signature
1090
+ : "");
1091
+ reasoning.redact(typeof eventPayload.redactedContent === "string"
1092
+ ? eventPayload.redactedContent
1093
+ : "");
1094
+ reasoning.push(chunk);
715
1095
  }
716
1096
  else if (eventType === "codeEvent") {
717
1097
  const chunk = typeof eventPayload.content === "string"
@@ -719,72 +1099,124 @@ export const kiroProvider = {
719
1099
  : "";
720
1100
  if (chunk) {
721
1101
  fullText += chunk;
722
- onToken(chunk);
1102
+ pendingStreamEvents.push({ kind: "token", text: chunk });
723
1103
  }
724
1104
  }
725
1105
  else if (eventType === "toolUseEvent") {
726
- const toolId = typeof eventPayload.toolUseId === "string"
1106
+ const explicitToolId = typeof eventPayload.toolUseId === "string"
727
1107
  ? eventPayload.toolUseId
728
- : `tool_${accumulatedTools.length + 1}`;
1108
+ : typeof eventPayload.tool_use_id === "string"
1109
+ ? eventPayload.tool_use_id
1110
+ : undefined;
729
1111
  const name = typeof eventPayload.name === "string" ? eventPayload.name : "";
730
1112
  const input = eventPayload.input;
731
- let existing = accumulatedTools.find((t) => t.id === toolId);
732
- if (!existing) {
733
- existing = { id: toolId, name, rawArgs: "" };
1113
+ const stopped = eventPayload.stop === true;
1114
+ let existing = explicitToolId
1115
+ ? accumulatedTools.find((tool) => tool.id === explicitToolId)
1116
+ : accumulatedTools[currentToolIndex];
1117
+ if (explicitToolId &&
1118
+ !existing &&
1119
+ currentToolIndex >= 0 &&
1120
+ accumulatedTools[currentToolIndex]?.syntheticId) {
1121
+ const activeTool = accumulatedTools[currentToolIndex];
1122
+ activeTool.id = explicitToolId;
1123
+ activeTool.syntheticId = false;
1124
+ existing = activeTool;
1125
+ }
1126
+ if (!existing && (explicitToolId || name || input !== undefined)) {
1127
+ existing = {
1128
+ id: explicitToolId ?? `tool_${accumulatedTools.length + 1}`,
1129
+ name,
1130
+ rawArgs: "",
1131
+ syntheticId: explicitToolId === undefined,
1132
+ };
734
1133
  accumulatedTools.push(existing);
735
- currentToolIndex = accumulatedTools.length - 1;
736
1134
  }
737
- let chunkBytes = 0;
1135
+ if (!existing) {
1136
+ if (stopped)
1137
+ currentToolIndex = -1;
1138
+ continue;
1139
+ }
738
1140
  if (typeof input === "string") {
739
1141
  existing.rawArgs += input;
740
- chunkBytes = Buffer.byteLength(input, "utf8");
741
1142
  }
742
1143
  else if (input && typeof input === "object") {
743
1144
  existing.rawArgs = JSON.stringify(input);
744
- chunkBytes = Buffer.byteLength(existing.rawArgs, "utf8");
745
1145
  }
746
- if (name && !existing.name) {
1146
+ if (name && !existing.name)
747
1147
  existing.name = name;
748
- }
1148
+ currentToolIndex = accumulatedTools.indexOf(existing);
1149
+ const argumentsBytes = Buffer.byteLength(existing.rawArgs, "utf8");
749
1150
  const delta = {
750
1151
  index: currentToolIndex,
751
1152
  id: existing.id,
752
1153
  name: existing.name,
753
- argumentsBytes: chunkBytes,
1154
+ argumentsBytes,
754
1155
  };
755
- request.onToolCallDelta?.(delta);
1156
+ pendingStreamEvents.push({ kind: "tool", delta });
756
1157
  finishReason = "tool_calls";
1158
+ if (stopped)
1159
+ currentToolIndex = -1;
757
1160
  }
758
1161
  else if (eventType === "messageStopEvent") {
759
- const stop = typeof eventPayload.stopReason === "string"
760
- ? eventPayload.stopReason
761
- : "";
762
- if (stop) {
763
- finishReason =
764
- stop === "tool_use" || stop === "tool_calls"
765
- ? "tool_calls"
766
- : stop === "max_tokens" || stop === "length"
767
- ? "length"
768
- : "stop";
1162
+ stopReason = mergeKiroStopReason(stopReason, normalizeKiroStopReason(eventPayload.stopReason ?? eventPayload.stop_reason));
1163
+ }
1164
+ else if (eventType === "contextUsageEvent") {
1165
+ if (typeof eventPayload.contextUsagePercentage === "number") {
1166
+ contextUsagePercentage = eventPayload.contextUsagePercentage;
769
1167
  }
770
1168
  }
771
1169
  else if (eventType === "metricsEvent" ||
772
- eventType === "contextUsageEvent" ||
773
- eventType === "meteringEvent") {
774
- if (typeof eventPayload.inputTokens === "number") {
775
- promptTokens = eventPayload.inputTokens;
776
- }
777
- if (typeof eventPayload.outputTokens === "number") {
778
- completionTokens = eventPayload.outputTokens;
1170
+ eventType === "meteringEvent" ||
1171
+ eventType === "metadataEvent" ||
1172
+ eventType === "MetadataEvent") {
1173
+ const usagePayload = asKiroRecord(eventPayload.usage);
1174
+ const tokenUsagePayload = asKiroRecord(eventPayload.tokenUsage);
1175
+ const usageSources = [
1176
+ eventPayload,
1177
+ ...(usagePayload ? [usagePayload] : []),
1178
+ ...(tokenUsagePayload ? [tokenUsagePayload] : []),
1179
+ ];
1180
+ const inputTokens = firstKiroMetric(usageSources, [
1181
+ "inputTokens",
1182
+ "input_tokens",
1183
+ "promptTokens",
1184
+ "prompt_tokens",
1185
+ ]);
1186
+ const outputTokens = firstKiroMetric(usageSources, [
1187
+ "outputTokens",
1188
+ "output_tokens",
1189
+ "completionTokens",
1190
+ "completion_tokens",
1191
+ ]);
1192
+ const cacheReadTokens = firstKiroMetric(usageSources, [
1193
+ "cacheReadTokens",
1194
+ "cache_read_tokens",
1195
+ "cacheReadInputTokens",
1196
+ "cache_read_input_tokens",
1197
+ "cachedTokens",
1198
+ ]);
1199
+ const cacheWriteTokens = firstKiroMetric(usageSources, [
1200
+ "cacheCreationTokens",
1201
+ "cache_creation_tokens",
1202
+ "cacheWriteTokens",
1203
+ "cache_write_tokens",
1204
+ "cacheWriteInputTokens",
1205
+ "cacheCreationInputTokens",
1206
+ "cache_creation_input_tokens",
1207
+ ]);
1208
+ if (inputTokens !== undefined)
1209
+ promptTokens = inputTokens;
1210
+ if (outputTokens !== undefined)
1211
+ completionTokens = outputTokens;
1212
+ if (cacheReadTokens !== undefined) {
1213
+ cachedTokens = Math.max(cachedTokens, cacheReadTokens);
779
1214
  }
780
- if (typeof eventPayload.cachedTokens === "number") {
781
- cachedTokens = eventPayload.cachedTokens;
1215
+ if (cacheWriteTokens !== undefined) {
1216
+ cacheCreationTokens = Math.max(cacheCreationTokens, cacheWriteTokens);
782
1217
  }
783
- if (typeof eventPayload.cacheReadInputTokens === "number") {
784
- cachedTokens = Math.max(cachedTokens, eventPayload.cacheReadInputTokens);
785
- }
786
- if (typeof eventPayload.cacheWriteInputTokens === "number") {
787
- cacheCreationTokens = eventPayload.cacheWriteInputTokens;
1218
+ if (/^metadataevent$/i.test(eventType)) {
1219
+ stopReason = mergeKiroStopReason(stopReason, normalizeKiroStopReason(eventPayload.stopReason ?? eventPayload.stop_reason));
788
1220
  }
789
1221
  }
790
1222
  }
@@ -803,10 +1235,50 @@ export const kiroProvider = {
803
1235
  rawArguments: t.rawArgs,
804
1236
  };
805
1237
  });
806
- if (toolCalls.length > 0) {
1238
+ reasoning.finish();
1239
+ const hasReasoningEvidence = Boolean(reasoning.text.trim() || reasoning.signature || reasoning.redactedContent);
1240
+ const stopDisposition = kiroStopDisposition(stopReason, toolCalls.length > 0);
1241
+ if (stopDisposition === "blocked") {
1242
+ throw new ProviderError(`Kiro blocked the response under its content safety policy (${stopReason})`, 400);
1243
+ }
1244
+ if (stopDisposition === "retryable" ||
1245
+ stopDisposition === "incomplete" ||
1246
+ stopDisposition === "unknown") {
1247
+ throw new ProviderError(`Kiro ended with non-success stop reason: ${stopReason}`, 502);
1248
+ }
1249
+ if (!fullText.trim() &&
1250
+ !hasReasoningEvidence &&
1251
+ toolCalls.length === 0) {
1252
+ throw new ProviderError("Kiro completed without a visible answer.");
1253
+ }
1254
+ if (toolCalls.length === 0 && ["...", "…"].includes(fullText.trim())) {
1255
+ throw new ProviderError("Kiro completed without a visible answer.");
1256
+ }
1257
+ if (reasoning.opaque && (fullText.trim() || toolCalls.length > 0)) {
1258
+ pendingStreamEvents.push({
1259
+ kind: "reasoning",
1260
+ text: kiroPrivateReasoningNote({
1261
+ model: upstream,
1262
+ effort: sentKiroReasoningEffort(asKiroRecord(body.additionalModelRequestFields)),
1263
+ signatureChars: reasoning.signature.length,
1264
+ }),
1265
+ });
1266
+ }
1267
+ if (stopDisposition === "length") {
1268
+ finishReason = "length";
1269
+ }
1270
+ else if (toolCalls.length > 0) {
807
1271
  finishReason = "tool_calls";
808
1272
  }
809
- const usage = promptTokens > 0 || completionTokens > 0
1273
+ const advertisedWindow = kiroModelCatalog().find((info) => info.modelId === upstream)
1274
+ ?.maxInputTokens;
1275
+ const catalogWindow = advertisedWindow !== undefined &&
1276
+ Number.isFinite(advertisedWindow) &&
1277
+ advertisedWindow > 0
1278
+ ? Math.floor(advertisedWindow)
1279
+ : undefined;
1280
+ const estimatedPromptTokens = providerRatioPromptTokens(contextUsagePercentage, catalogWindow ?? modelContextWindow(upstream, "kiro"));
1281
+ const tokenUsage = promptTokens > 0 || completionTokens > 0
810
1282
  ? {
811
1283
  promptTokens,
812
1284
  completionTokens,
@@ -818,7 +1290,54 @@ export const kiroProvider = {
818
1290
  ? Math.max(0, promptTokens - cachedTokens)
819
1291
  : promptTokens,
820
1292
  }
821
- : undefined;
1293
+ : catalogWindow !== undefined
1294
+ ? providerRatioUsage({
1295
+ percentUsed: contextUsagePercentage,
1296
+ windowTokens: catalogWindow,
1297
+ })
1298
+ : estimatedPromptTokens !== undefined
1299
+ ? {
1300
+ promptTokens: estimatedPromptTokens,
1301
+ completionTokens: 0,
1302
+ totalTokens: estimatedPromptTokens,
1303
+ exact: false,
1304
+ }
1305
+ : undefined;
1306
+ const usage = tokenUsage
1307
+ ? {
1308
+ ...tokenUsage,
1309
+ ...(usageCharges.length > 0 ? { charges: usageCharges } : {}),
1310
+ }
1311
+ : usageCharges.length > 0
1312
+ ? {
1313
+ promptTokens: 0,
1314
+ promptTokensKnown: false,
1315
+ completionTokens: 0,
1316
+ totalTokens: 0,
1317
+ exact: false,
1318
+ charges: usageCharges,
1319
+ }
1320
+ : undefined;
1321
+ const reasoningResult = kiroReasoningResult({
1322
+ reasoning,
1323
+ provenance: createReasoningArtifactProvenance({
1324
+ provider: "kiro",
1325
+ model: upstream,
1326
+ dialect: "kiro-eventstream",
1327
+ }),
1328
+ hasToolCalls: toolCalls.length > 0,
1329
+ });
1330
+ for (const event of pendingStreamEvents) {
1331
+ if (event.kind === "token") {
1332
+ onToken(event.text);
1333
+ }
1334
+ else if (event.kind === "reasoning") {
1335
+ emitStreamReasoningDelta(request.onStreamEvent, event.text);
1336
+ }
1337
+ else {
1338
+ request.onToolCallDelta?.(event.delta);
1339
+ }
1340
+ }
822
1341
  return {
823
1342
  text: fullText,
824
1343
  provider: "kiro",
@@ -827,13 +1346,23 @@ export const kiroProvider = {
827
1346
  finishReason,
828
1347
  toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
829
1348
  usage,
830
- reasoningBlock: reasoningText.trim()
831
- ? { text: reasoningText.trim() }
1349
+ reasoningArtifacts: reasoningResult.artifacts.length > 0
1350
+ ? reasoningResult.artifacts
832
1351
  : undefined,
1352
+ reasoningBlock: reasoningResult.block,
833
1353
  };
834
1354
  }
835
1355
  catch (err) {
836
- if (err instanceof ProviderError && (err.status === 401 || err.status === 403 || err.status === 400)) {
1356
+ if (request.signal?.aborted) {
1357
+ completeGenerationAttempt("cancelled");
1358
+ throw err;
1359
+ }
1360
+ const status = err instanceof ProviderError ? err.status : undefined;
1361
+ completeGenerationAttempt("failure", undefined, status);
1362
+ if (err instanceof ProviderError &&
1363
+ (status === 400 ||
1364
+ status === undefined ||
1365
+ (status === 401 && Boolean(credential.refreshToken)))) {
837
1366
  throw err;
838
1367
  }
839
1368
  lastError = err instanceof Error ? err : new Error(String(err));