@sunerpy/opencode-kiro-auth 0.13.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,6 +19,9 @@ export declare const KIRO_CONSTANTS: {
19
19
  export declare const MODEL_MAPPING: Record<string, string>;
20
20
  export declare const SUPPORTED_MODELS: string[];
21
21
  export declare function isLongContextModel(model: string): boolean;
22
+ export declare const DEFAULT_MODEL_CONTEXT_LIMIT = 200000;
23
+ export declare const MODEL_CONTEXT_LIMITS: Record<string, number>;
24
+ export declare function getModelContextLimit(baseModel: string): number;
22
25
  export declare const KIRO_AUTH_SERVICE: {
23
26
  ENDPOINT: string;
24
27
  SSO_OIDC_ENDPOINT: string;
package/dist/constants.js CHANGED
@@ -98,6 +98,37 @@ const LONG_CONTEXT_MODELS = new Set(Object.keys(MODEL_MAPPING).filter((k) => k.i
98
98
  export function isLongContextModel(model) {
99
99
  return LONG_CONTEXT_MODELS.has(model);
100
100
  }
101
+ // SSOT for token accounting, keyed by BASE model id (effort/thinking suffix
102
+ // stripped). Values MUST equal the limit.context advertised in src/plugin.ts —
103
+ // enforced by context-window.test.ts. Drift makes OpenCode under-count usage and
104
+ // skip auto-compaction until Kiro hard-rejects with 400 "Input is too long."
105
+ export const DEFAULT_MODEL_CONTEXT_LIMIT = 200000;
106
+ export const MODEL_CONTEXT_LIMITS = {
107
+ auto: 200000,
108
+ 'claude-haiku-4-5': 200000,
109
+ 'claude-sonnet-4': 200000,
110
+ 'claude-sonnet-4-5': 200000,
111
+ 'claude-sonnet-4-5-1m': 1000000,
112
+ 'claude-sonnet-4-6': 1000000,
113
+ 'claude-sonnet-4-6-1m': 1000000,
114
+ 'claude-sonnet-5': 1000000,
115
+ 'claude-opus-4-5': 200000,
116
+ 'claude-opus-4-6': 1000000,
117
+ 'claude-opus-4-6-1m': 1000000,
118
+ 'claude-opus-4-7': 1000000,
119
+ 'claude-opus-4-8': 1000000,
120
+ 'deepseek-3.2': 128000,
121
+ 'glm-5': 200000,
122
+ 'minimax-m2.5': 200000,
123
+ 'minimax-m2.1': 200000,
124
+ 'qwen3-coder-next': 256000,
125
+ 'gpt-5.6-sol': 272000,
126
+ 'gpt-5.6-terra': 272000,
127
+ 'gpt-5.6-luna': 272000
128
+ };
129
+ export function getModelContextLimit(baseModel) {
130
+ return MODEL_CONTEXT_LIMITS[baseModel] ?? DEFAULT_MODEL_CONTEXT_LIMIT;
131
+ }
101
132
  export const KIRO_AUTH_SERVICE = {
102
133
  ENDPOINT: 'https://prod.{{region}}.auth.desktop.kiro.dev',
103
134
  SSO_OIDC_ENDPOINT: 'https://oidc.{{region}}.amazonaws.com',
@@ -3,6 +3,7 @@ import type { AccountManager } from '../../plugin/accounts.js';
3
3
  import type { ManagedAccount } from '../../plugin/types.js';
4
4
  import type { ForceRefreshResult } from '../auth/token-refresher.js';
5
5
  type ToastFunction = (message: string, variant: 'info' | 'warning' | 'success' | 'error') => void;
6
+ export declare function isKiroContextOverflowBody(text: string): boolean;
6
7
  export interface RequestContext {
7
8
  retry: number;
8
9
  forcedRefreshAccountIds?: Set<string>;
@@ -1,4 +1,11 @@
1
1
  import { isAccessTokenError, toDeadReason } from '../../plugin/health.js';
2
+ // Unambiguous Kiro size-overflow 400 signals only. The generic "Improperly
3
+ // formed request." is deliberately excluded: it has no size discriminator in
4
+ // the body, so matching it would wrongly reclassify unrelated 400s as overflow.
5
+ const KIRO_CONTEXT_OVERFLOW_PATTERNS = [/input is too long/i, /CONTENT_LENGTH_EXCEEDS_THRESHOLD/i];
6
+ export function isKiroContextOverflowBody(text) {
7
+ return KIRO_CONTEXT_OVERFLOW_PATTERNS.some((p) => p.test(text));
8
+ }
2
9
  export class ErrorHandler {
3
10
  config;
4
11
  accountManager;
@@ -7,7 +7,7 @@ import { syncFromKiroCli } from '../../plugin/sync/kiro-cli.js';
7
7
  import { AccountSelector } from '../account/account-selector.js';
8
8
  import { UsageTracker } from '../account/usage-tracker.js';
9
9
  import { TokenRefresher } from '../auth/token-refresher.js';
10
- import { ErrorHandler } from './error-handler.js';
10
+ import { ErrorHandler, isKiroContextOverflowBody } from './error-handler.js';
11
11
  import { ResponseHandler } from './response-handler.js';
12
12
  import { RetryStrategy } from './retry-strategy.js';
13
13
  const KIRO_API_PATTERN = /^(https?:\/\/)?q\.[a-z0-9-]+\.amazonaws\.com/;
@@ -155,12 +155,14 @@ export class RequestHandler {
155
155
  if (apiTimestamp) {
156
156
  this.logSdkError(sdkPrep, e, acc, apiTimestamp);
157
157
  }
158
- const mockResponse = new Response(JSON.stringify({ message: e.message, __type: e.name }), {
158
+ const errorBody = JSON.stringify({ message: e.message, __type: e.name });
159
+ const errorStatusText = e.name || 'Error';
160
+ const jsonHeaders = { 'Content-Type': 'application/json' };
161
+ const errorResult = await this.errorHandler.handle(e, new Response(errorBody, {
159
162
  status: httpStatus,
160
- statusText: e.name || 'Error',
161
- headers: { 'Content-Type': 'application/json' }
162
- });
163
- const errorResult = await this.errorHandler.handle(e, mockResponse, acc, handlerContext, showToast);
163
+ statusText: errorStatusText,
164
+ headers: jsonHeaders
165
+ }), acc, handlerContext, showToast);
164
166
  if (errorResult.shouldRetry) {
165
167
  if (errorResult.newContext) {
166
168
  handlerContext = errorResult.newContext;
@@ -170,7 +172,17 @@ export class RequestHandler {
170
172
  }
171
173
  continue;
172
174
  }
173
- throw new Error(`Kiro Error: ${httpStatus}`);
175
+ // Terminal, non-retryable HTTP error. Return a fresh Response carrying
176
+ // the real Kiro body so @ai-sdk/openai-compatible produces an
177
+ // APICallError with status+body (not a bare Error that OpenCode
178
+ // degrades to UnknownError). Remap size-overflow 400s to 413 so
179
+ // OpenCode classifies context_overflow and auto-compacts.
180
+ const terminalStatus = httpStatus === 400 && isKiroContextOverflowBody(e.message ?? '') ? 413 : httpStatus;
181
+ return new Response(errorBody, {
182
+ status: terminalStatus,
183
+ statusText: errorStatusText,
184
+ headers: jsonHeaders
185
+ });
174
186
  }
175
187
  const networkResult = await this.errorHandler.handleNetworkError(e, handlerContext, showToast);
176
188
  if (networkResult.shouldRetry) {
@@ -20,4 +20,5 @@ export interface ResolvedModelVariant {
20
20
  * with `effort` left undefined.
21
21
  */
22
22
  export declare function resolveModelVariant(model: string): ResolvedModelVariant;
23
+ export declare function stripModelSuffix(model: string): string;
23
24
  export declare function getContextWindowSize(model: string): number;
@@ -1,4 +1,4 @@
1
- import { MODEL_MAPPING, SUPPORTED_MODELS, isLongContextModel } from '../constants.js';
1
+ import { MODEL_MAPPING, SUPPORTED_MODELS, getModelContextLimit } from '../constants.js';
2
2
  export function resolveKiroModel(model) {
3
3
  const resolved = MODEL_MAPPING[model];
4
4
  if (!resolved) {
@@ -43,6 +43,24 @@ export function resolveModelVariant(model) {
43
43
  }
44
44
  return { wireId: resolveKiroModel(model), effort: undefined };
45
45
  }
46
+ export function stripModelSuffix(model) {
47
+ for (const suffix of EFFORT_SUFFIXES) {
48
+ const marker = `-${suffix}`;
49
+ if (model.endsWith(marker)) {
50
+ const base = model.slice(0, -marker.length);
51
+ if (VARIANT_BASE_ALLOWLIST.has(base)) {
52
+ return base;
53
+ }
54
+ }
55
+ }
56
+ if (model.endsWith('-thinking')) {
57
+ const base = model.slice(0, -'-thinking'.length);
58
+ if (MODEL_MAPPING[base]) {
59
+ return base;
60
+ }
61
+ }
62
+ return model;
63
+ }
46
64
  export function getContextWindowSize(model) {
47
- return isLongContextModel(model) ? 1000000 : 200000;
65
+ return getModelContextLimit(stripModelSuffix(model));
48
66
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sunerpy/opencode-kiro-auth",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "description": "OpenCode plugin for AWS Kiro (CodeWhisperer) providing access to Claude models",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",