@zhafron/opencode-kiro-auth 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +1 -1
  2. package/dist/constants.js +7 -1
  3. package/dist/kiro/oauth-idc.js +4 -1
  4. package/dist/plugin/accounts.js +11 -2
  5. package/dist/plugin/auth-page.js +6 -1
  6. package/dist/plugin/config/loader.js +3 -1
  7. package/dist/plugin/config/schema.d.ts +3 -0
  8. package/dist/plugin/config/schema.js +4 -2
  9. package/dist/plugin/logger.d.ts +3 -0
  10. package/dist/plugin/logger.js +24 -6
  11. package/dist/plugin/request.js +154 -26
  12. package/dist/plugin/response.js +9 -2
  13. package/dist/plugin/server.js +16 -3
  14. package/dist/plugin/streaming.js +9 -2
  15. package/dist/plugin/types.d.ts +1 -1
  16. package/dist/plugin/usage.js +11 -2
  17. package/dist/plugin.js +84 -6
  18. package/package.json +2 -2
  19. package/dist/kiro/oauth-social.d.ts +0 -17
  20. package/dist/kiro/oauth-social.js +0 -51
  21. package/dist/plugin/cli.d.ts +0 -6
  22. package/dist/plugin/cli.js +0 -98
  23. package/dist/plugin/debug.d.ts +0 -2
  24. package/dist/plugin/debug.js +0 -9
  25. package/dist/plugin/oauth-parser.d.ts +0 -5
  26. package/dist/plugin/oauth-parser.js +0 -23
  27. package/dist/plugin/quota.d.ts +0 -15
  28. package/dist/plugin/quota.js +0 -68
  29. package/dist/plugin/recovery.d.ts +0 -19
  30. package/dist/plugin/recovery.js +0 -302
  31. package/dist/plugin/refresh-queue.d.ts +0 -14
  32. package/dist/plugin/refresh-queue.js +0 -69
  33. package/dist/src/constants.d.ts +0 -22
  34. package/dist/src/constants.js +0 -35
  35. package/dist/src/kiro/auth.d.ts +0 -5
  36. package/dist/src/kiro/auth.js +0 -69
  37. package/dist/src/kiro/oauth-idc.d.ts +0 -22
  38. package/dist/src/kiro/oauth-idc.js +0 -99
  39. package/dist/src/kiro/oauth-social.d.ts +0 -17
  40. package/dist/src/kiro/oauth-social.js +0 -69
  41. package/dist/src/plugin/accounts.d.ts +0 -23
  42. package/dist/src/plugin/accounts.js +0 -265
  43. package/dist/src/plugin/cli.d.ts +0 -6
  44. package/dist/src/plugin/cli.js +0 -98
  45. package/dist/src/plugin/config/index.d.ts +0 -3
  46. package/dist/src/plugin/config/index.js +0 -2
  47. package/dist/src/plugin/config/loader.d.ts +0 -7
  48. package/dist/src/plugin/config/loader.js +0 -143
  49. package/dist/src/plugin/config/schema.d.ts +0 -68
  50. package/dist/src/plugin/config/schema.js +0 -44
  51. package/dist/src/plugin/debug.d.ts +0 -2
  52. package/dist/src/plugin/debug.js +0 -9
  53. package/dist/src/plugin/errors.d.ts +0 -17
  54. package/dist/src/plugin/errors.js +0 -34
  55. package/dist/src/plugin/logger.d.ts +0 -4
  56. package/dist/src/plugin/logger.js +0 -17
  57. package/dist/src/plugin/models.d.ts +0 -3
  58. package/dist/src/plugin/models.js +0 -14
  59. package/dist/src/plugin/oauth-parser.d.ts +0 -5
  60. package/dist/src/plugin/oauth-parser.js +0 -23
  61. package/dist/src/plugin/quota.d.ts +0 -25
  62. package/dist/src/plugin/quota.js +0 -175
  63. package/dist/src/plugin/recovery.d.ts +0 -19
  64. package/dist/src/plugin/recovery.js +0 -302
  65. package/dist/src/plugin/refresh-queue.d.ts +0 -14
  66. package/dist/src/plugin/refresh-queue.js +0 -69
  67. package/dist/src/plugin/request.d.ts +0 -35
  68. package/dist/src/plugin/request.js +0 -411
  69. package/dist/src/plugin/response.d.ts +0 -6
  70. package/dist/src/plugin/response.js +0 -246
  71. package/dist/src/plugin/server.d.ts +0 -10
  72. package/dist/src/plugin/server.js +0 -203
  73. package/dist/src/plugin/storage.d.ts +0 -5
  74. package/dist/src/plugin/storage.js +0 -106
  75. package/dist/src/plugin/streaming.d.ts +0 -12
  76. package/dist/src/plugin/streaming.js +0 -444
  77. package/dist/src/plugin/token.d.ts +0 -8
  78. package/dist/src/plugin/token.js +0 -130
  79. package/dist/src/plugin/types.d.ts +0 -144
  80. package/dist/src/plugin/types.js +0 -0
  81. package/dist/src/plugin/usage.d.ts +0 -28
  82. package/dist/src/plugin/usage.js +0 -159
  83. package/dist/src/plugin.d.ts +0 -2
  84. package/dist/src/plugin.js +0 -341
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @zhafron/opencode-kiro-auth
1
+ # OpenCode Kiro Auth Plugin
2
2
 
