@gajae-code/ai 0.18.5 → 0.18.7
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/CHANGELOG.md +73 -0
- package/dist/types/auth-storage.d.ts +8 -1
- package/dist/types/providers/cursor.d.ts +6 -3
- package/dist/types/providers/kiro-api-key.d.ts +1 -0
- package/dist/types/providers/openai-codex-responses.d.ts +3 -0
- package/dist/types/types.d.ts +16 -1
- package/dist/types/utils/fallback-transport.d.ts +6 -0
- package/dist/types/utils/idle-iterator.d.ts +2 -0
- package/dist/types/utils/oauth/glm-zcode.d.ts +5 -0
- package/dist/types/utils/oauth/index.d.ts +4 -0
- package/package.json +3 -3
- package/src/auth-storage.ts +115 -39
- package/src/model-thinking.ts +8 -0
- package/src/providers/aws-eventstream.ts +2 -1
- package/src/providers/cursor.d.ts +6 -3
- package/src/providers/cursor.ts +73 -64
- package/src/providers/google-gemini-cli.ts +49 -3
- package/src/providers/kiro-api-key.d.ts +1 -0
- package/src/providers/kiro-api-key.ts +1 -1
- package/src/providers/kiro-codewhisperer.ts +164 -68
- package/src/providers/openai-codex-responses.d.ts +3 -0
- package/src/providers/openai-codex-responses.ts +589 -41
- package/src/providers/openai-opencodex-responses.ts +6 -1
- package/src/providers/openai-responses.ts +1 -1
- package/src/providers/register-builtins.ts +53 -5
- package/src/stream.ts +20 -0
- package/src/types.d.ts +16 -1
- package/src/types.ts +19 -0
- package/src/utils/fallback-transport.d.ts +6 -0
- package/src/utils/fallback-transport.ts +6 -0
- package/src/utils/idle-iterator.d.ts +2 -0
- package/src/utils/idle-iterator.ts +13 -3
- package/src/utils/oauth/glm-zcode.d.ts +5 -0
- package/src/utils/oauth/glm-zcode.ts +8 -1
- package/src/utils/oauth/index.ts +11 -1
- package/src/utils/validation.ts +5 -0
|
@@ -26,11 +26,11 @@ import type {
|
|
|
26
26
|
ToolResultMessage,
|
|
27
27
|
} from "../types";
|
|
28
28
|
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
29
|
-
import { transportFailureFacts } from "../utils/fallback-transport";
|
|
29
|
+
import { PROVIDER_PROTOCOL_MISMATCH_ERROR_CODE, transportFailureFacts } from "../utils/fallback-transport";
|
|
30
30
|
import { withHttpStatus } from "../utils/http-inspector";
|
|
31
31
|
import { captureUnicodeEscapeEvidence } from "../utils/json-parse";
|
|
32
32
|
import { decodeEventStream } from "./aws-eventstream";
|
|
33
|
-
import { isKiroApiKey, streamKiroApiKey, toKiroModelId } from "./kiro-api-key";
|
|
33
|
+
import { isKiroApiKey, sanitizeKiroError, streamKiroApiKey, toKiroModelId } from "./kiro-api-key";
|
|
34
34
|
|
|
35
35
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
36
36
|
// Provider options
|
|
@@ -74,11 +74,11 @@ interface WireUserMessage {
|
|
|
74
74
|
content: string;
|
|
75
75
|
modelId?: string;
|
|
76
76
|
userInputMessageContext?: {
|
|
77
|
-
tools?:
|
|
77
|
+
tools?: WireToolSpec[];
|
|
78
78
|
toolResults?: WireToolResult[];
|
|
79
79
|
editorStateContext?: Record<string, unknown>;
|
|
80
80
|
};
|
|
81
|
-
origin
|
|
81
|
+
origin: typeof KIRO_ORIGIN;
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -108,25 +108,19 @@ interface GenerateAssistantResponseRequest {
|
|
|
108
108
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
109
109
|
|
|
110
110
|
interface AssistantResponseEvent {
|
|
111
|
-
|
|
112
|
-
content?: string;
|
|
113
|
-
};
|
|
111
|
+
content?: string;
|
|
114
112
|
}
|
|
115
113
|
|
|
116
114
|
interface ToolUseEventPayload {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
stop?: { stopReason?: string };
|
|
122
|
-
};
|
|
115
|
+
toolUseId?: string;
|
|
116
|
+
name?: string;
|
|
117
|
+
input?: string;
|
|
118
|
+
stop?: boolean;
|
|
123
119
|
}
|
|
124
120
|
|
|
125
121
|
interface MessageMetadataEvent {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
utteranceId?: string;
|
|
129
|
-
};
|
|
122
|
+
conversationId?: string;
|
|
123
|
+
utteranceId?: string;
|
|
130
124
|
}
|
|
131
125
|
|
|
132
126
|
interface ErrorPayload {
|
|
@@ -142,6 +136,12 @@ interface ErrorPayload {
|
|
|
142
136
|
|
|
143
137
|
const DEFAULT_REGION = "us-east-1";
|
|
144
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Without `origin` the service ignores `modelId` and routes every request to
|
|
141
|
+
* `auto`; with it, response frames report the requested model.
|
|
142
|
+
*/
|
|
143
|
+
const KIRO_ORIGIN = "AI_EDITOR";
|
|
144
|
+
|
|
145
145
|
type Block = (TextContent | ToolCall) & { index?: number; partialJson?: string };
|
|
146
146
|
|
|
147
147
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -163,6 +163,8 @@ export const streamKiroCodeWhisperer: StreamFunction<"kiro-codewhisperer-stream"
|
|
|
163
163
|
(async () => {
|
|
164
164
|
const startTime = Date.now();
|
|
165
165
|
let firstTokenTime: number | undefined;
|
|
166
|
+
// Accumulator for streaming tool input fragments, keyed by toolUseId
|
|
167
|
+
const toolInputAccumulator = new Map<string, { name: string; input: string }>();
|
|
166
168
|
|
|
167
169
|
const output: AssistantMessage = {
|
|
168
170
|
role: "assistant",
|
|
@@ -184,6 +186,7 @@ export const streamKiroCodeWhisperer: StreamFunction<"kiro-codewhisperer-stream"
|
|
|
184
186
|
|
|
185
187
|
const blocks = output.content as Block[];
|
|
186
188
|
const region = options.region ?? $env.KIRO_REGION ?? $env.AWS_REGION ?? $env.AWS_DEFAULT_REGION ?? DEFAULT_REGION;
|
|
189
|
+
let started = false; // Track whether start event has been emitted
|
|
187
190
|
|
|
188
191
|
try {
|
|
189
192
|
assertAwsRegionLabel(region);
|
|
@@ -216,38 +219,53 @@ export const streamKiroCodeWhisperer: StreamFunction<"kiro-codewhisperer-stream"
|
|
|
216
219
|
const bodyText = JSON.stringify(requestBody);
|
|
217
220
|
const body = new TextEncoder().encode(bodyText);
|
|
218
221
|
const requestHeaders: Record<string, string> = {
|
|
219
|
-
"
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
"
|
|
222
|
+
"Content-Type": "application/x-amz-json-1.0",
|
|
223
|
+
Accept: "application/vnd.amazon.eventstream",
|
|
224
|
+
Authorization: `Bearer ${bearerToken}`,
|
|
225
|
+
"X-Amz-Target": "AmazonCodeWhispererStreamingService.GenerateAssistantResponse",
|
|
223
226
|
};
|
|
224
227
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Merge user-provided headers
|
|
228
|
+
// Merge user-provided headers (case-insensitively)
|
|
229
|
+
const headersList = new Headers(requestHeaders);
|
|
230
230
|
if (options.headers) {
|
|
231
|
-
|
|
231
|
+
const userHeaders = new Headers(options.headers);
|
|
232
|
+
userHeaders.forEach((value, key) => {
|
|
233
|
+
headersList.set(key, value);
|
|
234
|
+
});
|
|
232
235
|
}
|
|
233
236
|
|
|
234
237
|
const response = await fetch(url, {
|
|
235
238
|
method: "POST",
|
|
236
|
-
headers:
|
|
239
|
+
headers: headersList,
|
|
237
240
|
body,
|
|
238
241
|
redirect: "error",
|
|
239
242
|
signal: options.signal,
|
|
240
243
|
});
|
|
241
244
|
|
|
242
245
|
if (!response.ok) {
|
|
243
|
-
const errBody = await response
|
|
246
|
+
const errBody = await readBodyPrefix(response);
|
|
244
247
|
throw withHttpStatus(
|
|
245
|
-
new Error(`Kiro CodeWhisperer HTTP ${response.status}: ${errBody
|
|
248
|
+
new Error(sanitizeKiroError(`Kiro CodeWhisperer HTTP ${response.status}: ${errBody}`, bearerToken)),
|
|
246
249
|
response.status,
|
|
247
250
|
);
|
|
248
251
|
}
|
|
249
252
|
|
|
250
|
-
|
|
253
|
+
// Verify content-type is eventstream; if not, read and report the actual error
|
|
254
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
255
|
+
const mediaType = contentType.split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
256
|
+
if (!response.body || mediaType !== "application/vnd.amazon.eventstream") {
|
|
257
|
+
const errBody = await readBodyPrefix(response);
|
|
258
|
+
// Deliberately no HTTP status metadata and no "HTTP <code>" wording: a 2xx
|
|
259
|
+
// protocol mismatch is deterministic, and a status would materialize
|
|
260
|
+
// transport-failure facts that session fallback retries.
|
|
261
|
+
// Sanitize each untrusted part on its own so the fixed prefix does not eat
|
|
262
|
+
// into the body's 1000-character diagnostic budget.
|
|
263
|
+
const safeContentType = contentType ? sanitizeKiroError(contentType, bearerToken) : "";
|
|
264
|
+
const safeBody = errBody ? sanitizeKiroError(errBody, bearerToken) : "";
|
|
265
|
+
throw new KiroNonEventStreamError(
|
|
266
|
+
`Kiro CodeWhisperer returned a non-eventstream ${response.status} response (${safeContentType}): ${safeBody}`,
|
|
267
|
+
);
|
|
268
|
+
}
|
|
251
269
|
|
|
252
270
|
// Decode eventstream
|
|
253
271
|
for await (const message of decodeEventStream(response.body)) {
|
|
@@ -279,11 +297,12 @@ export const streamKiroCodeWhisperer: StreamFunction<"kiro-codewhisperer-stream"
|
|
|
279
297
|
switch (eventType) {
|
|
280
298
|
case "assistantResponseEvent": {
|
|
281
299
|
const ev = payload as AssistantResponseEvent;
|
|
282
|
-
const content = ev.
|
|
300
|
+
const content = ev.content;
|
|
283
301
|
if (content) {
|
|
284
302
|
if (!firstTokenTime) firstTokenTime = Date.now();
|
|
285
|
-
if (
|
|
303
|
+
if (!started) {
|
|
286
304
|
stream.push({ type: "start", partial: output });
|
|
305
|
+
started = true;
|
|
287
306
|
}
|
|
288
307
|
handleTextDelta(content, blocks, output, stream);
|
|
289
308
|
}
|
|
@@ -292,16 +311,21 @@ export const streamKiroCodeWhisperer: StreamFunction<"kiro-codewhisperer-stream"
|
|
|
292
311
|
case "toolUseEvent": {
|
|
293
312
|
const ev = payload as ToolUseEventPayload;
|
|
294
313
|
if (!firstTokenTime) firstTokenTime = Date.now();
|
|
295
|
-
if (
|
|
314
|
+
if (!started) {
|
|
296
315
|
stream.push({ type: "start", partial: output });
|
|
316
|
+
started = true;
|
|
317
|
+
}
|
|
318
|
+
handleToolUseEvent(ev, blocks, output, stream, toolInputAccumulator);
|
|
319
|
+
// Clear accumulator for completed tool
|
|
320
|
+
if (ev.stop) {
|
|
321
|
+
toolInputAccumulator.delete(ev.toolUseId ?? "");
|
|
297
322
|
}
|
|
298
|
-
handleToolUseEvent(ev, blocks, output, stream);
|
|
299
323
|
break;
|
|
300
324
|
}
|
|
301
325
|
case "messageMetadataEvent": {
|
|
302
326
|
const ev = payload as MessageMetadataEvent;
|
|
303
|
-
if (ev.
|
|
304
|
-
output.responseId = ev.
|
|
327
|
+
if (ev.conversationId) {
|
|
328
|
+
output.responseId = ev.conversationId;
|
|
305
329
|
}
|
|
306
330
|
break;
|
|
307
331
|
}
|
|
@@ -328,6 +352,12 @@ export const streamKiroCodeWhisperer: StreamFunction<"kiro-codewhisperer-stream"
|
|
|
328
352
|
|
|
329
353
|
if (options.signal?.aborted) throw new Error("Request was aborted");
|
|
330
354
|
|
|
355
|
+
// Reject EOF while tool input is still being accumulated
|
|
356
|
+
if (toolInputAccumulator.size > 0) {
|
|
357
|
+
const unfinishedIds = Array.from(toolInputAccumulator.keys()).join(", ");
|
|
358
|
+
throw new Error(`Kiro CodeWhisperer stream ended with incomplete tool calls: ${unfinishedIds}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
331
361
|
// Finalize blocks
|
|
332
362
|
for (const block of blocks) {
|
|
333
363
|
delete block.index;
|
|
@@ -348,8 +378,10 @@ export const streamKiroCodeWhisperer: StreamFunction<"kiro-codewhisperer-stream"
|
|
|
348
378
|
delete (block as Block).partialJson;
|
|
349
379
|
}
|
|
350
380
|
output.stopReason = options.signal?.aborted ? "aborted" : "error";
|
|
351
|
-
|
|
381
|
+
// The non-eventstream diagnostic embeds untrusted body text; never parse a status out of it.
|
|
382
|
+
output.errorStatus = error instanceof KiroNonEventStreamError ? undefined : extractHttpStatusFromError(error);
|
|
352
383
|
output.transportFailure = transportFailureFacts(error);
|
|
384
|
+
if (error instanceof KiroNonEventStreamError) output.errorCode = PROVIDER_PROTOCOL_MISMATCH_ERROR_CODE;
|
|
353
385
|
const baseMessage = error instanceof Error ? error.message : JSON.stringify(error);
|
|
354
386
|
output.errorMessage = baseMessage;
|
|
355
387
|
output.duration = Date.now() - startTime;
|
|
@@ -386,7 +418,20 @@ function buildConversationState(
|
|
|
386
418
|
const history: WireHistoryMessage[] = [];
|
|
387
419
|
const systemPrompt = context.systemPrompt?.join("\n") ?? "";
|
|
388
420
|
|
|
389
|
-
|
|
421
|
+
// Parallel tool calls end the conversation with several consecutive toolResult
|
|
422
|
+
// messages. They all answer the last assistant turn, so they must travel
|
|
423
|
+
// together in currentMessage; leaving the earlier ones as a separate history
|
|
424
|
+
// entry is rejected upstream with TOOL_USE_RESULT_MISMATCH.
|
|
425
|
+
let currentStart = messages.length - 1;
|
|
426
|
+
while (
|
|
427
|
+
currentStart > 0 &&
|
|
428
|
+
messages[currentStart].role === "toolResult" &&
|
|
429
|
+
messages[currentStart - 1].role === "toolResult"
|
|
430
|
+
) {
|
|
431
|
+
currentStart--;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
for (let i = 0; i < currentStart; i++) {
|
|
390
435
|
const msg = messages[i];
|
|
391
436
|
const previous = history[history.length - 1];
|
|
392
437
|
// Parallel tool calls produce consecutive toolResult messages; they answer one
|
|
@@ -406,18 +451,21 @@ function buildConversationState(
|
|
|
406
451
|
history.push(convertToWireMessage(msg, modelId, i === 0 ? systemPrompt : undefined));
|
|
407
452
|
}
|
|
408
453
|
|
|
409
|
-
// Convert the last message as currentMessage
|
|
454
|
+
// Convert the last message as currentMessage, carrying every trailing parallel result.
|
|
410
455
|
const lastMsg = messages[messages.length - 1];
|
|
411
456
|
const currentMessage = convertToWireUserMessage(lastMsg, modelId, systemPrompt);
|
|
457
|
+
if (currentStart < messages.length - 1) {
|
|
458
|
+
currentMessage.userInputMessage.userInputMessageContext = {
|
|
459
|
+
toolResults: messages.slice(currentStart).map(msg => convertToolResult(msg as ToolResultMessage)),
|
|
460
|
+
};
|
|
461
|
+
}
|
|
412
462
|
|
|
413
463
|
// Add tools to the current message context
|
|
414
464
|
if (context.tools && context.tools.length > 0) {
|
|
415
465
|
if (!currentMessage.userInputMessage.userInputMessageContext) {
|
|
416
466
|
currentMessage.userInputMessage.userInputMessageContext = {};
|
|
417
467
|
}
|
|
418
|
-
currentMessage.userInputMessage.userInputMessageContext.tools =
|
|
419
|
-
tools: convertTools(context.tools),
|
|
420
|
-
};
|
|
468
|
+
currentMessage.userInputMessage.userInputMessageContext.tools = convertTools(context.tools);
|
|
421
469
|
}
|
|
422
470
|
|
|
423
471
|
return {
|
|
@@ -480,6 +528,7 @@ function convertToWireUserMessage(
|
|
|
480
528
|
userInputMessage: {
|
|
481
529
|
content,
|
|
482
530
|
modelId,
|
|
531
|
+
origin: KIRO_ORIGIN,
|
|
483
532
|
},
|
|
484
533
|
};
|
|
485
534
|
|
|
@@ -559,50 +608,97 @@ function handleToolUseEvent(
|
|
|
559
608
|
blocks: Block[],
|
|
560
609
|
output: AssistantMessage,
|
|
561
610
|
stream: AssistantMessageEventStream,
|
|
611
|
+
accumulator: Map<string, { name: string; input: string }>,
|
|
562
612
|
): void {
|
|
563
|
-
const
|
|
564
|
-
|
|
613
|
+
const name = ev.name ?? "";
|
|
614
|
+
const input = ev.input ?? "";
|
|
615
|
+
|
|
616
|
+
// Determine the tool ID: use provided ID, or find the currently active tool call
|
|
617
|
+
let toolUseId = ev.toolUseId;
|
|
618
|
+
if (!toolUseId) {
|
|
619
|
+
// ID-less fragment: associate with the currently active tool call
|
|
620
|
+
// Find the last tool call being accumulated (the most recently started tool)
|
|
621
|
+
const keys = Array.from(accumulator.keys());
|
|
622
|
+
if (keys.length > 0) {
|
|
623
|
+
toolUseId = keys[keys.length - 1];
|
|
624
|
+
} else {
|
|
625
|
+
// No active tool calls; create a placeholder entry (should rarely happen)
|
|
626
|
+
toolUseId = "";
|
|
627
|
+
}
|
|
628
|
+
}
|
|
565
629
|
|
|
566
|
-
|
|
567
|
-
|
|
630
|
+
// Accumulate input fragments per toolUseId
|
|
631
|
+
let accumulated = accumulator.get(toolUseId);
|
|
632
|
+
if (!accumulated) {
|
|
633
|
+
accumulated = { name, input: "" };
|
|
634
|
+
accumulator.set(toolUseId, accumulated);
|
|
635
|
+
}
|
|
636
|
+
accumulated.input += input;
|
|
637
|
+
|
|
638
|
+
// Update name if provided and not yet set
|
|
639
|
+
if (name && !accumulated.name) {
|
|
640
|
+
accumulated.name = name;
|
|
641
|
+
}
|
|
568
642
|
|
|
569
|
-
//
|
|
570
|
-
if (
|
|
571
|
-
const inputStr =
|
|
643
|
+
// Emit toolcall_end only when we have a stop signal
|
|
644
|
+
if (ev.stop) {
|
|
645
|
+
const inputStr = accumulated.input;
|
|
572
646
|
const toolCall: ToolCall = {
|
|
573
647
|
type: "toolCall",
|
|
574
648
|
id: toolUseId,
|
|
575
|
-
name,
|
|
649
|
+
name: accumulated.name,
|
|
576
650
|
arguments: safeParseJson(inputStr) as Record<string, any>,
|
|
577
651
|
};
|
|
578
|
-
|
|
652
|
+
captureUnicodeEscapeEvidence(toolCall, inputStr);
|
|
579
653
|
|
|
580
654
|
const newBlock: Block = { ...toolCall, index: blocks.length };
|
|
581
|
-
|
|
655
|
+
captureUnicodeEscapeEvidence(newBlock, inputStr);
|
|
582
656
|
blocks.push(newBlock);
|
|
583
657
|
stream.push({ type: "toolcall_end", contentIndex: newBlock.index!, toolCall, partial: output });
|
|
584
|
-
|
|
658
|
+
accumulator.delete(toolUseId);
|
|
585
659
|
}
|
|
586
|
-
|
|
587
|
-
// Otherwise, accumulate partial input (if the service streams it in chunks)
|
|
588
|
-
// The published model does not document chunked tool input for CodeWhisperer,
|
|
589
|
-
// so this path handles the case defensively but expects complete input per event.
|
|
590
|
-
const toolCall: ToolCall = {
|
|
591
|
-
type: "toolCall",
|
|
592
|
-
id: toolUseId,
|
|
593
|
-
name,
|
|
594
|
-
arguments: {},
|
|
595
|
-
};
|
|
596
|
-
|
|
597
|
-
const newBlock: Block = { ...toolCall, index: blocks.length };
|
|
598
|
-
blocks.push(newBlock);
|
|
599
|
-
stream.push({ type: "toolcall_end", contentIndex: newBlock.index!, toolCall, partial: output });
|
|
600
660
|
}
|
|
601
661
|
|
|
602
662
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
603
663
|
// Helpers
|
|
604
664
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
605
665
|
|
|
666
|
+
/** A 2xx response that is not an AWS event stream: a deterministic protocol error, never a transport failure. */
|
|
667
|
+
class KiroNonEventStreamError extends Error {
|
|
668
|
+
override name = "KiroNonEventStreamError";
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/** Byte cap for diagnostic error bodies; the rest of the stream is cancelled unread. */
|
|
672
|
+
const ERROR_BODY_PREFIX_BYTES = 4096;
|
|
673
|
+
|
|
674
|
+
/** Read at most {@link ERROR_BODY_PREFIX_BYTES} of a response body for diagnostics, then cancel the remainder. */
|
|
675
|
+
async function readBodyPrefix(response: Response): Promise<string> {
|
|
676
|
+
if (!response.body) return "";
|
|
677
|
+
const reader = response.body.getReader();
|
|
678
|
+
const chunks: Uint8Array[] = [];
|
|
679
|
+
let total = 0;
|
|
680
|
+
try {
|
|
681
|
+
while (total < ERROR_BODY_PREFIX_BYTES) {
|
|
682
|
+
const { done, value } = await reader.read();
|
|
683
|
+
if (done) break;
|
|
684
|
+
const take = value.subarray(0, ERROR_BODY_PREFIX_BYTES - total);
|
|
685
|
+
chunks.push(take);
|
|
686
|
+
total += take.length;
|
|
687
|
+
}
|
|
688
|
+
} catch {
|
|
689
|
+
// Diagnostic read only; report whatever prefix arrived.
|
|
690
|
+
} finally {
|
|
691
|
+
reader.cancel().catch(() => {});
|
|
692
|
+
}
|
|
693
|
+
const bytes = new Uint8Array(total);
|
|
694
|
+
let offset = 0;
|
|
695
|
+
for (const chunk of chunks) {
|
|
696
|
+
bytes.set(chunk, offset);
|
|
697
|
+
offset += chunk.length;
|
|
698
|
+
}
|
|
699
|
+
return new TextDecoder().decode(bytes);
|
|
700
|
+
}
|
|
701
|
+
|
|
606
702
|
function resolveBearerToken(apiKey: string | undefined): string | undefined {
|
|
607
703
|
if (!apiKey) {
|
|
608
704
|
return $credentialEnv("KIRO_API_KEY") ?? $credentialEnv("AWS_BEARER_TOKEN_KIRO") ?? undefined;
|
|
@@ -11,6 +11,8 @@ export interface OpenAICodexResponsesOptions extends StreamOptions {
|
|
|
11
11
|
preferWebsockets?: boolean;
|
|
12
12
|
serviceTier?: ServiceTier;
|
|
13
13
|
}
|
|
14
|
+
/** @internal Exported for tests. */
|
|
15
|
+
export declare function createCodexStreamProgressClassifier(): (event: unknown) => boolean;
|
|
14
16
|
type CodexTransport = "sse" | "websocket";
|
|
15
17
|
export interface OpenAICodexWebSocketDebugStats {
|
|
16
18
|
fullContextRequests: number;
|
|
@@ -75,3 +77,4 @@ type CodexToolPayload = {
|
|
|
75
77
|
/** @internal Exported for tests. */
|
|
76
78
|
export declare function convertOpenAICodexResponsesTools(tools: Tool[], model: Model<"openai-codex-responses">): CodexToolPayload[];
|
|
77
79
|
declare function isRetryableCodexFailureEvent(rawEvent: Record<string, unknown>): boolean;
|
|
80
|
+
export declare function isExplicitCodexTerminalVeto(code?: string, providerMessage?: string): boolean;
|