@zhafron/opencode-kiro-auth 1.2.6 → 1.2.8

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.
@@ -2,10 +2,53 @@ import * as crypto from 'crypto';
2
2
  import * as os from 'os';
3
3
  import { KIRO_CONSTANTS } from '../constants.js';
4
4
  import { resolveKiroModel } from './models.js';
5
- import * as logger from './logger.js';
5
+ function truncate(s, max) {
6
+ if (s.length <= max)
7
+ return s;
8
+ const half = Math.floor(max / 2);
9
+ return s.substring(0, half) + '\n... [TRUNCATED] ...\n' + s.substring(s.length - half);
10
+ }
11
+ function sanitizeHistory(history) {
12
+ const result = [];
13
+ for (let i = 0; i < history.length; i++) {
14
+ const m = history[i];
15
+ if (!m)
16
+ continue;
17
+ if (m.assistantResponseMessage?.toolUses) {
18
+ const next = history[i + 1];
19
+ if (next?.userInputMessage?.userInputMessageContext?.toolResults)
20
+ result.push(m);
21
+ }
22
+ else if (m.userInputMessage?.userInputMessageContext?.toolResults) {
23
+ const prev = result[result.length - 1];
24
+ if (prev?.assistantResponseMessage?.toolUses)
25
+ result.push(m);
26
+ }
27
+ else
28
+ result.push(m);
29
+ }
30
+ return result;
31
+ }
32
+ function findOriginalToolCall(msgs, toolUseId) {
33
+ for (const m of msgs) {
34
+ if (m.role === 'assistant') {
35
+ if (m.tool_calls) {
36
+ for (const tc of m.tool_calls)
37
+ if (tc.id === toolUseId)
38
+ return tc;
39
+ }
40
+ if (Array.isArray(m.content)) {
41
+ for (const p of m.content)
42
+ if (p.type === 'tool_use' && p.id === toolUseId)
43
+ return p;
44
+ }
45
+ }
46
+ }
47
+ return null;
48
+ }
6
49
  export function transformToCodeWhisperer(url, body, model, auth, think = false, budget = 20000) {
7
50
  const req = typeof body === 'string' ? JSON.parse(body) : body;
8
- const { messages, tools, system, conversationId } = req;
51
+ const { messages, tools, system } = req;
9
52
  const convId = crypto.randomUUID();
10
53
  if (!messages || messages.length === 0)
11
54
  throw new Error('No messages');
@@ -20,7 +63,7 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
20
63
  if (lastMsg && lastMsg.role === 'assistant' && getContentText(lastMsg) === '{')
21
64
  msgs.pop();
22
65
  const cwTools = tools ? convertToolsToCodeWhisperer(tools) : [];
23
- const history = [];
66
+ let history = [];
24
67
  let firstUserIndex = -1;
25
68
  for (let i = 0; i < msgs.length; i++) {
26
69
  if (msgs[i].role === 'user') {
@@ -38,9 +81,8 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
38
81
  ...m.content.filter((p) => p.type !== 'text')
39
82
  ];
40
83
  }
41
- else {
84
+ else
42
85
  m.content = `${sys}\n\n${oldContent}`;
43
- }
44
86
  }