3
3
  OpenCode plugin for AWS Kiro (CodeWhisperer) providing access to the latest Claude 3.5/4.5 models with substantial trial quotas.
4
4
 
package/dist/constants.js CHANGED
@@ -56,5 +56,11 @@ export const KIRO_AUTH_SERVICE = {
56
56
  ENDPOINT: 'https://prod.{{region}}.auth.desktop.kiro.dev',
57
57
  SSO_OIDC_ENDPOINT: 'https://oidc.{{region}}.amazonaws.com',
58
58
  BUILDER_ID_START_URL: 'https://view.awsapps.com/start',
59
- SCOPES: ['codewhisperer:completions', 'codewhisperer:analysis', 'codewhisperer:conversations', 'codewhisperer:transformations', 'codewhisperer:taskassist']
59
+ SCOPES: [
60
+ 'codewhisperer:completions',
61
+ 'codewhisperer:analysis',
62
+ 'codewhisperer:conversations',
63
+ 'codewhisperer:transformations',
64
+ 'codewhisperer:taskassist'
65
+ ]
60
66
  };
@@ -120,7 +120,10 @@ export async function pollKiroIDCToken(clientId, clientSecret, deviceCode, inter
120
120
  }
121
121
  }
122
122
  catch (error) {
123
- if (error instanceof Error && (error.message.includes('expired') || error.message.includes('denied') || error.message.includes('failed'))) {
123
+ if (error instanceof Error &&
124
+ (error.message.includes('expired') ||
125
+ error.message.includes('denied') ||
126
+ error.message.includes('failed'))) {
124
127
  throw error;
125
128
  }
126
129
  if (attempts >= maxAttempts) {
@@ -28,7 +28,10 @@ export class AccountManager {
28
28
  static async loadFromDisk(strategy) {
29
29
  const s = await loadAccounts();
30
30
  const u = await loadUsage();
31
- const accounts = s.accounts.map((m) => ({ ...m, region: m.region || KIRO_CONSTANTS.DEFAULT_REGION }));
31
+ const accounts = s.accounts.map((m) => ({
32
+ ...m,
33
+ region: m.region || KIRO_CONSTANTS.DEFAULT_REGION
34
+ }));
32
35
  return new AccountManager(accounts, u.usage, strategy || 'sticky');
33
36
  }
34
37
  getAccountCount() {
@@ -140,7 +143,13 @@ export class AccountManager {
140
143
  await saveUsage({ version: 1, usage: this.usage });
141
144
  }
142
145
  toAuthDetails(a) {
143
- const p = { refreshToken: a.refreshToken, profileArn: a.profileArn, clientId: a.clientId, clientSecret: a.clientSecret, authMethod: a.authMethod };
146
+ const p = {
147
+ refreshToken: a.refreshToken,
148
+ profileArn: a.profileArn,
149
+ clientId: a.clientId,
150
+ clientSecret: a.clientSecret,
151
+ authMethod: a.authMethod
152
+ };
144
153
  return {
145
154
  refresh: encodeRefreshToken(p),
146
155
  access: a.accessToken,
@@ -1,5 +1,10 @@
1
1
  function escapeHtml(text) {
2
- return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;');
2
+ return text
3
+ .replace(/&/g, '&amp;')
4
+ .replace(/</g, '&lt;')
5
+ .replace(/>/g, '&gt;')
6
+ .replace(/"/g, '&quot;')
7
+ .replace(/'/g, '&#039;');
3
8
  }
4
9
  export function getIDCAuthHtml(verificationUrl, userCode, statusUrl) {
5
10
  const escapedUrl = escapeHtml(verificationUrl);
@@ -95,7 +95,9 @@ function applyEnvOverrides(config) {
95
95
  account_selection_strategy: env.KIRO_ACCOUNT_SELECTION_STRATEGY
96
96
  ? AccountSelectionStrategySchema.catch('lowest-usage').parse(env.KIRO_ACCOUNT_SELECTION_STRATEGY)
97
97
  : config.account_selection_strategy,
98
- default_region: env.KIRO_DEFAULT_REGION ? RegionSchema.catch('us-east-1').parse(env.KIRO_DEFAULT_REGION) : config.default_region,
98
+ default_region: env.KIRO_DEFAULT_REGION
99
+ ? RegionSchema.catch('us-east-1').parse(env.KIRO_DEFAULT_REGION)
100
+ : config.default_region,
99
101
  rate_limit_retry_delay_ms: parseNumberEnv(env.KIRO_RATE_LIMIT_RETRY_DELAY_MS, config.rate_limit_retry_delay_ms),
100
102
  rate_limit_max_retries: parseNumberEnv(env.KIRO_RATE_LIMIT_MAX_RETRIES, config.rate_limit_max_retries),
101
103
  usage_tracking_enabled: parseBooleanEnv(env.KIRO_USAGE_TRACKING_ENABLED, config.usage_tracking_enabled)
@@ -15,6 +15,7 @@ export declare const KiroConfigSchema: z.ZodObject<{
15
15
  rate_limit_retry_delay_ms: z.ZodDefault<z.ZodNumber>;
16
16
  rate_limit_max_retries: z.ZodDefault<z.ZodNumber>;
17
17
  usage_tracking_enabled: z.ZodDefault<z.ZodBoolean>;
18
+ enable_log_api_request: z.ZodDefault<z.ZodBoolean>;
18
19
  }, "strip", z.ZodTypeAny, {
19
20
  session_recovery: boolean;
20
21
  auto_resume: boolean;
@@ -26,6 +27,7 @@ export declare const KiroConfigSchema: z.ZodObject<{
26
27
  rate_limit_retry_delay_ms: number;
27
28
  rate_limit_max_retries: number;
28
29
  usage_tracking_enabled: boolean;
30
+ enable_log_api_request: boolean;
29
31
  $schema?: string | undefined;
30
32
  }, {
31
33
  $schema?: string | undefined;
@@ -39,6 +41,7 @@ export declare const KiroConfigSchema: z.ZodObject<{
39
41
  rate_limit_retry_delay_ms?: number | undefined;
40
42
  rate_limit_max_retries?: number | undefined;
41
43
  usage_tracking_enabled?: boolean | undefined;
44
+ enable_log_api_request?: boolean | undefined;
42
45
  }>;
43
46
  export type KiroConfig = z.infer<typeof KiroConfigSchema>;
44
47
  export declare const DEFAULT_CONFIG: KiroConfig;
@@ -12,7 +12,8 @@ export const KiroConfigSchema = z.object({
12
12
  default_region: RegionSchema.default('us-east-1'),
13
13
  rate_limit_retry_delay_ms: z.number().min(1000).max(60000).default(5000),
14
14
  rate_limit_max_retries: z.number().min(0).max(10).default(3),
15
- usage_tracking_enabled: z.boolean().default(true)
15
+ usage_tracking_enabled: z.boolean().default(true),
16
+ enable_log_api_request: z.boolean().default(false)
16
17
  });
17
18
  export const DEFAULT_CONFIG = {
18
19
  session_recovery: true,
@@ -24,5 +25,6 @@ export const DEFAULT_CONFIG = {
24
25
  default_region: 'us-east-1',
25
26
  rate_limit_retry_delay_ms: 5000,
26
27
  rate_limit_max_retries: 3,
27
- usage_tracking_enabled: true
28
+ usage_tracking_enabled: true,
29
+ enable_log_api_request: false
28
30
  };
@@ -2,3 +2,6 @@ export declare function log(message: string, ...args: unknown[]): void;
2
2
  export declare function error(message: string, ...args: unknown[]): void;
3
3
  export declare function warn(message: string, ...args: unknown[]): void;
4
4
  export declare function debug(message: string, ...args: unknown[]): void;
5
+ export declare function logApiRequest(data: any, timestamp: string): void;
6
+ export declare function logApiResponse(data: any, timestamp: string): void;
7
+ export declare function getTimestamp(): string;
@@ -1,9 +1,11 @@
1
- import { appendFileSync, mkdirSync } from 'node:fs';
1
+ import { appendFileSync, mkdirSync, writeFileSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import { join } from 'node:path';
4
4
  const getLogDir = () => {
5
5
  const platform = process.platform;
6
- const base = platform === 'win32' ? join(process.env.APPDATA || join(homedir(), 'AppData', 'Roaming'), 'opencode') : join(process.env.XDG_CONFIG_HOME || join(homedir(), '.config'), 'opencode');
6
+ const base = platform === 'win32'
7
+ ? join(process.env.APPDATA || join(homedir(), 'AppData', 'Roaming'), 'opencode')
8
+ : join(process.env.XDG_CONFIG_HOME || join(homedir(), '.config'), 'opencode');
7
9
  return join(base, 'kiro-logs');
8
10
  };
9
11
  const writeToFile = (level, message, ...args) => {
@@ -17,21 +19,37 @@ const writeToFile = (level, message, ...args) => {
17
19
  }
18
20
  catch (e) { }
19
21
  };
22
+ const writeApiLog = (type, data, timestamp) => {
23
+ try {
24
+ const dir = getLogDir();
25
+ mkdirSync(dir, { recursive: true });
26
+ const filename = `${timestamp}_${type}.json`;
27
+ const path = join(dir, filename);
28
+ const content = JSON.stringify(data, null, 2);
29
+ writeFileSync(path, content);
30
+ }
31
+ catch (e) { }
32
+ };
20
33
  export function log(message, ...args) {
21
- console.log(`[${new Date().toISOString()}] ${message}`, ...args);
22
34
  writeToFile('INFO', message, ...args);
23
35
  }
24
36
  export function error(message, ...args) {
25
- console.error(`[${new Date().toISOString()}] ERROR: ${message}`, ...args);
26
37
  writeToFile('ERROR', message, ...args);
27
38
  }
28
39
  export function warn(message, ...args) {
29
- console.warn(`[${new Date().toISOString()}] WARN: ${message}`, ...args);
30
40
  writeToFile('WARN', message, ...args);
31
41
  }
32
42
  export function debug(message, ...args) {
33
43
  if (process.env.DEBUG) {
34
- console.debug(`[${new Date().toISOString()}] DEBUG: ${message}`, ...args);
35
44
  writeToFile('DEBUG', message, ...args);
36
45
  }
37
46
  }
47
+ export function logApiRequest(data, timestamp) {
48
+ writeApiLog('request', data, timestamp);
49
+ }
50
+ export function logApiResponse(data, timestamp) {
51
+ writeApiLog('response', data, timestamp);
52
+ }
53
+ export function getTimestamp() {
54
+ return new Date().toISOString().replace(/[:.]/g, '-');
55
+ }
@@ -1,14 +1,14 @@
1
1
  import * as crypto from 'crypto';
2
2
  import * as os from 'os';
3
- import { KIRO_CONSTANTS } from '../constants';
4
- import { resolveKiroModel } from './models';
3
+ import { KIRO_CONSTANTS } from '../constants.js';
4
+ import { resolveKiroModel } from './models.js';
5
5
  export function transformToCodeWhisperer(url, body, model, auth, think = false, budget = 20000) {
6
6
  const req = typeof body === 'string' ? JSON.parse(body) : body;
7
- const { messages, tools, system } = req;
7
+ const { messages, tools, system, conversationId } = req;
8
+ const convId = crypto.randomUUID();
8
9
  if (!messages || messages.length === 0)
9
10
  throw new Error('No messages');
10
11
  const resolved = resolveKiroModel(model);
11
- const convId = crypto.randomUUID();
12
12
  let sys = system || '';
13
13
  if (think) {
14
14
  const pfx = `<thinking_mode>enabled</thinking_mode><max_thinking_length>${budget}</max_thinking_length>`;
@@ -20,17 +20,38 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
20
20
  msgs.pop();
21
21
  const cwTools = tools ? convertToolsToCodeWhisperer(tools) : [];
22
22
  const history = [];
23
- let start = 0;
23
+ let firstUserIndex = -1;
24
+ for (let i = 0; i < msgs.length; i++) {
25
+ if (msgs[i].role === 'user') {
26
+ firstUserIndex = i;
27
+ break;
28
+ }
29
+ }
24
30
  if (sys) {
25
- const first = msgs[0];
26
- if (first && first.role === 'user') {
27
- history.push({ userInputMessage: { content: `${sys}\n\n${getContentText(first)}`, modelId: resolved, origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR } });
28
- start = 1;
31
+ if (firstUserIndex !== -1) {
32
+ const m = msgs[firstUserIndex];
33
+ const oldContent = getContentText(m);
34
+ if (Array.isArray(m.content)) {
35
+ m.content = [
36
+ { type: 'text', text: `${sys}\n\n${oldContent}` },
37
+ ...m.content.filter((p) => p.type !== 'text')
38
+ ];
39
+ }
40
+ else {
41
+ m.content = `${sys}\n\n${oldContent}`;
42
+ }
43
+ }
44
+ else {
45
+ history.push({
46
+ userInputMessage: {
47
+ content: sys,
48
+ modelId: resolved,
49
+ origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR
50
+ }
51
+ });
29
52
  }
30
- else
31
- history.push({ userInputMessage: { content: sys, modelId: resolved, origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR } });
32
53
  }
33
- for (let i = start; i < msgs.length - 1; i++) {
54
+ for (let i = 0; i < msgs.length - 1; i++) {
34
55
  const m = msgs[i];
35
56
  if (!m)
36
57
  continue;
@@ -42,9 +63,16 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
42
63
  if (p.type === 'text')
43
64
  uim.content += p.text || '';
44
65
  else if (p.type === 'tool_result')
45
- trs.push({ content: [{ text: getContentText(p) }], status: 'success', toolUseId: p.tool_use_id });
66
+ trs.push({
67
+ content: [{ text: getContentText(p.content || p) }],
68
+ status: 'success',
69
+ toolUseId: p.tool_use_id
70
+ });
46
71
  else if (p.type === 'image' && p.source)
47
- imgs.push({ format: p.source.media_type?.split('/')[1] || 'png', source: { bytes: p.source.data } });
72
+ imgs.push({
73
+ format: p.source.media_type?.split('/')[1] || 'png',
74
+ source: { bytes: p.source.data }
75
+ });
48
76
  }
49
77
  }
50
78
  else
@@ -54,10 +82,41 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
54
82
  if (trs.length)
55
83
  uim.userInputMessageContext = { toolResults: deduplicateToolResults(trs) };
56
84
  const prev = history[history.length - 1];
57
- if (prev && prev.userInputMessage)
85
+ if (prev && prev.userInputMessage) {
58
86
  history.push({ assistantResponseMessage: { content: 'Continue' } });
87
+ }
59
88
  history.push({ userInputMessage: uim });
60
89
  }
90
+ else if (m.role === 'tool') {
91
+ const trs = [];
92
+ if (m.tool_results) {
93
+ for (const tr of m.tool_results)
94
+ trs.push({
95
+ content: [{ text: getContentText(tr) }],
96
+ status: 'success',
97
+ toolUseId: tr.tool_call_id
98
+ });
99
+ }
100
+ else {
101
+ trs.push({
102
+ content: [{ text: getContentText(m) }],
103
+ status: 'success',
104
+ toolUseId: m.tool_call_id
105
+ });
106
+ }
107
+ const prev = history[history.length - 1];
108
+ if (prev && prev.userInputMessage) {
109
+ history.push({ assistantResponseMessage: { content: 'Continue' } });
110
+ }
111
+ history.push({
112
+ userInputMessage: {
113
+ content: 'Tool results provided.',
114
+ modelId: resolved,
115
+ origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR,
116
+ userInputMessageContext: { toolResults: deduplicateToolResults(trs) }
117
+ }
118
+ });
119
+ }
61
120
  else if (m.role === 'assistant') {
62
121
  const arm = { content: '' };
63
122
  const tus = [];
@@ -72,15 +131,26 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
72
131
  tus.push({ input: p.input, name: p.name, toolUseId: p.id });
73
132
  }
74
133
  }
75
- else
134
+ else {
76
135
  arm.content = getContentText(m);
136
+ }
137
+ if (m.tool_calls && Array.isArray(m.tool_calls)) {
138
+ for (const tc of m.tool_calls) {
139
+ tus.push({
140
+ input: typeof tc.function?.arguments === 'string'
141
+ ? JSON.parse(tc.function.arguments)
142
+ : tc.function?.arguments,
143
+ name: tc.function?.name,
144
+ toolUseId: tc.id
145
+ });
146
+ }
147
+ }
77
148
  if (th)
78
- arm.content = arm.content ? `<thinking>${th}</thinking>\n\n${arm.content}` : `<thinking>${th}</thinking>`;
149
+ arm.content = arm.content
150
+ ? `<thinking>${th}</thinking>\n\n${arm.content}`
151
+ : `<thinking>${th}</thinking>`;
79
152
  if (tus.length)
80
153
  arm.toolUses = tus;
81
- const prev = history[history.length - 1];
82
- if (prev && prev.assistantResponseMessage)
83
- history.push({ userInputMessage: { content: 'Continue', modelId: resolved, origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR } });
84
154
  history.push({ assistantResponseMessage: arm });
85
155
  }
86
156
  }
@@ -107,8 +177,23 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
107
177
  }
108
178
  else
109
179
  arm.content = getContentText(curMsg);
180
+ if (curMsg.tool_calls && Array.isArray(curMsg.tool_calls)) {
181
+ if (!arm.toolUses)
182
+ arm.toolUses = [];
183
+ for (const tc of curMsg.tool_calls) {
184
+ arm.toolUses.push({
185
+ input: typeof tc.function?.arguments === 'string'
186
+ ? JSON.parse(tc.function.arguments)
187
+ : tc.function?.arguments,
188
+ name: tc.function?.name,
189
+ toolUseId: tc.id
190
+ });
191
+ }
192
+ }
110
193
  if (th)
111
- arm.content = arm.content ? `<thinking>${th}</thinking>\n\n${arm.content}` : `<thinking>${th}</thinking>`;
194
+ arm.content = arm.content
195
+ ? `<thinking>${th}</thinking>\n\n${arm.content}`
196
+ : `<thinking>${th}</thinking>`;
112
197
  history.push({ assistantResponseMessage: arm });
113
198
  curContent = 'Continue';
114
199
  }
@@ -116,14 +201,38 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
116
201
  const prev = history[history.length - 1];
117
202
  if (prev && !prev.assistantResponseMessage)
118
203
  history.push({ assistantResponseMessage: { content: 'Continue' } });
119
- if (Array.isArray(curMsg.content)) {
204
+ if (curMsg.role === 'tool') {
205
+ if (curMsg.tool_results) {
206
+ for (const tr of curMsg.tool_results)
207
+ curTrs.push({
208
+ content: [{ text: getContentText(tr) }],
209
+ status: 'success',
210
+ toolUseId: tr.tool_call_id
211
+ });
212
+ }
213
+ else {
214
+ curTrs.push({
215
+ content: [{ text: getContentText(curMsg) }],
216
+ status: 'success',
217
+ toolUseId: curMsg.tool_call_id
218
+ });
219
+ }
220
+ }
221
+ else if (Array.isArray(curMsg.content)) {
120
222
  for (const p of curMsg.content) {
121
223
  if (p.type === 'text')
122
224
  curContent += p.text || '';
123
225
  else if (p.type === 'tool_result')
124
- curTrs.push({ content: [{ text: getContentText(p) }], status: 'success', toolUseId: p.tool_use_id });
226
+ curTrs.push({
227
+ content: [{ text: getContentText(p.content || p) }],
228
+ status: 'success',
229
+ toolUseId: p.tool_use_id
230
+ });
125
231
  else if (p.type === 'image' && p.source)
126
- curImgs.push({ format: p.source.media_type?.split('/')[1] || 'png', source: { bytes: p.source.data } });
232
+ curImgs.push({
233
+ format: p.source.media_type?.split('/')[1] || 'png',
234
+ source: { bytes: p.source.data }
235
+ });
127
236
  }
128
237
  }
129
238
  else
@@ -135,10 +244,19 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
135
244
  conversationState: {
136
245
  chatTriggerType: KIRO_CONSTANTS.CHAT_TRIGGER_TYPE_MANUAL,
137
246
  conversationId: convId,
138
- history,
139
- currentMessage: { userInputMessage: { content: curContent, modelId: resolved, origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR } }
247
+ currentMessage: {
248
+ userInputMessage: {
249
+ content: curContent,
250
+ modelId: resolved,
251
+ origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR
252
+ }
253
+ }
140
254
  }
141
255
  };
256
+ if (history.length > 0) {
257
+ ;
258
+ request.conversationState.history = history;
259
+ }
142
260
  const uim = request.conversationState.currentMessage.userInputMessage;
143
261
  if (uim) {
144
262
  if (curImgs.length)
@@ -196,6 +314,16 @@ export function mergeAdjacentMessages(msgs) {
196
314
  last.content.push({ type: 'text', text: m.content });
197
315
  else if (typeof last.content === 'string' && Array.isArray(m.content))
198
316
  last.content = [{ type: 'text', text: last.content }, ...m.content];
317
+ if (m.tool_calls) {
318
+ if (!last.tool_calls)
319
+ last.tool_calls = [];
320
+ last.tool_calls.push(...m.tool_calls);
321
+ }
322
+ if (m.role === 'tool') {
323
+ if (!last.tool_results)
324
+ last.tool_results = [{ content: last.content, tool_call_id: last.tool_call_id }];
325
+ last.tool_results.push({ content: m.content, tool_call_id: m.tool_call_id });
326
+ }
199
327
  }
200
328
  else
201
329
  merged.push({ ...m });
@@ -76,7 +76,7 @@ function parseEventStreamChunk(rawText) {
76
76
  };
77
77
  });
78
78
  if (contextUsagePercentage !== undefined) {
79
- const totalTokens = Math.round((200000 * contextUsagePercentage) / 100);
79
+ const totalTokens = Math.round((172500 * contextUsagePercentage) / 100);
80
80
  outputTokens = estimateTokens(content);
81
81
  inputTokens = Math.max(0, totalTokens - outputTokens);
82
82
  }
@@ -99,7 +99,14 @@ function parseAwsEventStreamBuffer(buffer) {
99
99
  const inputStart = remaining.indexOf('{"input":', searchStart);
100
100
  const stopStart = remaining.indexOf('{"stop":', searchStart);
101
101
  const contextUsageStart = remaining.indexOf('{"contextUsagePercentage":', searchStart);
102
- const candidates = [contentStart, nameStart, followupStart, inputStart, stopStart, contextUsageStart].filter((pos) => pos >= 0);
102
+ const candidates = [
103
+ contentStart,
104
+ nameStart,
105
+ followupStart,
106
+ inputStart,
107
+ stopStart,
108
+ contextUsageStart
109
+ ].filter((pos) => pos >= 0);
103
110
  if (candidates.length === 0)
104
111
  break;
105
112
  const jsonStart = Math.min(...candidates);
@@ -25,16 +25,29 @@ export function startIDCAuthServer(authData, port = 19847) {
25
25
  client_id: authData.clientId,
26
26
  client_secret: authData.clientSecret
27
27
  });
28
- const res = await fetch(`https://oidc.${authData.region}.amazonaws.com/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: body.toString() });
28
+ const res = await fetch(`https://oidc.${authData.region}.amazonaws.com/token`, {
29
+ method: 'POST',
30
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
31
+ body: body.toString()
32
+ });
29
33
  const d = await res.json();
30
34
  if (res.ok) {
31
35
  const acc = d.access_token, ref = d.refresh_token, exp = Date.now() + d.expires_in * 1000;
32
- const infoRes = await fetch('https://view.awsapps.com/api/user/info', { headers: { Authorization: `Bearer ${acc}` } });
36
+ const infoRes = await fetch('https://view.awsapps.com/api/user/info', {
37
+ headers: { Authorization: `Bearer ${acc}` }
38
+ });
33
39
  const info = await infoRes.json();
34
40
  const email = info.email || info.userName || 'builder-id@aws.amazon.com';
35
41
  status.status = 'success';
36
42
  if (resolver)
37
- resolver({ email, accessToken: acc, refreshToken: ref, expiresAt: exp, clientId: authData.clientId, clientSecret: authData.clientSecret });
43
+ resolver({
44
+ email,
45
+ accessToken: acc,
46
+ refreshToken: ref,
47
+ expiresAt: exp,
48
+ clientId: authData.clientId,
49
+ clientSecret: authData.clientSecret
50
+ });
38
51
  setTimeout(cleanup, 2000);
39
52
  }
40
53
  else if (d.error === 'authorization_pending')
@@ -224,7 +224,7 @@ export async function* transformKiroStream(response, model, conversationId) {
224
224
  }
225
225
  outputTokens = estimateTokens(totalContent);
226
226
  if (contextUsagePercentage !== null && contextUsagePercentage > 0) {
227
- const totalTokens = Math.round((200000 * contextUsagePercentage) / 100);
227
+ const totalTokens = Math.round((172500 * contextUsagePercentage) / 100);
228
228
  inputTokens = Math.max(0, totalTokens - outputTokens);
229
229
  }
230
230
  yield convertToOpenAI({
@@ -322,7 +322,14 @@ function parseStreamBuffer(buffer) {
322
322
  const inputStart = remaining.indexOf('{"input":', searchStart);
323
323
  const stopStart = remaining.indexOf('{"stop":', searchStart);
324
324
  const contextUsageStart = remaining.indexOf('{"contextUsagePercentage":', searchStart);
325
- const candidates = [contentStart, nameStart, followupStart, inputStart, stopStart, contextUsageStart].filter((pos) => pos >= 0);
325
+ const candidates = [
326
+ contentStart,
327
+ nameStart,
328
+ followupStart,
329
+ inputStart,
330
+ stopStart,
331
+ contextUsageStart
332
+ ].filter((pos) => pos >= 0);
326
333
  if (candidates.length === 0)
327
334
  break;
328
335
  const jsonStart = Math.min(...candidates);
@@ -113,7 +113,7 @@ export interface CodeWhispererRequest {
113
113
  conversationState: {
114
114
  chatTriggerType: string;
115
115
  conversationId: string;
116
- history: CodeWhispererMessage[];
116
+ history?: CodeWhispererMessage[];
117
117
  currentMessage: CodeWhispererMessage;
118
118
  };
119
119
  profileArn?: string;
@@ -3,7 +3,12 @@ export async function fetchUsageLimits(auth) {
3
3
  try {
4
4
  const res = await fetch(url, {
5
5
  method: 'GET',
6
- headers: { Authorization: `Bearer ${auth.access}`, 'Content-Type': 'application/json', 'x-amzn-kiro-agent-mode': 'vibe', 'amz-sdk-request': 'attempt=1; max=1' }
6
+ headers: {
7
+ Authorization: `Bearer ${auth.access}`,
8
+ 'Content-Type': 'application/json',
9
+ 'x-amzn-kiro-agent-mode': 'vibe',
10
+ 'amz-sdk-request': 'attempt=1; max=1'
11
+ }
7
12
  });
8
13
  if (!res.ok)
9
14
  throw new Error(`Status: ${res.status}`);
@@ -26,7 +31,11 @@ export async function fetchUsageLimits(auth) {
26
31
  }
27
32
  }
28
33
  export function updateAccountQuota(account, usage, accountManager) {
29
- const meta = { usedCount: usage.usedCount || 0, limitCount: usage.limitCount || 0, realEmail: usage.email };
34
+ const meta = {
35
+ usedCount: usage.usedCount || 0,
36
+ limitCount: usage.limitCount || 0,
37
+ realEmail: usage.email
38
+ };
30
39
  account.usedCount = meta.usedCount;
31
40
  account.limitCount = meta.limitCount;
32
41
  if (meta.realEmail)