@sunerpy/opencode-kiro-auth 0.6.2 → 0.8.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 +12 -4
- package/dist/core/auth/auth-handler.js +4 -2
- package/dist/core/auth/idc-auth-method.js +15 -3
- package/dist/core/auth/token-keepalive.d.ts +27 -0
- package/dist/core/auth/token-keepalive.js +137 -0
- package/dist/core/auth/token-refresher.d.ts +20 -1
- package/dist/core/auth/token-refresher.js +124 -22
- package/dist/core/request/error-handler.d.ts +6 -3
- package/dist/core/request/error-handler.js +50 -24
- package/dist/core/request/request-handler.d.ts +2 -0
- package/dist/core/request/request-handler.js +5 -2
- package/dist/infrastructure/transformers/event-stream-parser.js +1 -1
- package/dist/infrastructure/transformers/tool-call-parser.js +1 -1
- package/dist/kiro/oauth-idc.js +54 -59
- package/dist/plugin/accounts.js +13 -8
- package/dist/plugin/config/loader.js +49 -1
- package/dist/plugin/config/schema.d.ts +14 -0
- package/dist/plugin/config/schema.js +14 -2
- package/dist/plugin/health.d.ts +22 -0
- package/dist/plugin/health.js +56 -1
- package/dist/plugin/image-handler.js +1 -1
- package/dist/plugin/logger.js +2 -2
- package/dist/plugin/request.js +1 -1
- package/dist/plugin/response.js +1 -1
- package/dist/plugin/storage/locked-operations.d.ts +7 -0
- package/dist/plugin/storage/locked-operations.js +93 -1
- package/dist/plugin/storage/migrations.js +1 -1
- package/dist/plugin/storage/sqlite.d.ts +4 -0
- package/dist/plugin/storage/sqlite.js +65 -3
- package/dist/plugin/streaming/sdk-stream-transformer.js +233 -244
- package/dist/plugin/streaming/stream-transformer.js +1 -1
- package/dist/plugin/sync/kiro-cli.js +0 -2
- package/dist/plugin.d.ts +2 -0
- package/dist/plugin.js +28 -1
- package/package.json +1 -1
|
@@ -39,7 +39,15 @@ export class KiroDatabase {
|
|
|
39
39
|
runMigrations(this.db);
|
|
40
40
|
}
|
|
41
41
|
getAccounts() {
|
|
42
|
-
return this.db
|
|
42
|
+
return this.db
|
|
43
|
+
.prepare(`
|
|
44
|
+
SELECT accounts.*
|
|
45
|
+
FROM accounts
|
|
46
|
+
WHERE NOT EXISTS (
|
|
47
|
+
SELECT 1 FROM removed_accounts WHERE removed_accounts.id = accounts.id
|
|
48
|
+
)
|
|
49
|
+
`)
|
|
50
|
+
.all();
|
|
43
51
|
}
|
|
44
52
|
upsertAccountInternal(acc) {
|
|
45
53
|
this.db
|
|
@@ -62,14 +70,22 @@ export class KiroDatabase {
|
|
|
62
70
|
`)
|
|
63
71
|
.run(acc.id, acc.email, acc.authMethod, acc.region, acc.oidcRegion || null, acc.clientId || null, acc.clientSecret || null, acc.profileArn || null, acc.startUrl || null, acc.refreshToken, acc.accessToken, acc.expiresAt, acc.rateLimitResetTime || 0, acc.isHealthy ? 1 : 0, acc.unhealthyReason || null, acc.recoveryTime || null, acc.failCount || 0, acc.lastUsed || 0, acc.usedCount || 0, acc.limitCount || 0, acc.lastSync || 0);
|
|
64
72
|
}
|
|
73
|
+
isRemovedSync(id) {
|
|
74
|
+
return !!this.db.prepare('SELECT id FROM removed_accounts WHERE id = ?').get(id);
|
|
75
|
+
}
|
|
76
|
+
purgeRemovedAccountsSync() {
|
|
77
|
+
this.db.prepare('DELETE FROM accounts WHERE id IN (SELECT id FROM removed_accounts)').run();
|
|
78
|
+
}
|
|
65
79
|
async upsertAccount(acc) {
|
|
66
80
|
await withDatabaseLock(this.path, async () => {
|
|
67
81
|
const existing = this.getAccounts().map(this.rowToAccount);
|
|
68
82
|
const merged = mergeAccounts(existing, [acc]);
|
|
69
83
|
const deduplicated = deduplicateAccounts(merged);
|
|
84
|
+
const writable = deduplicated.filter((a) => !this.isRemovedSync(a.id));
|
|
70
85
|
this.db.exec('BEGIN TRANSACTION');
|
|
71
86
|
try {
|
|
72
|
-
|
|
87
|
+
this.purgeRemovedAccountsSync();
|
|
88
|
+
for (const account of writable) {
|
|
73
89
|
this.upsertAccountInternal(account);
|
|
74
90
|
}
|
|
75
91
|
this.db.exec('COMMIT');
|
|
@@ -85,9 +101,11 @@ export class KiroDatabase {
|
|
|
85
101
|
const existing = this.getAccounts().map(this.rowToAccount);
|
|
86
102
|
const merged = mergeAccounts(existing, accounts);
|
|
87
103
|
const deduplicated = deduplicateAccounts(merged);
|
|
104
|
+
const writable = deduplicated.filter((a) => !this.isRemovedSync(a.id));
|
|
88
105
|
this.db.exec('BEGIN TRANSACTION');
|
|
89
106
|
try {
|
|
90
|
-
|
|
107
|
+
this.purgeRemovedAccountsSync();
|
|
108
|
+
for (const account of writable) {
|
|
91
109
|
this.upsertAccountInternal(account);
|
|
92
110
|
}
|
|
93
111
|
this.db.exec('COMMIT');
|
|
@@ -103,6 +121,50 @@ export class KiroDatabase {
|
|
|
103
121
|
this.db.prepare('DELETE FROM accounts WHERE id = ?').run(id);
|
|
104
122
|
});
|
|
105
123
|
}
|
|
124
|
+
async removeAccountWithTombstone(id) {
|
|
125
|
+
await withDatabaseLock(this.path, async () => {
|
|
126
|
+
this.db.exec('BEGIN TRANSACTION');
|
|
127
|
+
try {
|
|
128
|
+
this.db.prepare('DELETE FROM accounts WHERE id = ?').run(id);
|
|
129
|
+
this.db
|
|
130
|
+
.prepare('INSERT OR REPLACE INTO removed_accounts (id, removed_at) VALUES (?, ?)')
|
|
131
|
+
.run(id, Date.now());
|
|
132
|
+
this.db.exec('COMMIT');
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
this.db.exec('ROLLBACK');
|
|
136
|
+
throw e;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
async cleanupSupersededIdentities(keepId, email, authMethod, profileArn) {
|
|
141
|
+
const supersededIds = [];
|
|
142
|
+
await withDatabaseLock(this.path, async () => {
|
|
143
|
+
this.db.exec('BEGIN TRANSACTION');
|
|
144
|
+
try {
|
|
145
|
+
const rows = this.db
|
|
146
|
+
.prepare('SELECT id FROM accounts WHERE email = ? AND auth_method = ? AND profile_arn IS ? AND id != ?')
|
|
147
|
+
.all(email, authMethod, profileArn ?? null, keepId);
|
|
148
|
+
for (const row of rows) {
|
|
149
|
+
if (typeof row === 'object' && row !== null && 'id' in row && typeof row.id === 'string')
|
|
150
|
+
supersededIds.push(row.id);
|
|
151
|
+
}
|
|
152
|
+
const deleteStmt = this.db.prepare('DELETE FROM accounts WHERE id = ?');
|
|
153
|
+
const tombstoneStmt = this.db.prepare('INSERT OR REPLACE INTO removed_accounts (id, removed_at) VALUES (?, ?)');
|
|
154
|
+
const removedAt = Date.now();
|
|
155
|
+
for (const id of supersededIds) {
|
|
156
|
+
deleteStmt.run(id);
|
|
157
|
+
tombstoneStmt.run(id, removedAt);
|
|
158
|
+
}
|
|
159
|
+
this.db.exec('COMMIT');
|
|
160
|
+
}
|
|
161
|
+
catch (e) {
|
|
162
|
+
this.db.exec('ROLLBACK');
|
|
163
|
+
throw e;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
return supersededIds;
|
|
167
|
+
}
|
|
106
168
|
async addRemovedAccount(id) {
|
|
107
169
|
await withDatabaseLock(this.path, async () => {
|
|
108
170
|
this.db
|
|
@@ -17,7 +17,6 @@ export async function* transformSdkStream(sdkResponse, model, conversationId) {
|
|
|
17
17
|
nextBlockIndex: 0,
|
|
18
18
|
stoppedBlocks: new Set()
|
|
19
19
|
};
|
|
20
|
-
let totalContent = '';
|
|
21
20
|
let textOnlyContent = '';
|
|
22
21
|
let outputTokens = 0;
|
|
23
22
|
let inputTokens = 0;
|
|
@@ -46,297 +45,287 @@ export async function* transformSdkStream(sdkResponse, model, conversationId) {
|
|
|
46
45
|
if (!eventStream) {
|
|
47
46
|
throw new Error('SDK response has no event stream');
|
|
48
47
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
48
|
+
for await (const event of eventStream) {
|
|
49
|
+
if (event.reasoningContentEvent?.text) {
|
|
50
|
+
const reasoningText = event.reasoningContentEvent.text;
|
|
51
|
+
if (reasoningClosed) {
|
|
52
|
+
// Defensive, normally unreached (probe: reasoning is contiguous-before-text).
|
|
53
|
+
// The stopped thinking index cannot be reused, so open a fresh one.
|
|
54
|
+
streamState.thinkingBlockIndex = null;
|
|
55
|
+
reasoningClosed = false;
|
|
56
|
+
}
|
|
57
|
+
reasoningStarted = true;
|
|
58
|
+
for (const ev of createThinkingDeltaEvents(reasoningText, streamState)) {
|
|
59
|
+
const _c = convertToOpenAI(ev, conversationId, model);
|
|
60
|
+
if (_c !== null)
|
|
61
|
+
yield _c;
|
|
62
|
+
}
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (event.assistantResponseEvent?.content) {
|
|
66
|
+
const text = event.assistantResponseEvent.content;
|
|
67
|
+
textOnlyContent += text;
|
|
68
|
+
if (reasoningStarted && !reasoningClosed) {
|
|
69
|
+
for (const ev of stopBlock(streamState.thinkingBlockIndex, streamState)) {
|
|
61
70
|
const _c = convertToOpenAI(ev, conversationId, model);
|
|
62
71
|
if (_c !== null)
|
|
63
72
|
yield _c;
|
|
64
73
|
}
|
|
65
|
-
|
|
74
|
+
reasoningClosed = true;
|
|
66
75
|
}
|
|
67
|
-
if (
|
|
68
|
-
const text
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
for (const ev of stopBlock(streamState.thinkingBlockIndex, streamState)) {
|
|
73
|
-
const _c = convertToOpenAI(ev, conversationId, model);
|
|
74
|
-
if (_c !== null)
|
|
75
|
-
yield _c;
|
|
76
|
-
}
|
|
77
|
-
reasoningClosed = true;
|
|
76
|
+
if (reasoningStarted) {
|
|
77
|
+
for (const ev of createTextDeltaEvents(text, streamState)) {
|
|
78
|
+
const _c = toChunk(ev);
|
|
79
|
+
if (_c !== null)
|
|
80
|
+
yield _c;
|
|
78
81
|
}
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!thinkingRequested) {
|
|
85
|
+
for (const ev of createTextDeltaEvents(text, streamState)) {
|
|
86
|
+
{
|
|
81
87
|
const _c = toChunk(ev);
|
|
82
88
|
if (_c !== null)
|
|
83
89
|
yield _c;
|
|
84
90
|
}
|
|
85
|
-
continue;
|
|
86
91
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
streamState.buffer += text;
|
|
95
|
+
const deltaEvents = [];
|
|
96
|
+
while (streamState.buffer.length > 0) {
|
|
97
|
+
if (!streamState.inThinking && !streamState.thinkingExtracted) {
|
|
98
|
+
const startPos = findRealTag(streamState.buffer, THINKING_START_TAG);
|
|
99
|
+
if (startPos !== -1) {
|
|
100
|
+
const before = streamState.buffer.slice(0, startPos);
|
|
101
|
+
if (before) {
|
|
102
|
+
deltaEvents.push(...createTextDeltaEvents(before, streamState));
|
|
93
103
|
}
|
|
104
|
+
streamState.buffer = streamState.buffer.slice(startPos + THINKING_START_TAG.length);
|
|
105
|
+
streamState.inThinking = true;
|
|
106
|
+
continue;
|
|
94
107
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (!streamState.inThinking && !streamState.thinkingExtracted) {
|
|
101
|
-
const startPos = findRealTag(streamState.buffer, THINKING_START_TAG);
|
|
102
|
-
if (startPos !== -1) {
|
|
103
|
-
const before = streamState.buffer.slice(0, startPos);
|
|
104
|
-
if (before) {
|
|
105
|
-
deltaEvents.push(...createTextDeltaEvents(before, streamState));
|
|
106
|
-
}
|
|
107
|
-
streamState.buffer = streamState.buffer.slice(startPos + THINKING_START_TAG.length);
|
|
108
|
-
streamState.inThinking = true;
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
const safeLen = Math.max(0, streamState.buffer.length - THINKING_START_TAG.length);
|
|
112
|
-
if (safeLen > 0) {
|
|
113
|
-
const safeText = streamState.buffer.slice(0, safeLen);
|
|
114
|
-
if (safeText) {
|
|
115
|
-
deltaEvents.push(...createTextDeltaEvents(safeText, streamState));
|
|
116
|
-
}
|
|
117
|
-
streamState.buffer = streamState.buffer.slice(safeLen);
|
|
108
|
+
const safeLen = Math.max(0, streamState.buffer.length - THINKING_START_TAG.length);
|
|
109
|
+
if (safeLen > 0) {
|
|
110
|
+
const safeText = streamState.buffer.slice(0, safeLen);
|
|
111
|
+
if (safeText) {
|
|
112
|
+
deltaEvents.push(...createTextDeltaEvents(safeText, streamState));
|
|
118
113
|
}
|
|
119
|
-
|
|
114
|
+
streamState.buffer = streamState.buffer.slice(safeLen);
|
|
120
115
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
streamState.inThinking = false;
|
|
130
|
-
streamState.thinkingExtracted = true;
|
|
131
|
-
deltaEvents.push(...createThinkingDeltaEvents('', streamState));
|
|
132
|
-
deltaEvents.push(...stopBlock(streamState.thinkingBlockIndex, streamState));
|
|
133
|
-
if (streamState.buffer.startsWith('\n\n')) {
|
|
134
|
-
streamState.buffer = streamState.buffer.slice(2);
|
|
135
|
-
}
|
|
136
|
-
continue;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
if (streamState.inThinking) {
|
|
119
|
+
const endPos = findRealTag(streamState.buffer, THINKING_END_TAG);
|
|
120
|
+
if (endPos !== -1) {
|
|
121
|
+
const thinkingPart = streamState.buffer.slice(0, endPos);
|
|
122
|
+
if (thinkingPart) {
|
|
123
|
+
deltaEvents.push(...createThinkingDeltaEvents(thinkingPart, streamState));
|
|
137
124
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
streamState.buffer = streamState.buffer.slice(
|
|
125
|
+
streamState.buffer = streamState.buffer.slice(endPos + THINKING_END_TAG.length);
|
|
126
|
+
streamState.inThinking = false;
|
|
127
|
+
streamState.thinkingExtracted = true;
|
|
128
|
+
deltaEvents.push(...createThinkingDeltaEvents('', streamState));
|
|
129
|
+
deltaEvents.push(...stopBlock(streamState.thinkingBlockIndex, streamState));
|
|
130
|
+
if (streamState.buffer.startsWith('\n\n')) {
|
|
131
|
+
streamState.buffer = streamState.buffer.slice(2);
|
|
145
132
|
}
|
|
146
|
-
|
|
133
|
+
continue;
|
|
147
134
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
streamState.buffer
|
|
151
|
-
if (
|
|
152
|
-
deltaEvents.push(...
|
|
135
|
+
const safeLen = Math.max(0, streamState.buffer.length - THINKING_END_TAG.length);
|
|
136
|
+
if (safeLen > 0) {
|
|
137
|
+
const safeThinking = streamState.buffer.slice(0, safeLen);
|
|
138
|
+
if (safeThinking) {
|
|
139
|
+
deltaEvents.push(...createThinkingDeltaEvents(safeThinking, streamState));
|
|
153
140
|
}
|
|
154
|
-
|
|
141
|
+
streamState.buffer = streamState.buffer.slice(safeLen);
|
|
155
142
|
}
|
|
143
|
+
break;
|
|
156
144
|
}
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
145
|
+
if (streamState.thinkingExtracted) {
|
|
146
|
+
const rest = streamState.buffer;
|
|
147
|
+
streamState.buffer = '';
|
|
148
|
+
if (rest) {
|
|
149
|
+
deltaEvents.push(...createTextDeltaEvents(rest, streamState));
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
161
152
|
}
|
|
162
153
|
}
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
toolUseId: tc.toolUseId,
|
|
178
|
-
name: tc.name,
|
|
179
|
-
input: tc.input || ''
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
if (tc.stop && currentToolCall) {
|
|
154
|
+
for (const ev of deltaEvents) {
|
|
155
|
+
const chunk = toChunk(ev);
|
|
156
|
+
if (chunk !== null)
|
|
157
|
+
yield chunk;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else if (event.toolUseEvent) {
|
|
161
|
+
const tc = event.toolUseEvent;
|
|
162
|
+
if (tc.name && tc.toolUseId) {
|
|
163
|
+
if (currentToolCall && currentToolCall.toolUseId === tc.toolUseId) {
|
|
164
|
+
currentToolCall.input += tc.input || '';
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
if (currentToolCall)
|
|
183
168
|
toolCalls.push(currentToolCall);
|
|
184
|
-
|
|
185
|
-
|
|
169
|
+
currentToolCall = {
|
|
170
|
+
toolUseId: tc.toolUseId,
|
|
171
|
+
name: tc.name,
|
|
172
|
+
input: tc.input || ''
|
|
173
|
+
};
|
|
186
174
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
contextUsagePercentage = event.metadataEvent.contextUsagePercentage;
|
|
175
|
+
if (tc.stop && currentToolCall) {
|
|
176
|
+
toolCalls.push(currentToolCall);
|
|
177
|
+
currentToolCall = null;
|
|
191
178
|
}
|
|
192
179
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
180
|
+
}
|
|
181
|
+
else if (event.metadataEvent) {
|
|
182
|
+
if (event.metadataEvent.contextUsagePercentage) {
|
|
183
|
+
contextUsagePercentage = event.metadataEvent.contextUsagePercentage;
|
|
198
184
|
}
|
|
199
185
|
}
|
|
200
|
-
if (
|
|
201
|
-
|
|
202
|
-
|
|
186
|
+
else if (event.contextUsageEvent) {
|
|
187
|
+
const cue = event.contextUsageEvent;
|
|
188
|
+
if (cue.contextUsagePercentage) {
|
|
189
|
+
contextUsagePercentage = cue.contextUsagePercentage;
|
|
190
|
+
}
|
|
203
191
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
192
|
+
}
|
|
193
|
+
if (currentToolCall) {
|
|
194
|
+
toolCalls.push(currentToolCall);
|
|
195
|
+
currentToolCall = null;
|
|
196
|
+
}
|
|
197
|
+
// Reasoning-only responses (reasoning but no reply text): close the thinking block.
|
|
198
|
+
if (reasoningStarted && !reasoningClosed) {
|
|
199
|
+
for (const ev of stopBlock(streamState.thinkingBlockIndex, streamState)) {
|
|
200
|
+
const _c = convertToOpenAI(ev, conversationId, model);
|
|
201
|
+
if (_c !== null)
|
|
202
|
+
yield _c;
|
|
203
|
+
}
|
|
204
|
+
reasoningClosed = true;
|
|
205
|
+
}
|
|
206
|
+
if (thinkingRequested && streamState.buffer) {
|
|
207
|
+
if (streamState.inThinking) {
|
|
208
|
+
for (const ev of createThinkingDeltaEvents(streamState.buffer, streamState)) {
|
|
207
209
|
const _c = convertToOpenAI(ev, conversationId, model);
|
|
208
210
|
if (_c !== null)
|
|
209
211
|
yield _c;
|
|
210
212
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const _c = convertToOpenAI(ev, conversationId, model);
|
|
217
|
-
if (_c !== null)
|
|
218
|
-
yield _c;
|
|
219
|
-
}
|
|
220
|
-
streamState.buffer = '';
|
|
221
|
-
for (const ev of createThinkingDeltaEvents('', streamState)) {
|
|
222
|
-
const _c = convertToOpenAI(ev, conversationId, model);
|
|
223
|
-
if (_c !== null)
|
|
224
|
-
yield _c;
|
|
225
|
-
}
|
|
226
|
-
for (const ev of stopBlock(streamState.thinkingBlockIndex, streamState)) {
|
|
227
|
-
const _c = convertToOpenAI(ev, conversationId, model);
|
|
228
|
-
if (_c !== null)
|
|
229
|
-
yield _c;
|
|
230
|
-
}
|
|
213
|
+
streamState.buffer = '';
|
|
214
|
+
for (const ev of createThinkingDeltaEvents('', streamState)) {
|
|
215
|
+
const _c = convertToOpenAI(ev, conversationId, model);
|
|
216
|
+
if (_c !== null)
|
|
217
|
+
yield _c;
|
|
231
218
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
yield _c;
|
|
237
|
-
}
|
|
238
|
-
streamState.buffer = '';
|
|
219
|
+
for (const ev of stopBlock(streamState.thinkingBlockIndex, streamState)) {
|
|
220
|
+
const _c = convertToOpenAI(ev, conversationId, model);
|
|
221
|
+
if (_c !== null)
|
|
222
|
+
yield _c;
|
|
239
223
|
}
|
|
240
224
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const _c = convertToOpenAI(ev, conversationId, model);
|
|
225
|
+
else {
|
|
226
|
+
for (const ev of createTextDeltaEvents(streamState.buffer, streamState)) {
|
|
227
|
+
const _c = toChunk(ev);
|
|
245
228
|
if (_c !== null)
|
|
246
229
|
yield _c;
|
|
247
230
|
}
|
|
231
|
+
streamState.buffer = '';
|
|
248
232
|
}
|
|
249
|
-
|
|
233
|
+
}
|
|
234
|
+
const { toolCalls: dialectToolCalls, remainderText } = dialectGate.finalize();
|
|
235
|
+
if (remainderText) {
|
|
236
|
+
for (const ev of createTextDeltaEvents(remainderText, streamState)) {
|
|
250
237
|
const _c = convertToOpenAI(ev, conversationId, model);
|
|
251
238
|
if (_c !== null)
|
|
252
239
|
yield _c;
|
|
253
240
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
241
|
+
}
|
|
242
|
+
for (const ev of stopBlock(streamState.textBlockIndex, streamState)) {
|
|
243
|
+
const _c = convertToOpenAI(ev, conversationId, model);
|
|
244
|
+
if (_c !== null)
|
|
245
|
+
yield _c;
|
|
246
|
+
}
|
|
247
|
+
if (dialectToolCalls.length > 0) {
|
|
248
|
+
for (const btc of dialectToolCalls) {
|
|
249
|
+
toolCalls.push({
|
|
250
|
+
toolUseId: btc.toolUseId,
|
|
251
|
+
name: btc.name,
|
|
252
|
+
input: typeof btc.input === 'string' ? btc.input : JSON.stringify(btc.input)
|
|
253
|
+
});
|
|
262
254
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
255
|
+
}
|
|
256
|
+
if (toolCalls.length > 0) {
|
|
257
|
+
// OpenAI tool_calls[].index must be the tool call's own 0-based ordinal,
|
|
258
|
+
// NOT the Anthropic content_block global index (offset by reasoning/text
|
|
259
|
+
// blocks) — a global index misaligns the AI-SDK accumulator and drops the call.
|
|
260
|
+
for (let i = 0; i < toolCalls.length; i++) {
|
|
261
|
+
const tc = toolCalls[i];
|
|
262
|
+
if (!tc)
|
|
263
|
+
continue;
|
|
264
|
+
const blockIndex = i;
|
|
265
|
+
{
|
|
266
|
+
const _c = convertToOpenAI({
|
|
267
|
+
type: 'content_block_start',
|
|
268
|
+
index: blockIndex,
|
|
269
|
+
content_block: {
|
|
270
|
+
type: 'tool_use',
|
|
271
|
+
id: tc.toolUseId,
|
|
272
|
+
name: tc.name,
|
|
273
|
+
input: {}
|
|
274
|
+
}
|
|
275
|
+
}, conversationId, model);
|
|
276
|
+
if (_c !== null)
|
|
277
|
+
yield _c;
|
|
278
|
+
}
|
|
279
|
+
let inputJson;
|
|
280
|
+
try {
|
|
281
|
+
const parsed = JSON.parse(tc.input);
|
|
282
|
+
inputJson = JSON.stringify(parsed);
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
inputJson = tc.input;
|
|
286
|
+
}
|
|
287
|
+
{
|
|
288
|
+
const _c = convertToOpenAI({
|
|
289
|
+
type: 'content_block_delta',
|
|
290
|
+
index: blockIndex,
|
|
291
|
+
delta: {
|
|
292
|
+
type: 'input_json_delta',
|
|
293
|
+
partial_json: inputJson
|
|
294
|
+
}
|
|
295
|
+
}, conversationId, model);
|
|
296
|
+
if (_c !== null)
|
|
297
|
+
yield _c;
|
|
298
|
+
}
|
|
299
|
+
{
|
|
300
|
+
const _c = convertToOpenAI({ type: 'content_block_stop', index: blockIndex }, conversationId, model);
|
|
301
|
+
if (_c !== null)
|
|
302
|
+
yield _c;
|
|
311
303
|
}
|
|
312
304
|
}
|
|
313
|
-
outputTokens = estimateTokens(textOnlyContent);
|
|
314
|
-
if (contextUsagePercentage !== null && contextUsagePercentage > 0) {
|
|
315
|
-
const contextWindow = getContextWindowSize(model);
|
|
316
|
-
const totalTokens = Math.round((contextWindow * contextUsagePercentage) / 100);
|
|
317
|
-
inputTokens = Math.max(0, totalTokens - outputTokens);
|
|
318
|
-
}
|
|
319
|
-
{
|
|
320
|
-
const _c = convertToOpenAI({
|
|
321
|
-
type: 'message_delta',
|
|
322
|
-
delta: { stop_reason: toolCalls.length > 0 ? 'tool_use' : 'end_turn' },
|
|
323
|
-
usage: {
|
|
324
|
-
input_tokens: inputTokens,
|
|
325
|
-
output_tokens: outputTokens,
|
|
326
|
-
cache_creation_input_tokens: 0,
|
|
327
|
-
cache_read_input_tokens: 0
|
|
328
|
-
}
|
|
329
|
-
}, conversationId, model);
|
|
330
|
-
if (_c !== null)
|
|
331
|
-
yield _c;
|
|
332
|
-
}
|
|
333
|
-
{
|
|
334
|
-
const _c = convertToOpenAI({ type: 'message_stop' }, conversationId, model);
|
|
335
|
-
if (_c !== null)
|
|
336
|
-
yield _c;
|
|
337
|
-
}
|
|
338
305
|
}
|
|
339
|
-
|
|
340
|
-
|
|
306
|
+
outputTokens = estimateTokens(textOnlyContent);
|
|
307
|
+
if (contextUsagePercentage !== null && contextUsagePercentage > 0) {
|
|
308
|
+
const contextWindow = getContextWindowSize(model);
|
|
309
|
+
const totalTokens = Math.round((contextWindow * contextUsagePercentage) / 100);
|
|
310
|
+
inputTokens = Math.max(0, totalTokens - outputTokens);
|
|
311
|
+
}
|
|
312
|
+
{
|
|
313
|
+
const _c = convertToOpenAI({
|
|
314
|
+
type: 'message_delta',
|
|
315
|
+
delta: { stop_reason: toolCalls.length > 0 ? 'tool_use' : 'end_turn' },
|
|
316
|
+
usage: {
|
|
317
|
+
input_tokens: inputTokens,
|
|
318
|
+
output_tokens: outputTokens,
|
|
319
|
+
cache_creation_input_tokens: 0,
|
|
320
|
+
cache_read_input_tokens: 0
|
|
321
|
+
}
|
|
322
|
+
}, conversationId, model);
|
|
323
|
+
if (_c !== null)
|
|
324
|
+
yield _c;
|
|
325
|
+
}
|
|
326
|
+
{
|
|
327
|
+
const _c = convertToOpenAI({ type: 'message_stop' }, conversationId, model);
|
|
328
|
+
if (_c !== null)
|
|
329
|
+
yield _c;
|
|
341
330
|
}
|
|
342
331
|
}
|