@kervnet/opencode-kiro-auth 1.5.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.
- package/README.md +159 -0
- package/dist/constants.d.ts +24 -0
- package/dist/constants.js +55 -0
- package/dist/core/account/account-selector.d.ts +25 -0
- package/dist/core/account/account-selector.js +84 -0
- package/dist/core/account/usage-tracker.d.ts +17 -0
- package/dist/core/account/usage-tracker.js +39 -0
- package/dist/core/auth/auth-handler.d.ts +15 -0
- package/dist/core/auth/auth-handler.js +43 -0
- package/dist/core/auth/idc-auth-method.d.ts +17 -0
- package/dist/core/auth/idc-auth-method.js +200 -0
- package/dist/core/auth/token-refresher.d.ts +22 -0
- package/dist/core/auth/token-refresher.js +53 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.js +9 -0
- package/dist/core/request/error-handler.d.ts +30 -0
- package/dist/core/request/error-handler.js +113 -0
- package/dist/core/request/request-handler.d.ts +27 -0
- package/dist/core/request/request-handler.js +199 -0
- package/dist/core/request/response-handler.d.ts +5 -0
- package/dist/core/request/response-handler.js +61 -0
- package/dist/core/request/retry-strategy.d.ts +18 -0
- package/dist/core/request/retry-strategy.js +28 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/infrastructure/database/account-cache.d.ts +14 -0
- package/dist/infrastructure/database/account-cache.js +44 -0
- package/dist/infrastructure/database/account-repository.d.ts +12 -0
- package/dist/infrastructure/database/account-repository.js +64 -0
- package/dist/infrastructure/index.d.ts +7 -0
- package/dist/infrastructure/index.js +7 -0
- package/dist/infrastructure/transformers/event-stream-parser.d.ts +7 -0
- package/dist/infrastructure/transformers/event-stream-parser.js +115 -0
- package/dist/infrastructure/transformers/history-builder.d.ts +5 -0
- package/dist/infrastructure/transformers/history-builder.js +171 -0
- package/dist/infrastructure/transformers/message-transformer.d.ts +6 -0
- package/dist/infrastructure/transformers/message-transformer.js +102 -0
- package/dist/infrastructure/transformers/tool-call-parser.d.ts +4 -0
- package/dist/infrastructure/transformers/tool-call-parser.js +45 -0
- package/dist/infrastructure/transformers/tool-transformer.d.ts +2 -0
- package/dist/infrastructure/transformers/tool-transformer.js +19 -0
- package/dist/kiro/auth.d.ts +4 -0
- package/dist/kiro/auth.js +25 -0
- package/dist/kiro/oauth-idc.d.ts +24 -0
- package/dist/kiro/oauth-idc.js +151 -0
- package/dist/plugin/accounts.d.ts +29 -0
- package/dist/plugin/accounts.js +235 -0
- package/dist/plugin/auth-page.d.ts +3 -0
- package/dist/plugin/auth-page.js +573 -0
- package/dist/plugin/cli.d.ts +8 -0
- package/dist/plugin/cli.js +103 -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 +129 -0
- package/dist/plugin/config/schema.d.ts +56 -0
- package/dist/plugin/config/schema.js +36 -0
- package/dist/plugin/errors.d.ts +17 -0
- package/dist/plugin/errors.js +34 -0
- package/dist/plugin/health.d.ts +1 -0
- package/dist/plugin/health.js +9 -0
- package/dist/plugin/image-handler.d.ts +14 -0
- package/dist/plugin/image-handler.js +64 -0
- package/dist/plugin/logger.d.ts +8 -0
- package/dist/plugin/logger.js +63 -0
- package/dist/plugin/models.d.ts +1 -0
- package/dist/plugin/models.js +8 -0
- package/dist/plugin/request.d.ts +2 -0
- package/dist/plugin/request.js +239 -0
- package/dist/plugin/response.d.ts +3 -0
- package/dist/plugin/response.js +95 -0
- package/dist/plugin/server.d.ts +24 -0
- package/dist/plugin/server.js +166 -0
- package/dist/plugin/storage/locked-operations.d.ts +5 -0
- package/dist/plugin/storage/locked-operations.js +91 -0
- package/dist/plugin/storage/migrations.d.ts +2 -0
- package/dist/plugin/storage/migrations.js +109 -0
- package/dist/plugin/storage/sqlite.d.ts +17 -0
- package/dist/plugin/storage/sqlite.js +134 -0
- package/dist/plugin/streaming/index.d.ts +2 -0
- package/dist/plugin/streaming/index.js +2 -0
- package/dist/plugin/streaming/openai-converter.d.ts +2 -0
- package/dist/plugin/streaming/openai-converter.js +68 -0
- package/dist/plugin/streaming/stream-parser.d.ts +5 -0
- package/dist/plugin/streaming/stream-parser.js +136 -0
- package/dist/plugin/streaming/stream-state.d.ts +5 -0
- package/dist/plugin/streaming/stream-state.js +59 -0
- package/dist/plugin/streaming/stream-transformer.d.ts +1 -0
- package/dist/plugin/streaming/stream-transformer.js +248 -0
- package/dist/plugin/streaming/types.d.ts +25 -0
- package/dist/plugin/streaming/types.js +2 -0
- package/dist/plugin/sync/aws-sso.d.ts +2 -0
- package/dist/plugin/sync/aws-sso.js +50 -0
- package/dist/plugin/sync/kiro-cli-parser.d.ts +8 -0
- package/dist/plugin/sync/kiro-cli-parser.js +72 -0
- package/dist/plugin/sync/kiro-cli.d.ts +2 -0
- package/dist/plugin/sync/kiro-cli.js +197 -0
- package/dist/plugin/token.d.ts +2 -0
- package/dist/plugin/token.js +79 -0
- package/dist/plugin/types.d.ts +109 -0
- package/dist/plugin/types.js +0 -0
- package/dist/plugin/usage.d.ts +3 -0
- package/dist/plugin/usage.js +45 -0
- package/dist/plugin.d.ts +32 -0
- package/dist/plugin.js +37 -0
- package/package.json +65 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { parseEventStream } from '../../plugin/response';
|
|
2
|
+
import { transformKiroStream } from '../../plugin/streaming/index.js';
|
|
3
|
+
export class ResponseHandler {
|
|
4
|
+
async handleSuccess(response, model, conversationId, streaming) {
|
|
5
|
+
if (streaming) {
|
|
6
|
+
return this.handleStreaming(response, model, conversationId);
|
|
7
|
+
}
|
|
8
|
+
return this.handleNonStreaming(response, model, conversationId);
|
|
9
|
+
}
|
|
10
|
+
async handleStreaming(response, model, conversationId) {
|
|
11
|
+
const s = transformKiroStream(response, model, conversationId);
|
|
12
|
+
return new Response(new ReadableStream({
|
|
13
|
+
async start(c) {
|
|
14
|
+
try {
|
|
15
|
+
for await (const e of s) {
|
|
16
|
+
c.enqueue(new TextEncoder().encode(`data: ${JSON.stringify(e)}\n\n`));
|
|
17
|
+
}
|
|
18
|
+
c.close();
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
c.error(err);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}), { headers: { 'Content-Type': 'text/event-stream' } });
|
|
25
|
+
}
|
|
26
|
+
async handleNonStreaming(response, model, conversationId) {
|
|
27
|
+
const text = await response.text();
|
|
28
|
+
const p = parseEventStream(text);
|
|
29
|
+
const oai = {
|
|
30
|
+
id: conversationId,
|
|
31
|
+
object: 'chat.completion',
|
|
32
|
+
created: Math.floor(Date.now() / 1000),
|
|
33
|
+
model,
|
|
34
|
+
choices: [
|
|
35
|
+
{
|
|
36
|
+
index: 0,
|
|
37
|
+
message: { role: 'assistant', content: p.content },
|
|
38
|
+
finish_reason: p.stopReason === 'tool_use' ? 'tool_calls' : 'stop'
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
usage: {
|
|
42
|
+
prompt_tokens: p.inputTokens || 0,
|
|
43
|
+
completion_tokens: p.outputTokens || 0,
|
|
44
|
+
total_tokens: (p.inputTokens || 0) + (p.outputTokens || 0)
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
if (p.toolCalls.length > 0) {
|
|
48
|
+
oai.choices[0].message.tool_calls = p.toolCalls.map((tc) => ({
|
|
49
|
+
id: tc.toolUseId,
|
|
50
|
+
type: 'function',
|
|
51
|
+
function: {
|
|
52
|
+
name: tc.name,
|
|
53
|
+
arguments: typeof tc.input === 'string' ? tc.input : JSON.stringify(tc.input)
|
|
54
|
+
}
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
return new Response(JSON.stringify(oai), {
|
|
58
|
+
headers: { 'Content-Type': 'application/json' }
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface RetryConfig {
|
|
2
|
+
max_request_iterations: number;
|
|
3
|
+
request_timeout_ms: number;
|
|
4
|
+
}
|
|
5
|
+
interface RetryContext {
|
|
6
|
+
iterations: number;
|
|
7
|
+
startTime: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class RetryStrategy {
|
|
10
|
+
private config;
|
|
11
|
+
constructor(config: RetryConfig);
|
|
12
|
+
shouldContinue(context: RetryContext): {
|
|
13
|
+
canContinue: boolean;
|
|
14
|
+
error?: string;
|
|
15
|
+
};
|
|
16
|
+
createContext(): RetryContext;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export class RetryStrategy {
|
|
2
|
+
config;
|
|
3
|
+
constructor(config) {
|
|
4
|
+
this.config = config;
|
|
5
|
+
}
|
|
6
|
+
shouldContinue(context) {
|
|
7
|
+
context.iterations++;
|
|
8
|
+
if (context.iterations > this.config.max_request_iterations) {
|
|
9
|
+
return {
|
|
10
|
+
canContinue: false,
|
|
11
|
+
error: `Exceeded max iterations (${this.config.max_request_iterations})`
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
if (Date.now() - context.startTime > this.config.request_timeout_ms) {
|
|
15
|
+
return {
|
|
16
|
+
canContinue: false,
|
|
17
|
+
error: 'Request timeout'
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return { canContinue: true };
|
|
21
|
+
}
|
|
22
|
+
createContext() {
|
|
23
|
+
return {
|
|
24
|
+
iterations: 0,
|
|
25
|
+
startTime: Date.now()
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { KiroOAuthPlugin } from './plugin.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare class AccountCache {
|
|
2
|
+
private cache;
|
|
3
|
+
private allAccountsCache;
|
|
4
|
+
private lastUpdate;
|
|
5
|
+
private ttl;
|
|
6
|
+
constructor(ttlMs?: number);
|
|
7
|
+
getAll(): any[] | null;
|
|
8
|
+
setAll(accounts: any[]): void;
|
|
9
|
+
get(id: string): any | null;
|
|
10
|
+
set(id: string, account: any): void;
|
|
11
|
+
invalidate(id: string): void;
|
|
12
|
+
invalidateAll(): void;
|
|
13
|
+
private isExpired;
|
|
14
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export class AccountCache {
|
|
2
|
+
cache = new Map();
|
|
3
|
+
allAccountsCache = null;
|
|
4
|
+
lastUpdate = 0;
|
|
5
|
+
ttl;
|
|
6
|
+
constructor(ttlMs = 60000) {
|
|
7
|
+
this.ttl = ttlMs;
|
|
8
|
+
}
|
|
9
|
+
getAll() {
|
|
10
|
+
if (this.isExpired()) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return this.allAccountsCache ? [...this.allAccountsCache] : null;
|
|
14
|
+
}
|
|
15
|
+
setAll(accounts) {
|
|
16
|
+
this.allAccountsCache = [...accounts];
|
|
17
|
+
this.lastUpdate = Date.now();
|
|
18
|
+
for (const acc of accounts) {
|
|
19
|
+
this.cache.set(acc.id, acc);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
get(id) {
|
|
23
|
+
if (this.isExpired()) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return this.cache.get(id) || null;
|
|
27
|
+
}
|
|
28
|
+
set(id, account) {
|
|
29
|
+
this.cache.set(id, account);
|
|
30
|
+
this.lastUpdate = Date.now();
|
|
31
|
+
}
|
|
32
|
+
invalidate(id) {
|
|
33
|
+
this.cache.delete(id);
|
|
34
|
+
this.allAccountsCache = null;
|
|
35
|
+
}
|
|
36
|
+
invalidateAll() {
|
|
37
|
+
this.cache.clear();
|
|
38
|
+
this.allAccountsCache = null;
|
|
39
|
+
this.lastUpdate = 0;
|
|
40
|
+
}
|
|
41
|
+
isExpired() {
|
|
42
|
+
return Date.now() - this.lastUpdate > this.ttl;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AccountCache } from './account-cache.js';
|
|
2
|
+
export declare class AccountRepository {
|
|
3
|
+
private cache;
|
|
4
|
+
constructor(cache: AccountCache);
|
|
5
|
+
findAll(): Promise<any[]>;
|
|
6
|
+
findById(id: string): Promise<any | null>;
|
|
7
|
+
save(account: any): Promise<void>;
|
|
8
|
+
delete(id: string): Promise<void>;
|
|
9
|
+
findHealthyAccounts(): Promise<any[]>;
|
|
10
|
+
batchSave(accounts: any[]): Promise<void>;
|
|
11
|
+
invalidateCache(): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { kiroDb } from '../../plugin/storage/sqlite.js';
|
|
2
|
+
export class AccountRepository {
|
|
3
|
+
cache;
|
|
4
|
+
constructor(cache) {
|
|
5
|
+
this.cache = cache;
|
|
6
|
+
}
|
|
7
|
+
async findAll() {
|
|
8
|
+
const cached = this.cache.getAll();
|
|
9
|
+
if (cached) {
|
|
10
|
+
return cached;
|
|
11
|
+
}
|
|
12
|
+
const rows = kiroDb.getAccounts();
|
|
13
|
+
const accounts = rows.map((r) => ({
|
|
14
|
+
id: r.id,
|
|
15
|
+
email: r.email,
|
|
16
|
+
authMethod: r.auth_method,
|
|
17
|
+
region: r.region,
|
|
18
|
+
clientId: r.client_id,
|
|
19
|
+
clientSecret: r.client_secret,
|
|
20
|
+
profileArn: r.profile_arn,
|
|
21
|
+
refreshToken: r.refresh_token,
|
|
22
|
+
accessToken: r.access_token,
|
|
23
|
+
expiresAt: r.expires_at,
|
|
24
|
+
rateLimitResetTime: r.rate_limit_reset,
|
|
25
|
+
isHealthy: r.is_healthy === 1,
|
|
26
|
+
unhealthyReason: r.unhealthy_reason,
|
|
27
|
+
recoveryTime: r.recovery_time,
|
|
28
|
+
failCount: r.fail_count || 0,
|
|
29
|
+
lastUsed: r.last_used,
|
|
30
|
+
usedCount: r.used_count,
|
|
31
|
+
limitCount: r.limit_count,
|
|
32
|
+
lastSync: r.last_sync
|
|
33
|
+
}));
|
|
34
|
+
this.cache.setAll(accounts);
|
|
35
|
+
return accounts;
|
|
36
|
+
}
|
|
37
|
+
async findById(id) {
|
|
38
|
+
const cached = this.cache.get(id);
|
|
39
|
+
if (cached) {
|
|
40
|
+
return cached;
|
|
41
|
+
}
|
|
42
|
+
const accounts = await this.findAll();
|
|
43
|
+
return accounts.find((a) => a.id === id) || null;
|
|
44
|
+
}
|
|
45
|
+
async save(account) {
|
|
46
|
+
await kiroDb.upsertAccount(account);
|
|
47
|
+
this.cache.invalidate(account.id);
|
|
48
|
+
}
|
|
49
|
+
async delete(id) {
|
|
50
|
+
await kiroDb.deleteAccount(id);
|
|
51
|
+
this.cache.invalidate(id);
|
|
52
|
+
}
|
|
53
|
+
async findHealthyAccounts() {
|
|
54
|
+
const all = await this.findAll();
|
|
55
|
+
return all.filter((a) => a.isHealthy);
|
|
56
|
+
}
|
|
57
|
+
async batchSave(accounts) {
|
|
58
|
+
await kiroDb.batchUpsertAccounts(accounts);
|
|
59
|
+
this.cache.invalidateAll();
|
|
60
|
+
}
|
|
61
|
+
invalidateCache() {
|
|
62
|
+
this.cache.invalidateAll();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './database/account-cache.js';
|
|
2
|
+
export * from './database/account-repository.js';
|
|
3
|
+
export * from './transformers/event-stream-parser.js';
|
|
4
|
+
export * from './transformers/history-builder.js';
|
|
5
|
+
export * from './transformers/message-transformer.js';
|
|
6
|
+
export * from './transformers/tool-call-parser.js';
|
|
7
|
+
export * from './transformers/tool-transformer.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './database/account-cache.js';
|
|
2
|
+
export * from './database/account-repository.js';
|
|
3
|
+
export * from './transformers/event-stream-parser.js';
|
|
4
|
+
export * from './transformers/history-builder.js';
|
|
5
|
+
export * from './transformers/message-transformer.js';
|
|
6
|
+
export * from './transformers/tool-call-parser.js';
|
|
7
|
+
export * from './transformers/tool-transformer.js';
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export function parseAwsEventStreamBuffer(buffer) {
|
|
2
|
+
const events = [];
|
|
3
|
+
let remaining = buffer;
|
|
4
|
+
let searchStart = 0;
|
|
5
|
+
while (true) {
|
|
6
|
+
const contentStart = remaining.indexOf('{"content":', searchStart);
|
|
7
|
+
const nameStart = remaining.indexOf('{"name":', searchStart);
|
|
8
|
+
const followupStart = remaining.indexOf('{"followupPrompt":', searchStart);
|
|
9
|
+
const inputStart = remaining.indexOf('{"input":', searchStart);
|
|
10
|
+
const stopStart = remaining.indexOf('{"stop":', searchStart);
|
|
11
|
+
const contextUsageStart = remaining.indexOf('{"contextUsagePercentage":', searchStart);
|
|
12
|
+
const candidates = [
|
|
13
|
+
contentStart,
|
|
14
|
+
nameStart,
|
|
15
|
+
followupStart,
|
|
16
|
+
inputStart,
|
|
17
|
+
stopStart,
|
|
18
|
+
contextUsageStart
|
|
19
|
+
].filter((pos) => pos >= 0);
|
|
20
|
+
if (candidates.length === 0)
|
|
21
|
+
break;
|
|
22
|
+
const jsonStart = Math.min(...candidates);
|
|
23
|
+
if (jsonStart < 0)
|
|
24
|
+
break;
|
|
25
|
+
let braceCount = 0;
|
|
26
|
+
let jsonEnd = -1;
|
|
27
|
+
let inString = false;
|
|
28
|
+
let escapeNext = false;
|
|
29
|
+
for (let i = jsonStart; i < remaining.length; i++) {
|
|
30
|
+
const char = remaining[i];
|
|
31
|
+
if (escapeNext) {
|
|
32
|
+
escapeNext = false;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (char === '\\') {
|
|
36
|
+
escapeNext = true;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (char === '"') {
|
|
40
|
+
inString = !inString;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (!inString) {
|
|
44
|
+
if (char === '{') {
|
|
45
|
+
braceCount++;
|
|
46
|
+
}
|
|
47
|
+
else if (char === '}') {
|
|
48
|
+
braceCount--;
|
|
49
|
+
if (braceCount === 0) {
|
|
50
|
+
jsonEnd = i;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (jsonEnd < 0) {
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
const jsonStr = remaining.substring(jsonStart, jsonEnd + 1);
|
|
60
|
+
const parsed = parseEventLine(jsonStr);
|
|
61
|
+
if (parsed) {
|
|
62
|
+
if (parsed.content !== undefined && !parsed.followupPrompt) {
|
|
63
|
+
events.push({ type: 'content', data: parsed.content });
|
|
64
|
+
}
|
|
65
|
+
else if (parsed.name && parsed.toolUseId) {
|
|
66
|
+
events.push({
|
|
67
|
+
type: 'toolUse',
|
|
68
|
+
data: {
|
|
69
|
+
name: parsed.name,
|
|
70
|
+
toolUseId: parsed.toolUseId,
|
|
71
|
+
input: parsed.input || '',
|
|
72
|
+
stop: parsed.stop || false
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else if (parsed.input !== undefined && !parsed.name) {
|
|
77
|
+
events.push({
|
|
78
|
+
type: 'toolUseInput',
|
|
79
|
+
data: {
|
|
80
|
+
input: parsed.input
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else if (parsed.stop !== undefined && parsed.contextUsagePercentage === undefined) {
|
|
85
|
+
events.push({
|
|
86
|
+
type: 'toolUseStop',
|
|
87
|
+
data: {
|
|
88
|
+
stop: parsed.stop
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
else if (parsed.contextUsagePercentage !== undefined) {
|
|
93
|
+
events.push({
|
|
94
|
+
type: 'contextUsage',
|
|
95
|
+
data: {
|
|
96
|
+
contextUsagePercentage: parsed.contextUsagePercentage
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
searchStart = jsonEnd + 1;
|
|
102
|
+
if (searchStart >= remaining.length) {
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return events;
|
|
107
|
+
}
|
|
108
|
+
export function parseEventLine(line) {
|
|
109
|
+
try {
|
|
110
|
+
return JSON.parse(line);
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CodeWhispererMessage } from '../../plugin/types';
|
|
2
|
+
export declare function buildHistory(msgs: any[], resolved: string, system: string | undefined, toolResultLimit: number): CodeWhispererMessage[];
|
|
3
|
+
export declare function truncateHistory(history: CodeWhispererMessage[], historyLimit: number): CodeWhispererMessage[];
|
|
4
|
+
export declare function historyHasToolCalling(history: CodeWhispererMessage[]): boolean;
|
|
5
|
+
export declare function extractToolNamesFromHistory(history: CodeWhispererMessage[]): Set<string>;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { KIRO_CONSTANTS } from '../../constants.js';
|
|
2
|
+
import { convertImagesToKiroFormat, extractAllImages, extractTextFromParts } from '../../plugin/image-handler.js';
|
|
3
|
+
import { getContentText, sanitizeHistory, truncate } from './message-transformer.js';
|
|
4
|
+
import { deduplicateToolResults } from './tool-transformer.js';
|
|
5
|
+
export function buildHistory(msgs, resolved, system, toolResultLimit) {
|
|
6
|
+
let history = [];
|
|
7
|
+
let firstUserIndex = -1;
|
|
8
|
+
for (let i = 0; i < msgs.length; i++) {
|
|
9
|
+
if (msgs[i].role === 'user') {
|
|
10
|
+
firstUserIndex = i;
|
|
11
|
+
break;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
if (system) {
|
|
15
|
+
if (firstUserIndex !== -1) {
|
|
16
|
+
const m = msgs[firstUserIndex];
|
|
17
|
+
const oldContent = getContentText(m);
|
|
18
|
+
if (Array.isArray(m.content)) {
|
|
19
|
+
m.content = [
|
|
20
|
+
{ type: 'text', text: `${system}\n\n${oldContent}` },
|
|
21
|
+
...m.content.filter((p) => p.type !== 'text')
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
else
|
|
25
|
+
m.content = `${system}\n\n${oldContent}`;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
history.push({
|
|
29
|
+
userInputMessage: {
|
|
30
|
+
content: system,
|
|
31
|
+
modelId: resolved,
|
|
32
|
+
origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
for (let i = 0; i < msgs.length - 1; i++) {
|
|
38
|
+
const m = msgs[i];
|
|
39
|
+
if (!m)
|
|
40
|
+
continue;
|
|
41
|
+
if (m.role === 'user') {
|
|
42
|
+
const uim = { content: '', modelId: resolved, origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR };
|
|
43
|
+
const trs = [];
|
|
44
|
+
if (Array.isArray(m.content)) {
|
|
45
|
+
uim.content = extractTextFromParts(m.content);
|
|
46
|
+
for (const p of m.content) {
|
|
47
|
+
if (p.type === 'tool_result') {
|
|
48
|
+
trs.push({
|
|
49
|
+
content: [{ text: truncate(getContentText(p.content || p), toolResultLimit) }],
|
|
50
|
+
status: 'success',
|
|
51
|
+
toolUseId: p.tool_use_id
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const unifiedImages = extractAllImages(m.content);
|
|
56
|
+
if (unifiedImages.length > 0) {
|
|
57
|
+
uim.images = convertImagesToKiroFormat(unifiedImages);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
uim.content = getContentText(m);
|
|
62
|
+
}
|
|
63
|
+
if (trs.length)
|
|
64
|
+
uim.userInputMessageContext = { toolResults: deduplicateToolResults(trs) };
|
|
65
|
+
const prev = history[history.length - 1];
|
|
66
|
+
if (prev && prev.userInputMessage)
|
|
67
|
+
history.push({ assistantResponseMessage: { content: 'Continue' } });
|
|
68
|
+
history.push({ userInputMessage: uim });
|
|
69
|
+
}
|
|
70
|
+
else if (m.role === 'tool') {
|
|
71
|
+
const trs = [];
|
|
72
|
+
if (m.tool_results) {
|
|
73
|
+
for (const tr of m.tool_results)
|
|
74
|
+
trs.push({
|
|
75
|
+
content: [{ text: truncate(getContentText(tr), toolResultLimit) }],
|
|
76
|
+
status: 'success',
|
|
77
|
+
toolUseId: tr.tool_call_id
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
trs.push({
|
|
82
|
+
content: [{ text: truncate(getContentText(m), toolResultLimit) }],
|
|
83
|
+
status: 'success',
|
|
84
|
+
toolUseId: m.tool_call_id
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const prev = history[history.length - 1];
|
|
88
|
+
if (prev && prev.userInputMessage)
|
|
89
|
+
history.push({ assistantResponseMessage: { content: 'Continue' } });
|
|
90
|
+
history.push({
|
|
91
|
+
userInputMessage: {
|
|
92
|
+
content: 'Tool results provided.',
|
|
93
|
+
modelId: resolved,
|
|
94
|
+
origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR,
|
|
95
|
+
userInputMessageContext: { toolResults: deduplicateToolResults(trs) }
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
else if (m.role === 'assistant') {
|
|
100
|
+
const arm = { content: '' };
|
|
101
|
+
const tus = [];
|
|
102
|
+
let th = '';
|
|
103
|
+
if (Array.isArray(m.content)) {
|
|
104
|
+
for (const p of m.content) {
|
|
105
|
+
if (p.type === 'text')
|
|
106
|
+
arm.content += p.text || '';
|
|
107
|
+
else if (p.type === 'thinking')
|
|
108
|
+
th += p.thinking || p.text || '';
|
|
109
|
+
else if (p.type === 'tool_use')
|
|
110
|
+
tus.push({ input: p.input, name: p.name, toolUseId: p.id });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else
|
|
114
|
+
arm.content = getContentText(m);
|
|
115
|
+
if (m.tool_calls && Array.isArray(m.tool_calls)) {
|
|
116
|
+
for (const tc of m.tool_calls) {
|
|
117
|
+
tus.push({
|
|
118
|
+
input: typeof tc.function?.arguments === 'string'
|
|
119
|
+
? JSON.parse(tc.function.arguments)
|
|
120
|
+
: tc.function?.arguments,
|
|
121
|
+
name: tc.function?.name,
|
|
122
|
+
toolUseId: tc.id
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (th)
|
|
127
|
+
arm.content = arm.content
|
|
128
|
+
? `<thinking>${th}</thinking>\n\n${arm.content}`
|
|
129
|
+
: `<thinking>${th}</thinking>`;
|
|
130
|
+
if (tus.length)
|
|
131
|
+
arm.toolUses = tus;
|
|
132
|
+
if (!arm.content && !arm.toolUses) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
history.push({ assistantResponseMessage: arm });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return history;
|
|
139
|
+
}
|
|
140
|
+
export function truncateHistory(history, historyLimit) {
|
|
141
|
+
let sanitized = sanitizeHistory(history);
|
|
142
|
+
let historySize = JSON.stringify(sanitized).length;
|
|
143
|
+
while (historySize > historyLimit && sanitized.length > 2) {
|
|
144
|
+
sanitized.shift();
|
|
145
|
+
while (sanitized.length > 0) {
|
|
146
|
+
const first = sanitized[0];
|
|
147
|
+
if (first && first.userInputMessage)
|
|
148
|
+
break;
|
|
149
|
+
sanitized.shift();
|
|
150
|
+
}
|
|
151
|
+
sanitized = sanitizeHistory(sanitized);
|
|
152
|
+
historySize = JSON.stringify(sanitized).length;
|
|
153
|
+
}
|
|
154
|
+
return sanitized;
|
|
155
|
+
}
|
|
156
|
+
export function historyHasToolCalling(history) {
|
|
157
|
+
return history.some((h) => h.assistantResponseMessage?.toolUses ||
|
|
158
|
+
h.userInputMessage?.userInputMessageContext?.toolResults);
|
|
159
|
+
}
|
|
160
|
+
export function extractToolNamesFromHistory(history) {
|
|
161
|
+
const toolNames = new Set();
|
|
162
|
+
for (const h of history) {
|
|
163
|
+
if (h.assistantResponseMessage?.toolUses) {
|
|
164
|
+
for (const tu of h.assistantResponseMessage.toolUses) {
|
|
165
|
+
if (tu.name)
|
|
166
|
+
toolNames.add(tu.name);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return toolNames;
|
|
171
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CodeWhispererMessage } from '../../plugin/types';
|
|
2
|
+
export declare function sanitizeHistory(history: CodeWhispererMessage[]): CodeWhispererMessage[];
|
|
3
|
+
export declare function findOriginalToolCall(msgs: any[], toolUseId: string): any | null;
|
|
4
|
+
export declare function mergeAdjacentMessages(msgs: any[]): any[];
|
|
5
|
+
export declare function getContentText(m: any): string;
|
|
6
|
+
export declare function truncate(s: string, max: number): string;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export function sanitizeHistory(history) {
|
|
2
|
+
const result = [];
|
|
3
|
+
for (let i = 0; i < history.length; i++) {
|
|
4
|
+
const m = history[i];
|
|
5
|
+
if (!m)
|
|
6
|
+
continue;
|
|
7
|
+
if (m.assistantResponseMessage?.toolUses) {
|
|
8
|
+
const next = history[i + 1];
|
|
9
|
+
if (next?.userInputMessage?.userInputMessageContext?.toolResults) {
|
|
10
|
+
result.push(m);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
else if (m.userInputMessage?.userInputMessageContext?.toolResults) {
|
|
14
|
+
const prev = result[result.length - 1];
|
|
15
|
+
if (prev?.assistantResponseMessage?.toolUses) {
|
|
16
|
+
result.push(m);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
result.push(m);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (result.length > 0) {
|
|
24
|
+
const first = result[0];
|
|
25
|
+
if (!first ||
|
|
26
|
+
!first.userInputMessage ||
|
|
27
|
+
first.userInputMessage.userInputMessageContext?.toolResults) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
export function findOriginalToolCall(msgs, toolUseId) {
|
|
34
|
+
for (const m of msgs) {
|
|
35
|
+
if (m.role === 'assistant') {
|
|
36
|
+
if (m.tool_calls) {
|
|
37
|
+
for (const tc of m.tool_calls)
|
|
38
|
+
if (tc.id === toolUseId)
|
|
39
|
+
return tc;
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(m.content)) {
|
|
42
|
+
for (const p of m.content)
|
|
43
|
+
if (p.type === 'tool_use' && p.id === toolUseId)
|
|
44
|
+
return p;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
export function mergeAdjacentMessages(msgs) {
|
|
51
|
+
const merged = [];
|
|
52
|
+
for (const m of msgs) {
|
|
53
|
+
if (!merged.length)
|
|
54
|
+
merged.push({ ...m });
|
|
55
|
+
else {
|
|
56
|
+
const last = merged[merged.length - 1];
|
|
57
|
+
if (last && m.role === last.role) {
|
|
58
|
+
if (Array.isArray(last.content) && Array.isArray(m.content))
|
|
59
|
+
last.content.push(...m.content);
|
|
60
|
+
else if (typeof last.content === 'string' && typeof m.content === 'string')
|
|
61
|
+
last.content += '\n' + m.content;
|
|
62
|
+
else if (Array.isArray(last.content) && typeof m.content === 'string')
|
|
63
|
+
last.content.push({ type: 'text', text: m.content });
|
|
64
|
+
else if (typeof last.content === 'string' && Array.isArray(m.content))
|
|
65
|
+
last.content = [{ type: 'text', text: last.content }, ...m.content];
|
|
66
|
+
if (m.tool_calls) {
|
|
67
|
+
if (!last.tool_calls)
|
|
68
|
+
last.tool_calls = [];
|
|
69
|
+
last.tool_calls.push(...m.tool_calls);
|
|
70
|
+
}
|
|
71
|
+
if (m.role === 'tool') {
|
|
72
|
+
if (!last.tool_results)
|
|
73
|
+
last.tool_results = [{ content: last.content, tool_call_id: last.tool_call_id }];
|
|
74
|
+
last.tool_results.push({ content: m.content, tool_call_id: m.tool_call_id });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else
|
|
78
|
+
merged.push({ ...m });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return merged;
|
|
82
|
+
}
|
|
83
|
+
export function getContentText(m) {
|
|
84
|
+
if (!m)
|
|
85
|
+
return '';
|
|
86
|
+
if (typeof m === 'string')
|
|
87
|
+
return m;
|
|
88
|
+
if (typeof m.content === 'string')
|
|
89
|
+
return m.content;
|
|
90
|
+
if (Array.isArray(m.content))
|
|
91
|
+
return m.content
|
|
92
|
+
.filter((p) => p.type === 'text')
|
|
93
|
+
.map((p) => p.text || '')
|
|
94
|
+
.join('');
|
|
95
|
+
return m.text || '';
|
|
96
|
+
}
|
|
97
|
+
export function truncate(s, max) {
|
|
98
|
+
if (s.length <= max)
|
|
99
|
+
return s;
|
|
100
|
+
const half = Math.floor(max / 2);
|
|
101
|
+
return s.substring(0, half) + '\n... [TRUNCATED] ...\n' + s.substring(s.length - half);
|
|
102
|
+
}
|