@zhafron/opencode-kiro-auth 1.0.0
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.
- package/README.md +85 -0
- package/dist/constants.d.ts +26 -0
- package/dist/constants.js +60 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/kiro/auth.d.ts +5 -0
- package/dist/kiro/auth.js +24 -0
- package/dist/kiro/oauth-idc.d.ts +24 -0
- package/dist/kiro/oauth-idc.js +132 -0
- package/dist/kiro/oauth-social.d.ts +17 -0
- package/dist/kiro/oauth-social.js +51 -0
- package/dist/plugin/accounts.d.ts +28 -0
- package/dist/plugin/accounts.js +156 -0
- package/dist/plugin/auth-page.d.ts +3 -0
- package/dist/plugin/auth-page.js +568 -0
- package/dist/plugin/cli.d.ts +6 -0
- package/dist/plugin/cli.js +98 -0
- package/dist/plugin/config/index.d.ts +3 -0
- package/dist/plugin/config/index.js +2 -0
- package/dist/plugin/config/loader.d.ts +6 -0
- package/dist/plugin/config/loader.js +125 -0
- package/dist/plugin/config/schema.d.ts +44 -0
- package/dist/plugin/config/schema.js +28 -0
- package/dist/plugin/debug.d.ts +2 -0
- package/dist/plugin/debug.js +9 -0
- package/dist/plugin/errors.d.ts +17 -0
- package/dist/plugin/errors.js +34 -0
- package/dist/plugin/logger.d.ts +4 -0
- package/dist/plugin/logger.js +37 -0
- package/dist/plugin/models.d.ts +3 -0
- package/dist/plugin/models.js +14 -0
- package/dist/plugin/oauth-parser.d.ts +5 -0
- package/dist/plugin/oauth-parser.js +23 -0
- package/dist/plugin/quota.d.ts +15 -0
- package/dist/plugin/quota.js +68 -0
- package/dist/plugin/recovery.d.ts +19 -0
- package/dist/plugin/recovery.js +302 -0
- package/dist/plugin/refresh-queue.d.ts +14 -0
- package/dist/plugin/refresh-queue.js +69 -0
- package/dist/plugin/request.d.ts +4 -0
- package/dist/plugin/request.js +240 -0
- package/dist/plugin/response.d.ts +6 -0
- package/dist/plugin/response.js +246 -0
- package/dist/plugin/server.d.ts +24 -0
- package/dist/plugin/server.js +96 -0
- package/dist/plugin/storage.d.ts +7 -0
- package/dist/plugin/storage.js +75 -0
- package/dist/plugin/streaming.d.ts +3 -0
- package/dist/plugin/streaming.js +503 -0
- package/dist/plugin/token.d.ts +2 -0
- package/dist/plugin/token.js +56 -0
- package/dist/plugin/types.d.ts +148 -0
- package/dist/plugin/types.js +0 -0
- package/dist/plugin/usage.d.ts +3 -0
- package/dist/plugin/usage.js +36 -0
- package/dist/plugin.d.ts +32 -0
- package/dist/plugin.js +222 -0
- package/dist/src/constants.d.ts +22 -0
- package/dist/src/constants.js +35 -0
- package/dist/src/kiro/auth.d.ts +5 -0
- package/dist/src/kiro/auth.js +69 -0
- package/dist/src/kiro/oauth-idc.d.ts +22 -0
- package/dist/src/kiro/oauth-idc.js +99 -0
- package/dist/src/kiro/oauth-social.d.ts +17 -0
- package/dist/src/kiro/oauth-social.js +69 -0
- package/dist/src/plugin/accounts.d.ts +23 -0
- package/dist/src/plugin/accounts.js +265 -0
- package/dist/src/plugin/cli.d.ts +6 -0
- package/dist/src/plugin/cli.js +98 -0
- package/dist/src/plugin/config/index.d.ts +3 -0
- package/dist/src/plugin/config/index.js +2 -0
- package/dist/src/plugin/config/loader.d.ts +7 -0
- package/dist/src/plugin/config/loader.js +143 -0
- package/dist/src/plugin/config/schema.d.ts +68 -0
- package/dist/src/plugin/config/schema.js +44 -0
- package/dist/src/plugin/debug.d.ts +2 -0
- package/dist/src/plugin/debug.js +9 -0
- package/dist/src/plugin/errors.d.ts +17 -0
- package/dist/src/plugin/errors.js +34 -0
- package/dist/src/plugin/logger.d.ts +4 -0
- package/dist/src/plugin/logger.js +17 -0
- package/dist/src/plugin/models.d.ts +3 -0
- package/dist/src/plugin/models.js +14 -0
- package/dist/src/plugin/oauth-parser.d.ts +5 -0
- package/dist/src/plugin/oauth-parser.js +23 -0
- package/dist/src/plugin/quota.d.ts +25 -0
- package/dist/src/plugin/quota.js +175 -0
- package/dist/src/plugin/recovery.d.ts +19 -0
- package/dist/src/plugin/recovery.js +302 -0
- package/dist/src/plugin/refresh-queue.d.ts +14 -0
- package/dist/src/plugin/refresh-queue.js +69 -0
- package/dist/src/plugin/request.d.ts +35 -0
- package/dist/src/plugin/request.js +411 -0
- package/dist/src/plugin/response.d.ts +6 -0
- package/dist/src/plugin/response.js +246 -0
- package/dist/src/plugin/server.d.ts +10 -0
- package/dist/src/plugin/server.js +203 -0
- package/dist/src/plugin/storage.d.ts +5 -0
- package/dist/src/plugin/storage.js +106 -0
- package/dist/src/plugin/streaming.d.ts +12 -0
- package/dist/src/plugin/streaming.js +444 -0
- package/dist/src/plugin/token.d.ts +8 -0
- package/dist/src/plugin/token.js +130 -0
- package/dist/src/plugin/types.d.ts +144 -0
- package/dist/src/plugin/types.js +0 -0
- package/dist/src/plugin/usage.d.ts +28 -0
- package/dist/src/plugin/usage.js +159 -0
- package/dist/src/plugin.d.ts +2 -0
- package/dist/src/plugin.js +341 -0
- package/package.json +57 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
import { KIRO_CONSTANTS } from '../constants';
|
|
4
|
+
import { resolveKiroModel } from './models';
|
|
5
|
+
export function transformToCodeWhisperer(url, body, model, auth, thinkingEnabled = false, thinkingBudget = 20000) {
|
|
6
|
+
const openAIRequest = JSON.parse(body);
|
|
7
|
+
const { messages, tools, system: systemPrompt } = openAIRequest;
|
|
8
|
+
if (!messages || messages.length === 0) {
|
|
9
|
+
throw new Error('No messages found in request');
|
|
10
|
+
}
|
|
11
|
+
const resolvedModel = resolveKiroModel(model);
|
|
12
|
+
const conversationId = crypto.randomUUID();
|
|
13
|
+
let processedSystemPrompt = systemPrompt || '';
|
|
14
|
+
if (thinkingEnabled) {
|
|
15
|
+
const thinkingPrefix = `<thinking_mode>enabled</thinking_mode><max_thinking_length>${thinkingBudget}</max_thinking_length>`;
|
|
16
|
+
if (!processedSystemPrompt) {
|
|
17
|
+
processedSystemPrompt = thinkingPrefix;
|
|
18
|
+
}
|
|
19
|
+
else if (!processedSystemPrompt.includes('<thinking_mode>')) {
|
|
20
|
+
processedSystemPrompt = `${thinkingPrefix}\n${processedSystemPrompt}`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
let processedMessages = [...messages];
|
|
24
|
+
const lastMessage = processedMessages[processedMessages.length - 1];
|
|
25
|
+
if (lastMessage?.role === 'assistant') {
|
|
26
|
+
const content = getContentText(lastMessage);
|
|
27
|
+
if (content === '{') {
|
|
28
|
+
processedMessages.pop();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
processedMessages = mergeAdjacentMessages(processedMessages);
|
|
32
|
+
const codewhispererTools = tools ? convertToolsToCodeWhisperer(tools) : [];
|
|
33
|
+
const history = [];
|
|
34
|
+
let startIndex = 0;
|
|
35
|
+
if (processedSystemPrompt) {
|
|
36
|
+
if (processedMessages[0]?.role === 'user') {
|
|
37
|
+
const firstUserContent = getContentText(processedMessages[0]);
|
|
38
|
+
history.push({
|
|
39
|
+
userInputMessage: {
|
|
40
|
+
content: `${processedSystemPrompt}\n\n${firstUserContent}`,
|
|
41
|
+
modelId: resolvedModel,
|
|
42
|
+
origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR,
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
startIndex = 1;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
history.push({
|
|
49
|
+
userInputMessage: {
|
|
50
|
+
content: processedSystemPrompt,
|
|
51
|
+
modelId: resolvedModel,
|
|
52
|
+
origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR,
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const keepImageThreshold = 5;
|
|
58
|
+
for (let i = startIndex; i < processedMessages.length - 1; i++) {
|
|
59
|
+
const message = processedMessages[i];
|
|
60
|
+
if (!message)
|
|
61
|
+
continue;
|
|
62
|
+
const distanceFromEnd = (processedMessages.length - 1) - i;
|
|
63
|
+
const shouldKeepImages = distanceFromEnd <= keepImageThreshold;
|
|
64
|
+
if (message.role === 'user') {
|
|
65
|
+
const userInputMessage = {
|
|
66
|
+
content: '',
|
|
67
|
+
modelId: resolvedModel,
|
|
68
|
+
origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR
|
|
69
|
+
};
|
|
70
|
+
let imageCount = 0;
|
|
71
|
+
const toolResults = [];
|
|
72
|
+
const images = [];
|
|
73
|
+
if (Array.isArray(message.content)) {
|
|
74
|
+
for (const part of message.content) {
|
|
75
|
+
if (part.type === 'text') {
|
|
76
|
+
userInputMessage.content += part.text || '';
|
|
77
|
+
}
|
|
78
|
+
else if (part.type === 'tool_result') {
|
|
79
|
+
toolResults.push({
|
|
80
|
+
content: [{ text: getContentText(part) }],
|
|
81
|
+
status: 'success',
|
|
82
|
+
toolUseId: part.tool_use_id
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
else if (part.type === 'image') {
|
|
86
|
+
if (shouldKeepImages && part.source) {
|
|
87
|
+
images.push({
|
|
88
|
+
format: part.source.media_type?.split('/')[1] || 'png',
|
|
89
|
+
source: {
|
|
90
|
+
bytes: part.source.data
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
imageCount++;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
userInputMessage.content = getContentText(message);
|
|
102
|
+
}
|
|
103
|
+
if (images.length > 0) {
|
|
104
|
+
userInputMessage.images = images;
|
|
105
|
+
}
|
|
106
|
+
if (imageCount > 0) {
|
|
107
|
+
const imagePlaceholder = `[此消息包含 ${imageCount} 张图片,已在历史记录中省略]`;
|
|
108
|
+
userInputMessage.content = userInputMessage.content
|
|
109
|
+
? `${userInputMessage.content}\n${imagePlaceholder}`
|
|
110
|
+
: imagePlaceholder;
|
|
111
|
+
}
|
|
112
|
+
if (toolResults.length > 0) {
|
|
113
|
+
const uniqueToolResults = deduplicateToolResults(toolResults);
|
|
114
|
+
userInputMessage.userInputMessageContext = { toolResults: uniqueToolResults };
|
|
115
|
+
}
|
|
116
|
+
history.push({ userInputMessage });
|
|
117
|
+
}
|
|
118
|
+
else if (message.role === 'assistant') {
|
|
119
|
+
const assistantResponseMessage = {
|
|
120
|
+
content: ''
|
|
121
|
+
};
|
|
122
|
+
const toolUses = [];
|
|
123
|
+
let thinkingText = '';
|
|
124
|
+
if (Array.isArray(message.content)) {
|
|
125
|
+
for (const part of message.content) {
|
|
126
|
+
if (part.type === 'text') {
|
|
127
|
+
assistantResponseMessage.content += part.text || '';
|
|
128
|
+
}
|
|
129
|
+
else if (part.type === 'thinking') {
|
|
130
|
+
thinkingText += (part.thinking || part.text || '');
|
|
131
|
+
}
|
|
132
|
+
else if (part.type === 'tool_use') {
|
|
133
|
+
toolUses.push({
|
|
134
|
+
input: part.input,
|
|
135
|
+
name: part.name,
|
|
136
|
+
toolUseId: part.id
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
assistantResponseMessage.content = getContentText(message);
|
|
143
|
+
}
|
|
144
|
+
if (thinkingText) {
|
|
145
|
+
assistantResponseMessage.content = assistantResponseMessage.content
|
|
146
|
+
? `<thinking>${thinkingText}</thinking>\n\n${assistantResponseMessage.content}`
|
|
147
|
+
: `<thinking>${thinkingText}</thinking>`;
|
|
148
|
+
}
|
|
149
|
+
if (toolUses.length > 0) {
|
|
150
|
+
assistantResponseMessage.toolUses = toolUses;
|
|
151
|
+
}
|
|
152
|
+
history.push({ assistantResponseMessage });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const currentMessage = processedMessages[processedMessages.length - 1];
|
|
156
|
+
if (!currentMessage) {
|
|
157
|
+
throw new Error('No current message found');
|
|
158
|
+
}
|
|
159
|
+
let currentContent = '';
|
|
160
|
+
const currentToolResults = [];
|
|
161
|
+
const currentImages = [];
|
|
162
|
+
if (currentMessage.role === 'assistant') {
|
|
163
|
+
const assistantResponseMessage = {
|
|
164
|
+
content: ''
|
|
165
|
+
};
|
|
166
|
+
const toolUses = [];
|
|
167
|
+
let thinkingText = '';
|
|
168
|
+
if (Array.isArray(currentMessage.content)) {
|
|
169
|
+
for (const part of currentMessage.content) {
|
|
170
|
+
if (part.type === 'text') {
|
|
171
|
+
assistantResponseMessage.content += part.text || '';
|
|
172
|
+
}
|
|
173
|
+
else if (part.type === 'thinking') {
|
|
174
|
+
thinkingText += (part.thinking || part.text || '');
|
|
175
|
+
}
|
|
176
|
+
else if (part.type === 'tool_use') {
|
|
177
|
+
toolUses.push({
|
|
178
|
+
input: part.input,
|
|
179
|
+
name: part.name,
|
|
180
|
+
toolUseId: part.id
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
assistantResponseMessage.content = getContentText(currentMessage);
|
|
187
|
+
}
|
|
188
|
+
if (thinkingText) {
|
|
189
|
+
assistantResponseMessage.content = assistantResponseMessage.content
|
|
190
|
+
? `<thinking>${thinkingText}</thinking>\n\n${assistantResponseMessage.content}`
|
|
191
|
+
: `<thinking>${thinkingText}</thinking>`;
|
|
192
|
+
}
|
|
193
|
+
if (toolUses.length > 0) {
|
|
194
|
+
assistantResponseMessage.toolUses = toolUses;
|
|
195
|
+
}
|
|
196
|
+
history.push({ assistantResponseMessage });
|
|
197
|
+
currentContent = 'Continue';
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
if (history.length > 0) {
|
|
201
|
+
const lastHistoryItem = history[history.length - 1];
|
|
202
|
+
if (lastHistoryItem && !lastHistoryItem.assistantResponseMessage) {
|
|
203
|
+
history.push({
|
|
204
|
+
assistantResponseMessage: {
|
|
205
|
+
content: 'Continue'
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (Array.isArray(currentMessage.content)) {
|
|
211
|
+
for (const part of currentMessage.content) {
|
|
212
|
+
if (part.type === 'text') {
|
|
213
|
+
currentContent += part.text || '';
|
|
214
|
+
}
|
|
215
|
+
else if (part.type === 'tool_result') {
|
|
216
|
+
currentToolResults.push({
|
|
217
|
+
content: [{ text: getContentText(part) }],
|
|
218
|
+
status: 'success',
|
|
219
|
+
toolUseId: part.tool_use_id
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
else if (part.type === 'image') {
|
|
223
|
+
if (part.source) {
|
|
224
|
+
currentImages.push({
|
|
225
|
+
format: part.source.media_type?.split('/')[1] || 'png',
|
|
226
|
+
source: {
|
|
227
|
+
bytes: part.source.data
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
currentContent = getContentText(currentMessage);
|
|
236
|
+
}
|
|
237
|
+
if (!currentContent) {
|
|
238
|
+
currentContent = currentToolResults.length > 0 ? 'Tool results provided.' : 'Continue';
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const request = {
|
|
242
|
+
conversationState: {
|
|
243
|
+
chatTriggerType: KIRO_CONSTANTS.CHAT_TRIGGER_TYPE_MANUAL,
|
|
244
|
+
conversationId: conversationId,
|
|
245
|
+
history: history.length > 0 ? history : [],
|
|
246
|
+
currentMessage: {
|
|
247
|
+
userInputMessage: {
|
|
248
|
+
content: currentContent,
|
|
249
|
+
modelId: resolvedModel,
|
|
250
|
+
origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
if (history.length === 0) {
|
|
256
|
+
delete request.conversationState.history;
|
|
257
|
+
}
|
|
258
|
+
const userInputMessage = request.conversationState.currentMessage.userInputMessage;
|
|
259
|
+
if (currentImages.length > 0) {
|
|
260
|
+
userInputMessage.images = currentImages;
|
|
261
|
+
}
|
|
262
|
+
const userInputMessageContext = {};
|
|
263
|
+
if (currentToolResults.length > 0) {
|
|
264
|
+
const uniqueToolResults = deduplicateToolResults(currentToolResults);
|
|
265
|
+
userInputMessageContext.toolResults = uniqueToolResults;
|
|
266
|
+
}
|
|
267
|
+
if (codewhispererTools.length > 0) {
|
|
268
|
+
userInputMessageContext.tools = codewhispererTools;
|
|
269
|
+
}
|
|
270
|
+
if (Object.keys(userInputMessageContext).length > 0) {
|
|
271
|
+
userInputMessage.userInputMessageContext = userInputMessageContext;
|
|
272
|
+
}
|
|
273
|
+
if (auth.authMethod === 'social' && auth.profileArn) {
|
|
274
|
+
request.profileArn = auth.profileArn;
|
|
275
|
+
}
|
|
276
|
+
const finalUrl = KIRO_CONSTANTS.BASE_URL.replace('{{region}}', auth.region);
|
|
277
|
+
const machineId = generateMachineId(auth);
|
|
278
|
+
const userAgent = buildUserAgent(machineId);
|
|
279
|
+
return {
|
|
280
|
+
url: finalUrl,
|
|
281
|
+
init: {
|
|
282
|
+
method: 'POST',
|
|
283
|
+
headers: {
|
|
284
|
+
'Content-Type': 'application/json',
|
|
285
|
+
'Authorization': `Bearer ${auth.access}`,
|
|
286
|
+
'x-amzn-kiro-agent-mode': 'vibe',
|
|
287
|
+
'x-amz-user-agent': `aws-sdk-js/1.0.0 ${userAgent}`,
|
|
288
|
+
'user-agent': userAgent
|
|
289
|
+
},
|
|
290
|
+
body: JSON.stringify(request)
|
|
291
|
+
},
|
|
292
|
+
streaming: true,
|
|
293
|
+
effectiveModel: resolvedModel,
|
|
294
|
+
conversationId: conversationId
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
export function mergeAdjacentMessages(messages) {
|
|
298
|
+
const merged = [];
|
|
299
|
+
for (const currentMsg of messages) {
|
|
300
|
+
if (merged.length === 0) {
|
|
301
|
+
merged.push({ ...currentMsg });
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
const lastMsg = merged[merged.length - 1];
|
|
305
|
+
if (!lastMsg)
|
|
306
|
+
continue;
|
|
307
|
+
if (currentMsg.role === lastMsg.role) {
|
|
308
|
+
if (Array.isArray(lastMsg.content) && Array.isArray(currentMsg.content)) {
|
|
309
|
+
lastMsg.content.push(...currentMsg.content);
|
|
310
|
+
}
|
|
311
|
+
else if (typeof lastMsg.content === 'string' && typeof currentMsg.content === 'string') {
|
|
312
|
+
lastMsg.content += '\n' + currentMsg.content;
|
|
313
|
+
}
|
|
314
|
+
else if (Array.isArray(lastMsg.content) && typeof currentMsg.content === 'string') {
|
|
315
|
+
lastMsg.content.push({ type: 'text', text: currentMsg.content });
|
|
316
|
+
}
|
|
317
|
+
else if (typeof lastMsg.content === 'string' && Array.isArray(currentMsg.content)) {
|
|
318
|
+
lastMsg.content = [{ type: 'text', text: lastMsg.content }, ...currentMsg.content];
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
merged.push({ ...currentMsg });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return merged;
|
|
327
|
+
}
|
|
328
|
+
export function convertToolsToCodeWhisperer(tools) {
|
|
329
|
+
const MAX_DESCRIPTION_LENGTH = 9216;
|
|
330
|
+
const filteredTools = tools.filter(tool => {
|
|
331
|
+
const name = (tool.name || '').toLowerCase();
|
|
332
|
+
return name !== 'web_search' && name !== 'websearch';
|
|
333
|
+
});
|
|
334
|
+
return filteredTools.map(tool => {
|
|
335
|
+
let desc = tool.description || '';
|
|
336
|
+
if (desc.length > MAX_DESCRIPTION_LENGTH) {
|
|
337
|
+
desc = desc.substring(0, MAX_DESCRIPTION_LENGTH) + '...';
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
toolSpecification: {
|
|
341
|
+
name: tool.name,
|
|
342
|
+
description: desc,
|
|
343
|
+
inputSchema: {
|
|
344
|
+
json: tool.input_schema || {}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
export function extractImagesFromContent(content) {
|
|
351
|
+
const images = [];
|
|
352
|
+
let text = '';
|
|
353
|
+
if (Array.isArray(content)) {
|
|
354
|
+
for (const part of content) {
|
|
355
|
+
if (part.type === 'text') {
|
|
356
|
+
text += part.text || '';
|
|
357
|
+
}
|
|
358
|
+
else if (part.type === 'image' && part.source) {
|
|
359
|
+
images.push({
|
|
360
|
+
format: part.source.media_type?.split('/')[1] || 'png',
|
|
361
|
+
source: {
|
|
362
|
+
bytes: part.source.data
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
else if (typeof content === 'string') {
|
|
369
|
+
text = content;
|
|
370
|
+
}
|
|
371
|
+
return { text, images };
|
|
372
|
+
}
|
|
373
|
+
export function buildUserAgent(machineId) {
|
|
374
|
+
const platform = os.platform();
|
|
375
|
+
const nodeVersion = process.version.replace('v', '');
|
|
376
|
+
const arch = os.arch();
|
|
377
|
+
return `KiroIDE-${KIRO_CONSTANTS.KIRO_VERSION}-${machineId} ua/2.1 os/${platform} lang/js md/nodejs#${nodeVersion} api/codewhisperer#1.0.0 exec-env/${arch}`;
|
|
378
|
+
}
|
|
379
|
+
export function generateMachineId(auth) {
|
|
380
|
+
const source = auth.profileArn || auth.clientId || 'default';
|
|
381
|
+
return crypto.createHash('sha256').update(source).digest('hex').substring(0, 16);
|
|
382
|
+
}
|
|
383
|
+
function getContentText(message) {
|
|
384
|
+
if (typeof message === 'string') {
|
|
385
|
+
return message;
|
|
386
|
+
}
|
|
387
|
+
if (typeof message.content === 'string') {
|
|
388
|
+
return message.content;
|
|
389
|
+
}
|
|
390
|
+
if (Array.isArray(message.content)) {
|
|
391
|
+
return message.content
|
|
392
|
+
.filter((part) => part.type === 'text')
|
|
393
|
+
.map((part) => part.text || '')
|
|
394
|
+
.join('');
|
|
395
|
+
}
|
|
396
|
+
if (message.text) {
|
|
397
|
+
return message.text;
|
|
398
|
+
}
|
|
399
|
+
return '';
|
|
400
|
+
}
|
|
401
|
+
function deduplicateToolResults(toolResults) {
|
|
402
|
+
const unique = [];
|
|
403
|
+
const seenIds = new Set();
|
|
404
|
+
for (const tr of toolResults) {
|
|
405
|
+
if (!seenIds.has(tr.toolUseId)) {
|
|
406
|
+
seenIds.add(tr.toolUseId);
|
|
407
|
+
unique.push(tr);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return unique;
|
|
411
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ToolCall, ParsedResponse } from './types';
|
|
2
|
+
export declare function parseEventStream(rawResponse: string): ParsedResponse;
|
|
3
|
+
export declare function parseEventLine(line: string): any | null;
|
|
4
|
+
export declare function parseBracketToolCalls(text: string): ToolCall[];
|
|
5
|
+
export declare function deduplicateToolCalls(toolCalls: ToolCall[]): ToolCall[];
|
|
6
|
+
export declare function cleanToolCallsFromText(text: string, toolCalls: ToolCall[]): string;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
export function parseEventStream(rawResponse) {
|
|
2
|
+
const parsedFromEvents = parseEventStreamChunk(rawResponse);
|
|
3
|
+
let fullResponseText = parsedFromEvents.content;
|
|
4
|
+
let allToolCalls = [...parsedFromEvents.toolCalls];
|
|
5
|
+
const rawBracketToolCalls = parseBracketToolCalls(rawResponse);
|
|
6
|
+
if (rawBracketToolCalls.length > 0) {
|
|
7
|
+
allToolCalls.push(...rawBracketToolCalls);
|
|
8
|
+
}
|
|
9
|
+
const uniqueToolCalls = deduplicateToolCalls(allToolCalls);
|
|
10
|
+
if (uniqueToolCalls.length > 0) {
|
|
11
|
+
fullResponseText = cleanToolCallsFromText(fullResponseText, uniqueToolCalls);
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
content: fullResponseText,
|
|
15
|
+
toolCalls: uniqueToolCalls,
|
|
16
|
+
stopReason: parsedFromEvents.stopReason,
|
|
17
|
+
inputTokens: parsedFromEvents.inputTokens,
|
|
18
|
+
outputTokens: parsedFromEvents.outputTokens
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function parseEventStreamChunk(rawText) {
|
|
22
|
+
const events = parseAwsEventStreamBuffer(rawText);
|
|
23
|
+
let content = '';
|
|
24
|
+
const toolCallsMap = new Map();
|
|
25
|
+
let stopReason;
|
|
26
|
+
let inputTokens;
|
|
27
|
+
let outputTokens;
|
|
28
|
+
let contextUsagePercentage;
|
|
29
|
+
for (const event of events) {
|
|
30
|
+
if (event.type === 'content' && event.data) {
|
|
31
|
+
content += event.data;
|
|
32
|
+
}
|
|
33
|
+
else if (event.type === 'toolUse') {
|
|
34
|
+
const { name, toolUseId, input } = event.data;
|
|
35
|
+
if (name && toolUseId) {
|
|
36
|
+
if (toolCallsMap.has(toolUseId)) {
|
|
37
|
+
const existing = toolCallsMap.get(toolUseId);
|
|
38
|
+
existing.input = existing.input + (input || '');
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
toolCallsMap.set(toolUseId, {
|
|
42
|
+
toolUseId,
|
|
43
|
+
name,
|
|
44
|
+
input: input || ''
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else if (event.type === 'toolUseInput') {
|
|
50
|
+
const lastToolCall = Array.from(toolCallsMap.values()).pop();
|
|
51
|
+
if (lastToolCall) {
|
|
52
|
+
lastToolCall.input = lastToolCall.input + (event.data.input || '');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (event.type === 'toolUseStop') {
|
|
56
|
+
stopReason = 'tool_use';
|
|
57
|
+
}
|
|
58
|
+
else if (event.type === 'contextUsage') {
|
|
59
|
+
contextUsagePercentage = event.data.contextUsagePercentage;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const toolCalls = Array.from(toolCallsMap.values()).map(tc => {
|
|
63
|
+
let parsedInput = tc.input;
|
|
64
|
+
if (typeof tc.input === 'string' && tc.input.trim()) {
|
|
65
|
+
try {
|
|
66
|
+
parsedInput = JSON.parse(tc.input);
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
parsedInput = tc.input;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
toolUseId: tc.toolUseId,
|
|
74
|
+
name: tc.name,
|
|
75
|
+
input: parsedInput
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
if (contextUsagePercentage !== undefined) {
|
|
79
|
+
const totalTokens = Math.round(200000 * contextUsagePercentage / 100);
|
|
80
|
+
outputTokens = estimateTokens(content);
|
|
81
|
+
inputTokens = Math.max(0, totalTokens - outputTokens);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
content,
|
|
85
|
+
toolCalls,
|
|
86
|
+
stopReason: stopReason || (toolCalls.length > 0 ? 'tool_use' : 'end_turn'),
|
|
87
|
+
inputTokens,
|
|
88
|
+
outputTokens
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function parseAwsEventStreamBuffer(buffer) {
|
|
92
|
+
const events = [];
|
|
93
|
+
let remaining = buffer;
|
|
94
|
+
let searchStart = 0;
|
|
95
|
+
while (true) {
|
|
96
|
+
const contentStart = remaining.indexOf('{"content":', searchStart);
|
|
97
|
+
const nameStart = remaining.indexOf('{"name":', searchStart);
|
|
98
|
+
const followupStart = remaining.indexOf('{"followupPrompt":', searchStart);
|
|
99
|
+
const inputStart = remaining.indexOf('{"input":', searchStart);
|
|
100
|
+
const stopStart = remaining.indexOf('{"stop":', searchStart);
|
|
101
|
+
const contextUsageStart = remaining.indexOf('{"contextUsagePercentage":', searchStart);
|
|
102
|
+
const candidates = [contentStart, nameStart, followupStart, inputStart, stopStart, contextUsageStart].filter(pos => pos >= 0);
|
|
103
|
+
if (candidates.length === 0)
|
|
104
|
+
break;
|
|
105
|
+
const jsonStart = Math.min(...candidates);
|
|
106
|
+
if (jsonStart < 0)
|
|
107
|
+
break;
|
|
108
|
+
let braceCount = 0;
|
|
109
|
+
let jsonEnd = -1;
|
|
110
|
+
let inString = false;
|
|
111
|
+
let escapeNext = false;
|
|
112
|
+
for (let i = jsonStart; i < remaining.length; i++) {
|
|
113
|
+
const char = remaining[i];
|
|
114
|
+
if (escapeNext) {
|
|
115
|
+
escapeNext = false;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (char === '\\') {
|
|
119
|
+
escapeNext = true;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (char === '"') {
|
|
123
|
+
inString = !inString;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (!inString) {
|
|
127
|
+
if (char === '{') {
|
|
128
|
+
braceCount++;
|
|
129
|
+
}
|
|
130
|
+
else if (char === '}') {
|
|
131
|
+
braceCount--;
|
|
132
|
+
if (braceCount === 0) {
|
|
133
|
+
jsonEnd = i;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (jsonEnd < 0) {
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
const jsonStr = remaining.substring(jsonStart, jsonEnd + 1);
|
|
143
|
+
const parsed = parseEventLine(jsonStr);
|
|
144
|
+
if (parsed) {
|
|
145
|
+
if (parsed.content !== undefined && !parsed.followupPrompt) {
|
|
146
|
+
events.push({ type: 'content', data: parsed.content });
|
|
147
|
+
}
|
|
148
|
+
else if (parsed.name && parsed.toolUseId) {
|
|
149
|
+
events.push({
|
|
150
|
+
type: 'toolUse',
|
|
151
|
+
data: {
|
|
152
|
+
name: parsed.name,
|
|
153
|
+
toolUseId: parsed.toolUseId,
|
|
154
|
+
input: parsed.input || '',
|
|
155
|
+
stop: parsed.stop || false
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
else if (parsed.input !== undefined && !parsed.name) {
|
|
160
|
+
events.push({
|
|
161
|
+
type: 'toolUseInput',
|
|
162
|
+
data: {
|
|
163
|
+
input: parsed.input
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else if (parsed.stop !== undefined && parsed.contextUsagePercentage === undefined) {
|
|
168
|
+
events.push({
|
|
169
|
+
type: 'toolUseStop',
|
|
170
|
+
data: {
|
|
171
|
+
stop: parsed.stop
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
else if (parsed.contextUsagePercentage !== undefined) {
|
|
176
|
+
events.push({
|
|
177
|
+
type: 'contextUsage',
|
|
178
|
+
data: {
|
|
179
|
+
contextUsagePercentage: parsed.contextUsagePercentage
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
searchStart = jsonEnd + 1;
|
|
185
|
+
if (searchStart >= remaining.length) {
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return events;
|
|
190
|
+
}
|
|
191
|
+
export function parseEventLine(line) {
|
|
192
|
+
try {
|
|
193
|
+
return JSON.parse(line);
|
|
194
|
+
}
|
|
195
|
+
catch (e) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
export function parseBracketToolCalls(text) {
|
|
200
|
+
const toolCalls = [];
|
|
201
|
+
const pattern = /\[Called\s+(\w+)\s+with\s+args:\s*(\{[^}]*(?:\{[^}]*\}[^}]*)*\})\]/gs;
|
|
202
|
+
let match;
|
|
203
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
204
|
+
const funcName = match[1];
|
|
205
|
+
const argsStr = match[2];
|
|
206
|
+
if (!funcName || !argsStr)
|
|
207
|
+
continue;
|
|
208
|
+
try {
|
|
209
|
+
const args = JSON.parse(argsStr);
|
|
210
|
+
toolCalls.push({
|
|
211
|
+
toolUseId: `tool_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
212
|
+
name: funcName,
|
|
213
|
+
input: args
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
catch (e) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return toolCalls;
|
|
221
|
+
}
|
|
222
|
+
export function deduplicateToolCalls(toolCalls) {
|
|
223
|
+
const seen = new Set();
|
|
224
|
+
const unique = [];
|
|
225
|
+
for (const tc of toolCalls) {
|
|
226
|
+
if (!seen.has(tc.toolUseId)) {
|
|
227
|
+
seen.add(tc.toolUseId);
|
|
228
|
+
unique.push(tc);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return unique;
|
|
232
|
+
}
|
|
233
|
+
export function cleanToolCallsFromText(text, toolCalls) {
|
|
234
|
+
let cleaned = text;
|
|
235
|
+
for (const tc of toolCalls) {
|
|
236
|
+
const funcName = tc.name;
|
|
237
|
+
const escapedName = funcName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
238
|
+
const pattern = new RegExp(`\\[Called\\s+${escapedName}\\s+with\\s+args:\\s*\\{[^}]*(?:\\{[^}]*\\}[^}]*)*\\}\\]`, 'gs');
|
|
239
|
+
cleaned = cleaned.replace(pattern, '');
|
|
240
|
+
}
|
|
241
|
+
cleaned = cleaned.replace(/\s+/g, ' ').trim();
|
|
242
|
+
return cleaned;
|
|
243
|
+
}
|
|
244
|
+
function estimateTokens(text) {
|
|
245
|
+
return Math.ceil(text.length / 4);
|
|
246
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface CallbackResult {
|
|
2
|
+
code: string;
|
|
3
|
+
state: string;
|
|
4
|
+
}
|
|
5
|
+
interface CallbackServerResult {
|
|
6
|
+
url: string;
|
|
7
|
+
waitForCallback: () => Promise<CallbackResult>;
|
|
8
|
+
}
|
|
9
|
+
export declare function startCallbackServer(port?: number): Promise<CallbackServerResult>;
|
|
10
|
+
export {};
|