45
87
  else {
46
88
  history.push({
@@ -65,7 +107,7 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
65
107
  uim.content += p.text || '';
66
108
  else if (p.type === 'tool_result')
67
109
  trs.push({
68
- content: [{ text: getContentText(p.content || p) }],
110
+ content: [{ text: truncate(getContentText(p.content || p), 250000) }],
69
111
  status: 'success',
70
112
  toolUseId: p.tool_use_id
71
113
  });
@@ -83,9 +125,8 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
83
125
  if (trs.length)
84
126
  uim.userInputMessageContext = { toolResults: deduplicateToolResults(trs) };
85
127
  const prev = history[history.length - 1];
86
- if (prev && prev.userInputMessage) {
128
+ if (prev && prev.userInputMessage)
87
129
  history.push({ assistantResponseMessage: { content: 'Continue' } });
88
- }
89
130
  history.push({ userInputMessage: uim });
90
131
  }
91
132
  else if (m.role === 'tool') {
@@ -93,22 +134,21 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
93
134
  if (m.tool_results) {
94
135
  for (const tr of m.tool_results)
95
136
  trs.push({
96
- content: [{ text: getContentText(tr) }],
137
+ content: [{ text: truncate(getContentText(tr), 250000) }],
97
138
  status: 'success',
98
139
  toolUseId: tr.tool_call_id
99
140
  });
100
141
  }
101
142
  else {
102
143
  trs.push({
103
- content: [{ text: getContentText(m) }],
144
+ content: [{ text: truncate(getContentText(m), 250000) }],
104
145
  status: 'success',
105
146
  toolUseId: m.tool_call_id
106
147
  });
107
148
  }
108
149
  const prev = history[history.length - 1];
109
- if (prev && prev.userInputMessage) {
150
+ if (prev && prev.userInputMessage)
110
151
  history.push({ assistantResponseMessage: { content: 'Continue' } });
111
- }
112
152
  history.push({
113
153
  userInputMessage: {
114
154
  content: 'Tool results provided.',
@@ -132,9 +172,8 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
132
172
  tus.push({ input: p.input, name: p.name, toolUseId: p.id });
133
173
  }
134
174
  }
135
- else {
175
+ else
136
176
  arm.content = getContentText(m);
137
- }
138
177
  if (m.tool_calls && Array.isArray(m.tool_calls)) {
139
178
  for (const tc of m.tool_calls) {
140
179
  tus.push({
@@ -155,6 +194,19 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
155
194
  history.push({ assistantResponseMessage: arm });
156
195
  }
157
196
  }
197
+ history = sanitizeHistory(history);
198
+ let historySize = JSON.stringify(history).length;
199
+ while (historySize > 850000 && history.length > 2) {
200
+ history.shift();
201
+ while (history.length > 0) {
202
+ const first = history[0];
203
+ if (first && first.userInputMessage)
204
+ break;
205
+ history.shift();
206
+ }
207
+ history = sanitizeHistory(history);
208
+ historySize = JSON.stringify(history).length;
209
+ }
158
210
  const curMsg = msgs[msgs.length - 1];
159
211
  if (!curMsg)
160
212
  throw new Error('Empty');
@@ -206,14 +258,14 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
206
258
  if (curMsg.tool_results) {
207
259
  for (const tr of curMsg.tool_results)
208
260
  curTrs.push({
209
- content: [{ text: getContentText(tr) }],
261
+ content: [{ text: truncate(getContentText(tr), 250000) }],
210
262
  status: 'success',
211
263
  toolUseId: tr.tool_call_id
212
264
  });
213
265
  }
214
266
  else {
215
267
  curTrs.push({
216
- content: [{ text: getContentText(curMsg) }],
268
+ content: [{ text: truncate(getContentText(curMsg), 250000) }],
217
269
  status: 'success',
218
270
  toolUseId: curMsg.tool_call_id
219
271
  });
@@ -225,7 +277,7 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
225
277
  curContent += p.text || '';
226
278
  else if (p.type === 'tool_result')
227
279
  curTrs.push({
228
- content: [{ text: getContentText(p.content || p) }],
280
+ content: [{ text: truncate(getContentText(p.content || p), 250000) }],
229
281
  status: 'success',
230
282
  toolUseId: p.tool_use_id
231
283
  });
@@ -254,17 +306,60 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
254
306
  }
255
307
  }
256
308
  };
257
- if (history.length > 0) {
258
- ;
259
- request.conversationState.history = history;
309
+ const toolUsesInHistory = history.flatMap((h) => h.assistantResponseMessage?.toolUses || []);
310
+ const allToolUseIdsInHistory = new Set(toolUsesInHistory.map((tu) => tu.toolUseId));
311
+ const finalCurTrs = [];
312
+ const orphanedTrs = [];
313
+ for (const tr of curTrs) {
314
+ if (allToolUseIdsInHistory.has(tr.toolUseId))
315
+ finalCurTrs.push(tr);
316
+ else {
317
+ const originalCall = findOriginalToolCall(messages, tr.toolUseId);
318
+ if (originalCall) {
319
+ orphanedTrs.push({
320
+ call: {
321
+ name: originalCall.name || originalCall.function?.name || 'tool',
322
+ toolUseId: tr.toolUseId,
323
+ input: originalCall.input ||
324
+ (originalCall.function?.arguments ? JSON.parse(originalCall.function.arguments) : {})
325
+ },
326
+ result: tr
327
+ });
328
+ }
329
+ else {
330
+ curContent += `\n\n[Output for tool call ${tr.toolUseId}]:\n${tr.content?.[0]?.text || ''}`;
331
+ }
332
+ }
260
333
  }
334
+ if (orphanedTrs.length > 0) {
335
+ const prev = history[history.length - 1];
336
+ if (prev && !prev.userInputMessage) {
337
+ history.push({
338
+ userInputMessage: {
339
+ content: 'Running tools...',
340
+ modelId: resolved,
341
+ origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR
342
+ }
343
+ });
344
+ }
345
+ history.push({
346
+ assistantResponseMessage: {
347
+ content: 'I will execute the following tools.',
348
+ toolUses: orphanedTrs.map((o) => o.call)
349
+ }
350
+ });
351
+ finalCurTrs.push(...orphanedTrs.map((o) => o.result));
352
+ }
353
+ if (history.length > 0)
354
+ request.conversationState.history = history;
261
355
  const uim = request.conversationState.currentMessage.userInputMessage;
262
356
  if (uim) {
357
+ uim.content = curContent;
263
358
  if (curImgs.length)
264
359
  uim.images = curImgs;
265
360
  const ctx = {};
266
- if (curTrs.length)
267
- ctx.toolResults = deduplicateToolResults(curTrs);
361
+ if (finalCurTrs.length)
362
+ ctx.toolResults = deduplicateToolResults(finalCurTrs);
268
363
  if (cwTools.length)
269
364
  ctx.tools = cwTools;
270
365
  if (Object.keys(ctx).length)
@@ -277,17 +372,11 @@ export function transformToCodeWhisperer(url, body, model, auth, think = false,
277
372
  const existingToolNames = new Set(existingTools.map((t) => t.toolSpecification?.name).filter(Boolean));
278
373
  const missingToolNames = Array.from(toolNamesInHistory).filter((name) => !existingToolNames.has(name));
279
374
  if (missingToolNames.length > 0) {
280
- logger.log(`[Kiro] Adding ${missingToolNames.length} missing tool definitions: ${missingToolNames.join(', ')}`);
281
375
  const placeholderTools = missingToolNames.map((name) => ({
282
376
  toolSpecification: {
283
377
  name,
284
378
  description: 'Tool',
285
- inputSchema: {
286
- json: {
287
- type: 'object',
288
- properties: {}
289
- }
290
- }
379
+ inputSchema: { json: { type: 'object', properties: {} } }
291
380
  }
292
381
  }));
293
382
  if (!uim.userInputMessageContext)
@@ -76,7 +76,7 @@ function parseEventStreamChunk(rawText) {
76
76
  };
77
77
  });
78
78
  if (contextUsagePercentage !== undefined) {
79
- const totalTokens = Math.round((172500 * contextUsagePercentage) / 100);
79
+ const totalTokens = Math.round((200000 * contextUsagePercentage) / 100);
80
80
  outputTokens = estimateTokens(content);
81
81
  inputTokens = Math.max(0, totalTokens - outputTokens);
82
82
  }
@@ -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((172500 * contextUsagePercentage) / 100);
227
+ const totalTokens = Math.round((200000 * contextUsagePercentage) / 100);
228
228
  inputTokens = Math.max(0, totalTokens - outputTokens);
229
229
  }
230
230
  yield convertToOpenAI({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhafron/opencode-kiro-auth",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "description": "OpenCode plugin for AWS Kiro (CodeWhisperer) providing access to Claude models",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",