@javargasm/opencode-kiro-auth 0.3.1 → 0.4.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/dist/debug.d.ts.map +1 -1
- package/dist/event-parser.d.ts +9 -0
- package/dist/event-parser.d.ts.map +1 -1
- package/dist/file-logger.d.ts +73 -46
- package/dist/file-logger.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +438 -159
- package/dist/models.d.ts +6 -1
- package/dist/models.d.ts.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/stream.d.ts +16 -0
- package/dist/stream.d.ts.map +1 -1
- package/dist/thinking-parser.d.ts.map +1 -1
- package/dist/transform.d.ts +7 -0
- package/dist/transform.d.ts.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16,8 +16,121 @@ var __export = (target, all) => {
|
|
|
16
16
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
17
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
18
18
|
|
|
19
|
-
// src/
|
|
19
|
+
// src/file-logger.ts
|
|
20
20
|
import { appendFileSync, mkdirSync } from "node:fs";
|
|
21
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
22
|
+
function isEnabled() {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
function ensureLogDir() {
|
|
26
|
+
if (dirEnsured)
|
|
27
|
+
return;
|
|
28
|
+
try {
|
|
29
|
+
mkdirSync(LOG_DIR, { recursive: true });
|
|
30
|
+
dirEnsured = true;
|
|
31
|
+
} catch {}
|
|
32
|
+
}
|
|
33
|
+
function sanitizeSessionId(id) {
|
|
34
|
+
const slug = id.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
35
|
+
return slug.length > 0 ? slug : "default";
|
|
36
|
+
}
|
|
37
|
+
function currentSessionLogFile() {
|
|
38
|
+
return sessionLogStore.getStore()?.file ?? null;
|
|
39
|
+
}
|
|
40
|
+
function enterSessionLog(sessionId) {
|
|
41
|
+
const id = sanitizeSessionId(sessionId ?? "default");
|
|
42
|
+
sessionLogStore.enterWith({ file: `${LOG_DIR}/session-${id}.log`, sessionId: id });
|
|
43
|
+
return id;
|
|
44
|
+
}
|
|
45
|
+
function writeLine(file, data) {
|
|
46
|
+
if (!isEnabled())
|
|
47
|
+
return;
|
|
48
|
+
ensureLogDir();
|
|
49
|
+
const entry = {
|
|
50
|
+
ts: new Date().toISOString(),
|
|
51
|
+
...data
|
|
52
|
+
};
|
|
53
|
+
try {
|
|
54
|
+
appendFileSync(file, JSON.stringify(entry) + `
|
|
55
|
+
`);
|
|
56
|
+
} catch {}
|
|
57
|
+
}
|
|
58
|
+
function createSessionLogger(sessionId) {
|
|
59
|
+
const id = sanitizeSessionId(sessionId ?? "default");
|
|
60
|
+
const file = `${LOG_DIR}/session-${id}.log`;
|
|
61
|
+
const emit = (data) => writeLine(file, { sessionId: id, ...data });
|
|
62
|
+
return {
|
|
63
|
+
file,
|
|
64
|
+
sessionId: id,
|
|
65
|
+
logRequest(meta, requestBody) {
|
|
66
|
+
emit({
|
|
67
|
+
type: "request",
|
|
68
|
+
...meta,
|
|
69
|
+
body: safeParseJson(requestBody)
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
logResponseEvent(event) {
|
|
73
|
+
emit({
|
|
74
|
+
type: "response_event",
|
|
75
|
+
eventType: event.type,
|
|
76
|
+
seq: event.eventSeq,
|
|
77
|
+
data: event.data
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
logResponseDone(meta) {
|
|
81
|
+
emit({ type: "response_done", ...meta });
|
|
82
|
+
},
|
|
83
|
+
logHttpError(meta) {
|
|
84
|
+
emit({ type: "http_error", ...meta });
|
|
85
|
+
},
|
|
86
|
+
logStreamError(meta) {
|
|
87
|
+
emit({ type: "stream_error", ...meta });
|
|
88
|
+
},
|
|
89
|
+
logCaughtError(meta, error) {
|
|
90
|
+
emit({
|
|
91
|
+
type: "caught_error",
|
|
92
|
+
...meta,
|
|
93
|
+
error: error === undefined ? undefined : serializeError(error)
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function safeParseJson(s) {
|
|
99
|
+
try {
|
|
100
|
+
return JSON.parse(s);
|
|
101
|
+
} catch {
|
|
102
|
+
return s;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function serializeError(error, depth = 0) {
|
|
106
|
+
if (depth > 5)
|
|
107
|
+
return "[cause chain truncated]";
|
|
108
|
+
if (error instanceof Error) {
|
|
109
|
+
const out = {
|
|
110
|
+
name: error.name,
|
|
111
|
+
message: error.message,
|
|
112
|
+
stack: error.stack
|
|
113
|
+
};
|
|
114
|
+
if (error.cause !== undefined) {
|
|
115
|
+
out.cause = serializeError(error.cause, depth + 1);
|
|
116
|
+
}
|
|
117
|
+
for (const key of Object.keys(error)) {
|
|
118
|
+
if (!(key in out))
|
|
119
|
+
out[key] = error[key];
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
if (error && typeof error === "object")
|
|
124
|
+
return error;
|
|
125
|
+
return String(error);
|
|
126
|
+
}
|
|
127
|
+
var LOG_DIR = "/tmp/kiro-logs", dirEnsured = false, sessionLogStore;
|
|
128
|
+
var init_file_logger = __esm(() => {
|
|
129
|
+
sessionLogStore = new AsyncLocalStorage;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// src/debug.ts
|
|
133
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
21
134
|
import { dirname, isAbsolute, resolve } from "node:path";
|
|
22
135
|
function currentLevel() {
|
|
23
136
|
const raw = (globalThis.process?.env?.KIRO_LOG ?? "").toLowerCase();
|
|
@@ -29,6 +142,9 @@ function enabled(level) {
|
|
|
29
142
|
return LEVEL_ORDER[level] <= LEVEL_ORDER[currentLevel()];
|
|
30
143
|
}
|
|
31
144
|
function currentFilePath() {
|
|
145
|
+
const sessionFile = currentSessionLogFile();
|
|
146
|
+
if (sessionFile)
|
|
147
|
+
return sessionFile;
|
|
32
148
|
const raw = globalThis.process?.env?.KIRO_LOG_FILE;
|
|
33
149
|
if (!raw)
|
|
34
150
|
return null;
|
|
@@ -38,10 +154,10 @@ function writeToFile(filePath, line) {
|
|
|
38
154
|
try {
|
|
39
155
|
const dir = dirname(filePath);
|
|
40
156
|
if (!ensuredDirs.has(dir)) {
|
|
41
|
-
|
|
157
|
+
mkdirSync2(dir, { recursive: true });
|
|
42
158
|
ensuredDirs.add(dir);
|
|
43
159
|
}
|
|
44
|
-
|
|
160
|
+
appendFileSync2(filePath, line + `
|
|
45
161
|
`);
|
|
46
162
|
} catch (err) {
|
|
47
163
|
if (!fileFallbackWarned) {
|
|
@@ -101,6 +217,7 @@ function previewChunk(s) {
|
|
|
101
217
|
}
|
|
102
218
|
var LEVEL_ORDER, ensuredDirs, fileFallbackWarned = false, log, CHUNK_PREVIEW_LIMIT = 2048;
|
|
103
219
|
var init_debug = __esm(() => {
|
|
220
|
+
init_file_logger();
|
|
104
221
|
LEVEL_ORDER = {
|
|
105
222
|
error: 0,
|
|
106
223
|
warn: 1,
|
|
@@ -167,7 +284,7 @@ async function resolveProfileArn(accessToken, apiRegion) {
|
|
|
167
284
|
Authorization: `Bearer ${accessToken}`,
|
|
168
285
|
"Content-Type": "application/x-amz-json-1.0",
|
|
169
286
|
"X-Amz-Target": "AmazonCodeWhispererService.ListAvailableProfiles",
|
|
170
|
-
"user-agent": "aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererruntime/0.1.16551 os/macos lang/rust/1.92.0 md/appVersion-2.
|
|
287
|
+
"user-agent": "aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererruntime/0.1.16551 os/macos lang/rust/1.92.0 md/appVersion-2.8.1 app/AmazonQ-For-CLI"
|
|
171
288
|
},
|
|
172
289
|
body: "{}"
|
|
173
290
|
});
|
|
@@ -190,7 +307,7 @@ async function fetchAvailableModels(accessToken, apiRegion, profileArn) {
|
|
|
190
307
|
Authorization: `Bearer ${accessToken}`,
|
|
191
308
|
"Content-Type": "application/x-amz-json-1.0",
|
|
192
309
|
"X-Amz-Target": "AmazonCodeWhispererService.ListAvailableModels",
|
|
193
|
-
"user-agent": "aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererruntime/0.1.16551 os/macos lang/rust/1.92.0 md/appVersion-2.
|
|
310
|
+
"user-agent": "aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererruntime/0.1.16551 os/macos lang/rust/1.92.0 md/appVersion-2.8.1 app/AmazonQ-For-CLI"
|
|
194
311
|
},
|
|
195
312
|
body: "{}"
|
|
196
313
|
});
|
|
@@ -293,7 +410,7 @@ var init_models = __esm(() => {
|
|
|
293
410
|
{
|
|
294
411
|
...KIRO_DEFAULTS,
|
|
295
412
|
id: "claude-fable-5",
|
|
296
|
-
name: "Claude Fable 5",
|
|
413
|
+
name: "Claude Fable 5 (disabled)",
|
|
297
414
|
reasoning: true,
|
|
298
415
|
input: MULTIMODAL,
|
|
299
416
|
contextWindow: 1e6,
|
|
@@ -364,7 +481,7 @@ var init_models = __esm(() => {
|
|
|
364
481
|
reasoning: true,
|
|
365
482
|
input: MULTIMODAL,
|
|
366
483
|
contextWindow: 200000,
|
|
367
|
-
maxTokens:
|
|
484
|
+
maxTokens: 64000
|
|
368
485
|
},
|
|
369
486
|
{
|
|
370
487
|
...KIRO_DEFAULTS,
|
|
@@ -373,7 +490,7 @@ var init_models = __esm(() => {
|
|
|
373
490
|
reasoning: true,
|
|
374
491
|
input: MULTIMODAL,
|
|
375
492
|
contextWindow: 200000,
|
|
376
|
-
maxTokens:
|
|
493
|
+
maxTokens: 64000
|
|
377
494
|
},
|
|
378
495
|
{
|
|
379
496
|
...KIRO_DEFAULTS,
|
|
@@ -382,7 +499,7 @@ var init_models = __esm(() => {
|
|
|
382
499
|
reasoning: false,
|
|
383
500
|
input: MULTIMODAL,
|
|
384
501
|
contextWindow: 200000,
|
|
385
|
-
maxTokens:
|
|
502
|
+
maxTokens: 64000
|
|
386
503
|
},
|
|
387
504
|
{
|
|
388
505
|
...KIRO_DEFAULTS,
|
|
@@ -417,8 +534,8 @@ var init_models = __esm(() => {
|
|
|
417
534
|
name: "Auto",
|
|
418
535
|
reasoning: true,
|
|
419
536
|
input: MULTIMODAL,
|
|
420
|
-
contextWindow:
|
|
421
|
-
maxTokens:
|
|
537
|
+
contextWindow: 1e6,
|
|
538
|
+
maxTokens: 64000
|
|
422
539
|
}
|
|
423
540
|
];
|
|
424
541
|
REASONING_FAMILIES = new Set([
|
|
@@ -863,6 +980,9 @@ var init_kiro_cli_sync = __esm(() => {
|
|
|
863
980
|
init_debug();
|
|
864
981
|
});
|
|
865
982
|
|
|
983
|
+
// src/server.ts
|
|
984
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
985
|
+
|
|
866
986
|
// src/types.ts
|
|
867
987
|
class EventStream {
|
|
868
988
|
queue = [];
|
|
@@ -870,20 +990,25 @@ class EventStream {
|
|
|
870
990
|
done = false;
|
|
871
991
|
finalResultPromise;
|
|
872
992
|
resolveFinalResult;
|
|
993
|
+
rejectFinalResult;
|
|
994
|
+
resultSettled = false;
|
|
873
995
|
isComplete;
|
|
874
996
|
extractResult;
|
|
875
997
|
constructor(isComplete, extractResult) {
|
|
876
998
|
this.isComplete = isComplete;
|
|
877
999
|
this.extractResult = extractResult;
|
|
878
|
-
this.finalResultPromise = new Promise((resolve) => {
|
|
1000
|
+
this.finalResultPromise = new Promise((resolve, reject) => {
|
|
879
1001
|
this.resolveFinalResult = resolve;
|
|
1002
|
+
this.rejectFinalResult = reject;
|
|
880
1003
|
});
|
|
1004
|
+
this.finalResultPromise.catch(() => {});
|
|
881
1005
|
}
|
|
882
1006
|
push(event) {
|
|
883
1007
|
if (this.done)
|
|
884
1008
|
return;
|
|
885
1009
|
if (this.isComplete(event)) {
|
|
886
1010
|
this.done = true;
|
|
1011
|
+
this.resultSettled = true;
|
|
887
1012
|
this.resolveFinalResult(this.extractResult(event));
|
|
888
1013
|
}
|
|
889
1014
|
const waiter = this.waiting.shift();
|
|
@@ -896,7 +1021,11 @@ class EventStream {
|
|
|
896
1021
|
end(result) {
|
|
897
1022
|
this.done = true;
|
|
898
1023
|
if (result !== undefined) {
|
|
1024
|
+
this.resultSettled = true;
|
|
899
1025
|
this.resolveFinalResult(result);
|
|
1026
|
+
} else if (!this.resultSettled) {
|
|
1027
|
+
this.resultSettled = true;
|
|
1028
|
+
this.rejectFinalResult(new Error("Stream ended before producing a final result"));
|
|
900
1029
|
}
|
|
901
1030
|
while (this.waiting.length > 0) {
|
|
902
1031
|
const waiter = this.waiting.shift();
|
|
@@ -1086,6 +1215,9 @@ function parseKiroEventMulti(parsed) {
|
|
|
1086
1215
|
if (parsed.unit === "credit" && parsed.usage !== undefined && typeof parsed.usage === "number") {
|
|
1087
1216
|
events.push({ type: "metering", data: { usage: parsed.usage } });
|
|
1088
1217
|
}
|
|
1218
|
+
if (typeof parsed.stopReason === "string") {
|
|
1219
|
+
events.push({ type: "metadata", data: { stopReason: parsed.stopReason } });
|
|
1220
|
+
}
|
|
1089
1221
|
return events;
|
|
1090
1222
|
}
|
|
1091
1223
|
var EVENT_PATTERNS = [
|
|
@@ -1100,6 +1232,7 @@ var EVENT_PATTERNS = [
|
|
|
1100
1232
|
'{"input":',
|
|
1101
1233
|
'{"stop":',
|
|
1102
1234
|
'{"contextUsagePercentage":',
|
|
1235
|
+
'{"stopReason":',
|
|
1103
1236
|
'{"followupPrompt":',
|
|
1104
1237
|
'{"usage":',
|
|
1105
1238
|
'{"Usage":',
|
|
@@ -1118,9 +1251,31 @@ function findNextEventStart(buffer, from) {
|
|
|
1118
1251
|
}
|
|
1119
1252
|
return earliest;
|
|
1120
1253
|
}
|
|
1254
|
+
var EXCEPTION_TYPE_RE = /:exception-type[\s\S]{0,4}?([A-Za-z][A-Za-z0-9]*(?:Exception|Error|Fault))/;
|
|
1255
|
+
var MESSAGE_TYPE_EXCEPTION_RE = /:message-type[\s\S]{0,4}?exception\b/;
|
|
1256
|
+
function detectEventStreamException(buffer) {
|
|
1257
|
+
const typeMatch = buffer.match(EXCEPTION_TYPE_RE);
|
|
1258
|
+
if (!typeMatch && !MESSAGE_TYPE_EXCEPTION_RE.test(buffer))
|
|
1259
|
+
return null;
|
|
1260
|
+
const type = typeMatch?.[1] ?? "ServiceException";
|
|
1261
|
+
let message;
|
|
1262
|
+
const msgIdx = buffer.lastIndexOf('{"message":');
|
|
1263
|
+
if (msgIdx >= 0) {
|
|
1264
|
+
const end = findJsonEnd(buffer, msgIdx);
|
|
1265
|
+
if (end >= 0) {
|
|
1266
|
+
try {
|
|
1267
|
+
const parsed = JSON.parse(buffer.substring(msgIdx, end + 1));
|
|
1268
|
+
if (typeof parsed.message === "string")
|
|
1269
|
+
message = parsed.message;
|
|
1270
|
+
} catch {}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
return { type, message };
|
|
1274
|
+
}
|
|
1121
1275
|
function parseKiroEvents(buffer) {
|
|
1122
1276
|
const events = [];
|
|
1123
1277
|
let pos = 0;
|
|
1278
|
+
let remaining = "";
|
|
1124
1279
|
while (pos < buffer.length) {
|
|
1125
1280
|
const jsonStart = findNextEventStart(buffer, pos);
|
|
1126
1281
|
if (jsonStart < 0) {
|
|
@@ -1148,7 +1303,8 @@ function parseKiroEvents(buffer) {
|
|
|
1148
1303
|
}
|
|
1149
1304
|
const jsonEnd = findJsonEnd(buffer, jsonStart);
|
|
1150
1305
|
if (jsonEnd < 0) {
|
|
1151
|
-
|
|
1306
|
+
remaining = buffer.substring(jsonStart);
|
|
1307
|
+
break;
|
|
1152
1308
|
}
|
|
1153
1309
|
try {
|
|
1154
1310
|
const parsed = JSON.parse(buffer.substring(jsonStart, jsonEnd + 1));
|
|
@@ -1168,7 +1324,12 @@ function parseKiroEvents(buffer) {
|
|
|
1168
1324
|
}
|
|
1169
1325
|
pos = jsonEnd + 1;
|
|
1170
1326
|
}
|
|
1171
|
-
|
|
1327
|
+
const exception = detectEventStreamException(buffer);
|
|
1328
|
+
if (exception && !events.some((e) => e.type === "error")) {
|
|
1329
|
+
events.push({ type: "error", data: { error: exception.type, message: exception.message } });
|
|
1330
|
+
remaining = "";
|
|
1331
|
+
}
|
|
1332
|
+
return { events, remaining };
|
|
1172
1333
|
}
|
|
1173
1334
|
|
|
1174
1335
|
// src/health.ts
|
|
@@ -1191,6 +1352,7 @@ function isPermanentError(reason) {
|
|
|
1191
1352
|
|
|
1192
1353
|
// src/stream.ts
|
|
1193
1354
|
init_models();
|
|
1355
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
1194
1356
|
|
|
1195
1357
|
// src/thinking-parser.ts
|
|
1196
1358
|
init_debug();
|
|
@@ -1391,14 +1553,8 @@ class ThinkingTagParser {
|
|
|
1391
1553
|
if (!thinking)
|
|
1392
1554
|
return;
|
|
1393
1555
|
if (this.thinkingBlockIndex === null) {
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
this.output.content.splice(this.thinkingBlockIndex, 0, { type: "thinking", thinking: "" });
|
|
1397
|
-
this.textBlockIndex = this.textBlockIndex + 1;
|
|
1398
|
-
} else {
|
|
1399
|
-
this.thinkingBlockIndex = this.output.content.length;
|
|
1400
|
-
this.output.content.push({ type: "thinking", thinking: "" });
|
|
1401
|
-
}
|
|
1556
|
+
this.thinkingBlockIndex = this.output.content.length;
|
|
1557
|
+
this.output.content.push({ type: "thinking", thinking: "" });
|
|
1402
1558
|
this.stream.push({
|
|
1403
1559
|
type: "thinking_start",
|
|
1404
1560
|
contentIndex: this.thinkingBlockIndex,
|
|
@@ -2002,83 +2158,8 @@ async function startSocialLogin() {
|
|
|
2002
2158
|
return { signInUrl, waitForCredentials };
|
|
2003
2159
|
}
|
|
2004
2160
|
|
|
2005
|
-
// src/
|
|
2006
|
-
|
|
2007
|
-
var LOG_DIR = "/tmp/kiro-logs";
|
|
2008
|
-
var REQUESTS_FILE = `${LOG_DIR}/requests.log`;
|
|
2009
|
-
var RESPONSES_FILE = `${LOG_DIR}/responses.log`;
|
|
2010
|
-
var ERRORS_FILE = `${LOG_DIR}/errors.log`;
|
|
2011
|
-
var dirEnsured = false;
|
|
2012
|
-
function isEnabled() {
|
|
2013
|
-
return true;
|
|
2014
|
-
}
|
|
2015
|
-
function ensureDir() {
|
|
2016
|
-
if (dirEnsured)
|
|
2017
|
-
return;
|
|
2018
|
-
try {
|
|
2019
|
-
mkdirSync2(LOG_DIR, { recursive: true });
|
|
2020
|
-
dirEnsured = true;
|
|
2021
|
-
} catch {}
|
|
2022
|
-
}
|
|
2023
|
-
function writeLine(file, data) {
|
|
2024
|
-
if (!isEnabled())
|
|
2025
|
-
return;
|
|
2026
|
-
ensureDir();
|
|
2027
|
-
const entry = {
|
|
2028
|
-
ts: new Date().toISOString(),
|
|
2029
|
-
...data
|
|
2030
|
-
};
|
|
2031
|
-
try {
|
|
2032
|
-
appendFileSync2(file, JSON.stringify(entry) + `
|
|
2033
|
-
`);
|
|
2034
|
-
} catch {}
|
|
2035
|
-
}
|
|
2036
|
-
function logRequest(meta, requestBody) {
|
|
2037
|
-
writeLine(REQUESTS_FILE, {
|
|
2038
|
-
type: "request",
|
|
2039
|
-
...meta,
|
|
2040
|
-
body: safeParseJson(requestBody)
|
|
2041
|
-
});
|
|
2042
|
-
}
|
|
2043
|
-
function logResponseEvent(event) {
|
|
2044
|
-
writeLine(RESPONSES_FILE, {
|
|
2045
|
-
type: "response_event",
|
|
2046
|
-
eventType: event.type,
|
|
2047
|
-
seq: event.eventSeq,
|
|
2048
|
-
data: event.data
|
|
2049
|
-
});
|
|
2050
|
-
}
|
|
2051
|
-
function logResponseDone(meta) {
|
|
2052
|
-
writeLine(RESPONSES_FILE, {
|
|
2053
|
-
type: "response_done",
|
|
2054
|
-
...meta
|
|
2055
|
-
});
|
|
2056
|
-
}
|
|
2057
|
-
function logHttpError(meta) {
|
|
2058
|
-
writeLine(ERRORS_FILE, {
|
|
2059
|
-
type: "http_error",
|
|
2060
|
-
...meta
|
|
2061
|
-
});
|
|
2062
|
-
}
|
|
2063
|
-
function logStreamError(meta) {
|
|
2064
|
-
writeLine(ERRORS_FILE, {
|
|
2065
|
-
type: "stream_error",
|
|
2066
|
-
...meta
|
|
2067
|
-
});
|
|
2068
|
-
}
|
|
2069
|
-
function logCaughtError(meta) {
|
|
2070
|
-
writeLine(ERRORS_FILE, {
|
|
2071
|
-
type: "caught_error",
|
|
2072
|
-
...meta
|
|
2073
|
-
});
|
|
2074
|
-
}
|
|
2075
|
-
function safeParseJson(s) {
|
|
2076
|
-
try {
|
|
2077
|
-
return JSON.parse(s);
|
|
2078
|
-
} catch {
|
|
2079
|
-
return s;
|
|
2080
|
-
}
|
|
2081
|
-
}
|
|
2161
|
+
// src/stream.ts
|
|
2162
|
+
init_file_logger();
|
|
2082
2163
|
|
|
2083
2164
|
// src/transform.ts
|
|
2084
2165
|
import { createHash as createHash2 } from "node:crypto";
|
|
@@ -2256,6 +2337,13 @@ function convertToolsToKiro(tools) {
|
|
|
2256
2337
|
};
|
|
2257
2338
|
});
|
|
2258
2339
|
}
|
|
2340
|
+
var KIRO_IMAGE_FORMATS = new Set(["png", "jpeg", "gif", "webp"]);
|
|
2341
|
+
function normalizeImageFormat(mimeType) {
|
|
2342
|
+
const sub = (mimeType.split("/")[1] || "").toLowerCase().split(";")[0].trim();
|
|
2343
|
+
const base = sub.replace(/\+.*$/, "").replace(/^vnd\./, "");
|
|
2344
|
+
const canonical = base === "jpg" ? "jpeg" : base;
|
|
2345
|
+
return KIRO_IMAGE_FORMATS.has(canonical) ? canonical : null;
|
|
2346
|
+
}
|
|
2259
2347
|
function convertImagesToKiro(images) {
|
|
2260
2348
|
let omitted = 0;
|
|
2261
2349
|
const valid = [];
|
|
@@ -2269,8 +2357,13 @@ function convertImagesToKiro(images) {
|
|
|
2269
2357
|
omitted++;
|
|
2270
2358
|
continue;
|
|
2271
2359
|
}
|
|
2360
|
+
const format = normalizeImageFormat(img.mimeType);
|
|
2361
|
+
if (!format) {
|
|
2362
|
+
omitted++;
|
|
2363
|
+
continue;
|
|
2364
|
+
}
|
|
2272
2365
|
valid.push({
|
|
2273
|
-
format
|
|
2366
|
+
format,
|
|
2274
2367
|
source: { bytes: img.data }
|
|
2275
2368
|
});
|
|
2276
2369
|
}
|
|
@@ -2529,9 +2622,28 @@ function isTransientError(status) {
|
|
|
2529
2622
|
return status === 429 || status >= 500;
|
|
2530
2623
|
}
|
|
2531
2624
|
function firstTokenTimeoutForModel(modelId) {
|
|
2532
|
-
const m = kiroModels.find((x) => x.id === modelId);
|
|
2625
|
+
const m = kiroModels.find((x) => x.id === modelId) ?? getCachedDynamicModels()?.find((x) => x.id === modelId);
|
|
2533
2626
|
return m?.firstTokenTimeout ?? FIRST_TOKEN_TIMEOUT_DEFAULT_MS;
|
|
2534
2627
|
}
|
|
2628
|
+
function regionFromEndpoint(endpoint) {
|
|
2629
|
+
const m = endpoint.match(/(?:runtime|management)\.([a-z0-9-]+)\.kiro\.dev/i);
|
|
2630
|
+
return m?.[1] ?? "us-east-1";
|
|
2631
|
+
}
|
|
2632
|
+
function mapKiroStopReason(raw) {
|
|
2633
|
+
switch (raw?.toUpperCase()) {
|
|
2634
|
+
case "TOOL_USE":
|
|
2635
|
+
return "toolUse";
|
|
2636
|
+
case "MAX_TOKENS":
|
|
2637
|
+
return "length";
|
|
2638
|
+
case "END_TURN":
|
|
2639
|
+
case "STOP_SEQUENCE":
|
|
2640
|
+
case "COMPLETE":
|
|
2641
|
+
case "FINISHED":
|
|
2642
|
+
return "stop";
|
|
2643
|
+
default:
|
|
2644
|
+
return null;
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2535
2647
|
var HIDDEN_REASONING_PLACEHOLDER = "Reasoning hidden by provider";
|
|
2536
2648
|
var HIDDEN_REASONING_COUNTDOWN_MS = 2000;
|
|
2537
2649
|
function emitHiddenReasoningLate(output, stream) {
|
|
@@ -2577,8 +2689,23 @@ function emitToolCall(state, output, stream) {
|
|
|
2577
2689
|
stream.push({ type: "toolcall_end", contentIndex, toolCall, partial: output });
|
|
2578
2690
|
return true;
|
|
2579
2691
|
}
|
|
2692
|
+
var CONVERSATION_ID_NAMESPACE = "opencode-kiro/conversation";
|
|
2693
|
+
function deterministicConversationId(key) {
|
|
2694
|
+
const digest = createHash3("sha1").update(`${CONVERSATION_ID_NAMESPACE}\x00${key}`).digest();
|
|
2695
|
+
const b = Buffer.from(digest.subarray(0, 16));
|
|
2696
|
+
b[6] = b[6] & 15 | 80;
|
|
2697
|
+
b[8] = b[8] & 63 | 128;
|
|
2698
|
+
const hex = b.toString("hex");
|
|
2699
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
2700
|
+
}
|
|
2701
|
+
function resolveConversationId(sessionId) {
|
|
2702
|
+
if (!sessionId)
|
|
2703
|
+
return crypto.randomUUID();
|
|
2704
|
+
return deterministicConversationId(sessionId);
|
|
2705
|
+
}
|
|
2580
2706
|
function streamKiro(model, context, options) {
|
|
2581
2707
|
const stream = new AssistantMessageEventStream;
|
|
2708
|
+
const fileLog = createSessionLogger(options?.logSessionId ?? options?.sessionId);
|
|
2582
2709
|
(async () => {
|
|
2583
2710
|
const output = {
|
|
2584
2711
|
role: "assistant",
|
|
@@ -2604,7 +2731,7 @@ function streamKiro(model, context, options) {
|
|
|
2604
2731
|
throw new Error("Kiro credentials not set. Run /login kiro.");
|
|
2605
2732
|
}
|
|
2606
2733
|
const endpoint = model.baseUrl || "https://runtime.us-east-1.kiro.dev";
|
|
2607
|
-
const profileArn = await resolveProfileArn(accessToken, endpoint);
|
|
2734
|
+
const profileArn = await resolveProfileArn(accessToken, regionFromEndpoint(endpoint));
|
|
2608
2735
|
const kiroModelId = resolveKiroModel(model.id);
|
|
2609
2736
|
const thinkingEnabled = !!options?.reasoning || model.reasoning;
|
|
2610
2737
|
const reasoningHidden = !!model.reasoningHidden;
|
|
@@ -2633,7 +2760,7 @@ ${systemPrompt}` : ""}`;
|
|
|
2633
2760
|
operatingSystem: resolveOS(),
|
|
2634
2761
|
currentWorkingDirectory: process.cwd()
|
|
2635
2762
|
};
|
|
2636
|
-
const conversationId = options?.sessionId
|
|
2763
|
+
const conversationId = resolveConversationId(options?.sessionId);
|
|
2637
2764
|
let retryCount = 0;
|
|
2638
2765
|
while (retryCount <= MAX_RETRIES) {
|
|
2639
2766
|
if (options?.signal?.aborted)
|
|
@@ -2828,6 +2955,12 @@ ${currentContent}`;
|
|
|
2828
2955
|
log.debug("effort.set", { effort: options.reasoning, model: model.id });
|
|
2829
2956
|
}
|
|
2830
2957
|
}
|
|
2958
|
+
if (supportsThinkingConfig && typeof options?.maxTokens === "number" && options.maxTokens > 0) {
|
|
2959
|
+
const capped = Math.min(Math.max(Math.floor(options.maxTokens), 1024), model.maxTokens || 64000);
|
|
2960
|
+
request.additionalModelRequestFields = request.additionalModelRequestFields || {};
|
|
2961
|
+
request.additionalModelRequestFields.max_tokens = capped;
|
|
2962
|
+
log.debug("maxTokens.set", { maxTokens: capped, model: model.id });
|
|
2963
|
+
}
|
|
2831
2964
|
stream.push({ type: "start", partial: output });
|
|
2832
2965
|
if (reasoningHidden && thinkingEnabled && hiddenShimTimer === null) {
|
|
2833
2966
|
hiddenShimTimer = setTimeout(() => {
|
|
@@ -2841,7 +2974,7 @@ ${currentContent}`;
|
|
|
2841
2974
|
let contextTruncationAttempt = 0;
|
|
2842
2975
|
while (true) {
|
|
2843
2976
|
const osName = resolveOS();
|
|
2844
|
-
const ua = `aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererstreaming/0.1.16551 os/${osName} lang/rust/1.92.0 md/appVersion-2.
|
|
2977
|
+
const ua = `aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererstreaming/0.1.16551 os/${osName} lang/rust/1.92.0 md/appVersion-2.8.1 app/AmazonQ-For-CLI`;
|
|
2845
2978
|
const xAmzUa = `aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererstreaming/0.1.16551 os/${osName} lang/rust/1.92.0 m/F app/AmazonQ-For-CLI`;
|
|
2846
2979
|
const requestBody = JSON.stringify(request);
|
|
2847
2980
|
log.debug("request.send", {
|
|
@@ -2856,10 +2989,11 @@ ${currentContent}`;
|
|
|
2856
2989
|
log.debug(`[stream] req=${requestBody.length}c hist=${history.length} content=${currentContent.length}c profileArn=${!!profileArn}`);
|
|
2857
2990
|
if (log.isDebug()) {
|
|
2858
2991
|
try {
|
|
2859
|
-
|
|
2992
|
+
ensureLogDir();
|
|
2993
|
+
__require("fs").writeFileSync(`${LOG_DIR}/session-${fileLog.sessionId}.last-request.json`, requestBody);
|
|
2860
2994
|
} catch {}
|
|
2861
2995
|
}
|
|
2862
|
-
logRequest({
|
|
2996
|
+
fileLog.logRequest({
|
|
2863
2997
|
endpoint,
|
|
2864
2998
|
model: model.id,
|
|
2865
2999
|
historyLength: history.length,
|
|
@@ -2898,7 +3032,7 @@ ${currentContent}`;
|
|
|
2898
3032
|
status: response.status,
|
|
2899
3033
|
body: errText
|
|
2900
3034
|
});
|
|
2901
|
-
logHttpError({
|
|
3035
|
+
fileLog.logHttpError({
|
|
2902
3036
|
status: response.status,
|
|
2903
3037
|
statusText: response.statusText,
|
|
2904
3038
|
body: errText,
|
|
@@ -2965,10 +3099,10 @@ ${currentContent}`;
|
|
|
2965
3099
|
const decoder = new TextDecoder;
|
|
2966
3100
|
let buffer = "";
|
|
2967
3101
|
let totalContent = "";
|
|
2968
|
-
let lastContentData = "";
|
|
2969
3102
|
let usageEvent = null;
|
|
2970
3103
|
let meteringCredits;
|
|
2971
3104
|
let receivedContextUsage = false;
|
|
3105
|
+
let serverStopReason = null;
|
|
2972
3106
|
let chunkSeq = 0;
|
|
2973
3107
|
let eventSeq = 0;
|
|
2974
3108
|
const thinkingParser = thinkingEnabled ? new ThinkingTagParser(output, stream) : null;
|
|
@@ -3051,7 +3185,7 @@ ${currentContent}`;
|
|
|
3051
3185
|
}
|
|
3052
3186
|
}
|
|
3053
3187
|
for (const ev of events) {
|
|
3054
|
-
logResponseEvent({ type: ev.type, data: ev.data, eventSeq });
|
|
3188
|
+
fileLog.logResponseEvent({ type: ev.type, data: ev.data, eventSeq });
|
|
3055
3189
|
}
|
|
3056
3190
|
for (const event of events) {
|
|
3057
3191
|
switch (event.type) {
|
|
@@ -3064,7 +3198,10 @@ ${currentContent}`;
|
|
|
3064
3198
|
}
|
|
3065
3199
|
case "reasoning": {
|
|
3066
3200
|
cancelHiddenShim();
|
|
3067
|
-
|
|
3201
|
+
const lastIsThinking = output.content.length > 0 && output.content[output.content.length - 1]?.type === "thinking";
|
|
3202
|
+
if (!event.data.text && !lastIsThinking)
|
|
3203
|
+
break;
|
|
3204
|
+
if (!lastIsThinking) {
|
|
3068
3205
|
output.content.push({ type: "thinking", thinking: "" });
|
|
3069
3206
|
stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
|
|
3070
3207
|
}
|
|
@@ -3081,9 +3218,6 @@ ${currentContent}`;
|
|
|
3081
3218
|
break;
|
|
3082
3219
|
}
|
|
3083
3220
|
case "content": {
|
|
3084
|
-
if (event.data === lastContentData)
|
|
3085
|
-
continue;
|
|
3086
|
-
lastContentData = event.data;
|
|
3087
3221
|
totalContent += event.data;
|
|
3088
3222
|
cancelHiddenShim();
|
|
3089
3223
|
if (thinkingParser) {
|
|
@@ -3142,9 +3276,14 @@ ${currentContent}`;
|
|
|
3142
3276
|
meteringCredits = event.data.usage;
|
|
3143
3277
|
break;
|
|
3144
3278
|
}
|
|
3279
|
+
case "metadata": {
|
|
3280
|
+
if (event.data.stopReason)
|
|
3281
|
+
serverStopReason = event.data.stopReason;
|
|
3282
|
+
break;
|
|
3283
|
+
}
|
|
3145
3284
|
case "error": {
|
|
3146
3285
|
streamError = event.data.message ? `${event.data.error}: ${event.data.message}` : event.data.error;
|
|
3147
|
-
logStreamError({
|
|
3286
|
+
fileLog.logStreamError({
|
|
3148
3287
|
error: streamError,
|
|
3149
3288
|
context: "stream_event",
|
|
3150
3289
|
model: model.id,
|
|
@@ -3161,11 +3300,12 @@ ${currentContent}`;
|
|
|
3161
3300
|
if (idleTimer)
|
|
3162
3301
|
clearTimeout(idleTimer);
|
|
3163
3302
|
if (firstTokenTimedOut || idleCancelled || streamError) {
|
|
3164
|
-
|
|
3303
|
+
const alreadyStreamed = totalContent.length > 0 || emittedToolCalls > 0;
|
|
3304
|
+
if (!alreadyStreamed && retryCount < MAX_RETRIES) {
|
|
3165
3305
|
retryCount++;
|
|
3166
3306
|
const delayMs = exponentialBackoff(retryCount - 1, 1000, MAX_RETRY_DELAY_MS);
|
|
3167
3307
|
const streamErrDesc = firstTokenTimedOut ? "first-token timed out" : idleCancelled ? "idle timed out" : `error: ${streamError}`;
|
|
3168
|
-
logStreamError({
|
|
3308
|
+
fileLog.logStreamError({
|
|
3169
3309
|
error: streamErrDesc,
|
|
3170
3310
|
context: "retry",
|
|
3171
3311
|
model: model.id,
|
|
@@ -3178,9 +3318,13 @@ ${currentContent}`;
|
|
|
3178
3318
|
textBlockIndex = null;
|
|
3179
3319
|
continue;
|
|
3180
3320
|
}
|
|
3181
|
-
if (streamError)
|
|
3182
|
-
throw new Error(`Kiro API stream error after max retries: ${streamError}`);
|
|
3183
|
-
|
|
3321
|
+
if (streamError) {
|
|
3322
|
+
throw new Error(`Kiro API stream error${alreadyStreamed ? " after partial output" : " after max retries"}: ${streamError}`);
|
|
3323
|
+
}
|
|
3324
|
+
if (!alreadyStreamed) {
|
|
3325
|
+
throw new Error(`Kiro API error: ${firstTokenTimedOut ? "first token" : "idle"} timeout after max retries`);
|
|
3326
|
+
}
|
|
3327
|
+
log.info(`stream ${firstTokenTimedOut ? "first-token" : "idle"} timeout after partial output — finalizing with partial content`);
|
|
3184
3328
|
}
|
|
3185
3329
|
cancelHiddenShim();
|
|
3186
3330
|
if (currentToolCall && emitToolCall(currentToolCall, output, stream))
|
|
@@ -3230,7 +3374,10 @@ ${currentContent}`;
|
|
|
3230
3374
|
log.info(`empty response persisted after ${MAX_RETRIES} retries`);
|
|
3231
3375
|
cancelHiddenShim();
|
|
3232
3376
|
}
|
|
3233
|
-
|
|
3377
|
+
const mappedServerStop = mapKiroStopReason(serverStopReason);
|
|
3378
|
+
if (mappedServerStop) {
|
|
3379
|
+
output.stopReason = mappedServerStop;
|
|
3380
|
+
} else if (!receivedContextUsage && emittedToolCalls === 0) {
|
|
3234
3381
|
output.stopReason = "length";
|
|
3235
3382
|
} else {
|
|
3236
3383
|
output.stopReason = emittedToolCalls > 0 ? "toolUse" : "stop";
|
|
@@ -3246,7 +3393,7 @@ ${currentContent}`;
|
|
|
3246
3393
|
sawAnyToolCalls,
|
|
3247
3394
|
usage: output.usage
|
|
3248
3395
|
});
|
|
3249
|
-
logResponseDone({
|
|
3396
|
+
fileLog.logResponseDone({
|
|
3250
3397
|
stopReason: output.stopReason,
|
|
3251
3398
|
emittedToolCalls,
|
|
3252
3399
|
usage: output.usage,
|
|
@@ -3260,11 +3407,11 @@ ${currentContent}`;
|
|
|
3260
3407
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
3261
3408
|
output.errorMessage = error instanceof Error ? error.message : String(error);
|
|
3262
3409
|
log.debug("response.caught", { stopReason: output.stopReason, error: output.errorMessage });
|
|
3263
|
-
logCaughtError({
|
|
3410
|
+
fileLog.logCaughtError({
|
|
3264
3411
|
stopReason: output.stopReason,
|
|
3265
3412
|
errorMessage: output.errorMessage,
|
|
3266
3413
|
model: model.id
|
|
3267
|
-
});
|
|
3414
|
+
}, error);
|
|
3268
3415
|
if (hiddenShimTimer) {
|
|
3269
3416
|
clearTimeout(hiddenShimTimer);
|
|
3270
3417
|
hiddenShimTimer = null;
|
|
@@ -3282,6 +3429,7 @@ ${currentContent}`;
|
|
|
3282
3429
|
|
|
3283
3430
|
// src/server.ts
|
|
3284
3431
|
init_debug();
|
|
3432
|
+
init_file_logger();
|
|
3285
3433
|
init_models();
|
|
3286
3434
|
init_models();
|
|
3287
3435
|
|
|
@@ -3715,6 +3863,7 @@ function getDashboardHtml() {
|
|
|
3715
3863
|
|
|
3716
3864
|
// src/server.ts
|
|
3717
3865
|
var _creds = null;
|
|
3866
|
+
var _refreshInFlight = null;
|
|
3718
3867
|
async function initGatewayAuth() {
|
|
3719
3868
|
try {
|
|
3720
3869
|
const { importFromKiroCli: importFromKiroCli2 } = await Promise.resolve().then(() => (init_kiro_cli_sync(), exports_kiro_cli_sync));
|
|
@@ -3727,7 +3876,9 @@ async function initGatewayAuth() {
|
|
|
3727
3876
|
imported.refreshToken,
|
|
3728
3877
|
imported.clientId || "",
|
|
3729
3878
|
imported.clientSecret || "",
|
|
3730
|
-
imported.authMethod
|
|
3879
|
+
imported.authMethod,
|
|
3880
|
+
imported.source || "",
|
|
3881
|
+
imported.tokenKey || ""
|
|
3731
3882
|
];
|
|
3732
3883
|
_creds = {
|
|
3733
3884
|
accessToken: imported.accessToken,
|
|
@@ -3772,15 +3923,37 @@ async function getAccessToken() {
|
|
|
3772
3923
|
if (!_creds)
|
|
3773
3924
|
throw new Error("Kiro credentials not initialized — run /login kiro");
|
|
3774
3925
|
if (Date.now() >= _creds.expiresAt) {
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3926
|
+
if (!_refreshInFlight) {
|
|
3927
|
+
const creds = _creds;
|
|
3928
|
+
_refreshInFlight = (async () => {
|
|
3929
|
+
log.info("[gateway-auth] Token expired, refreshing...");
|
|
3930
|
+
const refreshed = await refreshKiroToken(creds.refreshPacked, creds.region, creds.authMethod);
|
|
3931
|
+
creds.accessToken = refreshed.access;
|
|
3932
|
+
creds.refreshPacked = refreshed.refresh;
|
|
3933
|
+
creds.expiresAt = refreshed.expires;
|
|
3934
|
+
log.info("[gateway-auth] Token refreshed successfully");
|
|
3935
|
+
})().finally(() => {
|
|
3936
|
+
_refreshInFlight = null;
|
|
3937
|
+
});
|
|
3938
|
+
}
|
|
3939
|
+
await _refreshInFlight;
|
|
3781
3940
|
}
|
|
3782
3941
|
return _creds.accessToken;
|
|
3783
3942
|
}
|
|
3943
|
+
function isLocalhostOrigin(origin) {
|
|
3944
|
+
try {
|
|
3945
|
+
const host = new URL(origin).hostname;
|
|
3946
|
+
return host === "127.0.0.1" || host === "localhost" || host === "::1" || host === "[::1]";
|
|
3947
|
+
} catch {
|
|
3948
|
+
return false;
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
function isDisallowedBrowserRequest(req) {
|
|
3952
|
+
const origin = req.headers.get("origin");
|
|
3953
|
+
if (!origin)
|
|
3954
|
+
return false;
|
|
3955
|
+
return !isLocalhostOrigin(origin);
|
|
3956
|
+
}
|
|
3784
3957
|
function anthropicError(status, type, message) {
|
|
3785
3958
|
return new Response(JSON.stringify({ type: "error", error: { type, message } }), {
|
|
3786
3959
|
status,
|
|
@@ -3800,6 +3973,56 @@ function isTitleGenerationRequest(messages) {
|
|
|
3800
3973
|
}
|
|
3801
3974
|
return false;
|
|
3802
3975
|
}
|
|
3976
|
+
function shortHash(input) {
|
|
3977
|
+
return createHash4("sha256").update(input).digest("hex").slice(0, 12);
|
|
3978
|
+
}
|
|
3979
|
+
function firstUserMessageText(messages) {
|
|
3980
|
+
for (const m of messages) {
|
|
3981
|
+
if (m?.role !== "user")
|
|
3982
|
+
continue;
|
|
3983
|
+
if (typeof m.content === "string")
|
|
3984
|
+
return m.content;
|
|
3985
|
+
if (Array.isArray(m.content)) {
|
|
3986
|
+
const text = m.content.map((b) => typeof b === "string" ? b : b?.text || "").join(" ").trim();
|
|
3987
|
+
if (text)
|
|
3988
|
+
return text;
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
return "";
|
|
3992
|
+
}
|
|
3993
|
+
function conversationSeed(messages) {
|
|
3994
|
+
const text = firstUserMessageText(messages);
|
|
3995
|
+
if (text)
|
|
3996
|
+
return text;
|
|
3997
|
+
if (messages.length > 0) {
|
|
3998
|
+
try {
|
|
3999
|
+
return "msg0:" + JSON.stringify(messages[0]);
|
|
4000
|
+
} catch {}
|
|
4001
|
+
}
|
|
4002
|
+
return "";
|
|
4003
|
+
}
|
|
4004
|
+
function deriveLogSessionId(body, messages, headers) {
|
|
4005
|
+
const headerId = headers?.get("x-session-id") || headers?.get("x-kiro-session-id") || headers?.get("anthropic-session-id");
|
|
4006
|
+
if (headerId && headerId.trim().length > 0) {
|
|
4007
|
+
return `s-${shortHash(headerId.trim())}`;
|
|
4008
|
+
}
|
|
4009
|
+
const userId = body?.metadata?.user_id;
|
|
4010
|
+
if (typeof userId === "string" && userId.trim().length > 0) {
|
|
4011
|
+
return `u-${shortHash(userId.trim())}`;
|
|
4012
|
+
}
|
|
4013
|
+
const seed = conversationSeed(messages);
|
|
4014
|
+
if (isTitleGenerationRequest(messages)) {
|
|
4015
|
+
return `title-${shortHash(seed || "untitled")}`;
|
|
4016
|
+
}
|
|
4017
|
+
if (seed) {
|
|
4018
|
+
return `c-${shortHash(seed)}`;
|
|
4019
|
+
}
|
|
4020
|
+
try {
|
|
4021
|
+
return `c-${shortHash(JSON.stringify(body))}`;
|
|
4022
|
+
} catch {
|
|
4023
|
+
return `c-${shortHash(String(Date.now()))}`;
|
|
4024
|
+
}
|
|
4025
|
+
}
|
|
3803
4026
|
function stripTitleMarkdown(text) {
|
|
3804
4027
|
let t = text.trim();
|
|
3805
4028
|
let prev;
|
|
@@ -3823,13 +4046,16 @@ function startGatewayServer(port = 0) {
|
|
|
3823
4046
|
idleTimeout: 255,
|
|
3824
4047
|
async fetch(req) {
|
|
3825
4048
|
if (req.method === "OPTIONS") {
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
4049
|
+
const origin = req.headers.get("origin");
|
|
4050
|
+
const headers = {
|
|
4051
|
+
"Access-Control-Allow-Methods": "POST, GET, OPTIONS",
|
|
4052
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
4053
|
+
};
|
|
4054
|
+
if (origin && isLocalhostOrigin(origin)) {
|
|
4055
|
+
headers["Access-Control-Allow-Origin"] = origin;
|
|
4056
|
+
headers["Vary"] = "Origin";
|
|
4057
|
+
}
|
|
4058
|
+
return new Response(null, { headers });
|
|
3833
4059
|
}
|
|
3834
4060
|
const url = new URL(req.url);
|
|
3835
4061
|
if (url.pathname === "/dashboard") {
|
|
@@ -3878,6 +4104,9 @@ function startGatewayServer(port = 0) {
|
|
|
3878
4104
|
}
|
|
3879
4105
|
}
|
|
3880
4106
|
if ((url.pathname === "/v1/messages" || url.pathname === "/messages") && req.method === "POST") {
|
|
4107
|
+
if (isDisallowedBrowserRequest(req)) {
|
|
4108
|
+
return anthropicError(403, "invalid_request_error", "Cross-origin requests are not allowed");
|
|
4109
|
+
}
|
|
3881
4110
|
let accessToken;
|
|
3882
4111
|
try {
|
|
3883
4112
|
accessToken = await getAccessToken();
|
|
@@ -3902,6 +4131,9 @@ function startGatewayServer(port = 0) {
|
|
|
3902
4131
|
}
|
|
3903
4132
|
const streamRequested = !!body.stream;
|
|
3904
4133
|
const temperature = body.temperature ?? 0.5;
|
|
4134
|
+
const maxTokens = typeof body.max_tokens === "number" ? body.max_tokens : undefined;
|
|
4135
|
+
const logSessionId = deriveLogSessionId(body, anthropicMessages, req.headers);
|
|
4136
|
+
enterSessionLog(logSessionId);
|
|
3905
4137
|
log.debug(`[gateway] sys=${systemPrompt.length}c msgs=${anthropicMessages.length} tools=${body.tools?.length ?? 0}`);
|
|
3906
4138
|
try {
|
|
3907
4139
|
const piMessages = translateAnthropicToPi(anthropicMessages);
|
|
@@ -3934,7 +4166,10 @@ function startGatewayServer(port = 0) {
|
|
|
3934
4166
|
const kiroStream = streamKiro(piModel, context, {
|
|
3935
4167
|
apiKey: accessToken,
|
|
3936
4168
|
reasoning: reasoningEffort,
|
|
3937
|
-
temperature
|
|
4169
|
+
temperature,
|
|
4170
|
+
maxTokens,
|
|
4171
|
+
sessionId: logSessionId,
|
|
4172
|
+
logSessionId
|
|
3938
4173
|
});
|
|
3939
4174
|
if (streamRequested) {
|
|
3940
4175
|
const iter = kiroStream[Symbol.asyncIterator]();
|
|
@@ -3970,6 +4205,29 @@ function startGatewayServer(port = 0) {
|
|
|
3970
4205
|
}
|
|
3971
4206
|
const streamResponse = new ReadableStream({
|
|
3972
4207
|
async start(controller) {
|
|
4208
|
+
const PING_INTERVAL_MS = 15000;
|
|
4209
|
+
let lastActivity = Date.now();
|
|
4210
|
+
let pingTimer = null;
|
|
4211
|
+
const stopHeartbeat = () => {
|
|
4212
|
+
if (pingTimer) {
|
|
4213
|
+
clearInterval(pingTimer);
|
|
4214
|
+
pingTimer = null;
|
|
4215
|
+
}
|
|
4216
|
+
};
|
|
4217
|
+
const startHeartbeat = () => {
|
|
4218
|
+
pingTimer = setInterval(() => {
|
|
4219
|
+
if (Date.now() - lastActivity < PING_INTERVAL_MS)
|
|
4220
|
+
return;
|
|
4221
|
+
try {
|
|
4222
|
+
controller.enqueue(`event: ping
|
|
4223
|
+
data: {"type":"ping"}
|
|
4224
|
+
|
|
4225
|
+
`);
|
|
4226
|
+
} catch {
|
|
4227
|
+
stopHeartbeat();
|
|
4228
|
+
}
|
|
4229
|
+
}, PING_INTERVAL_MS);
|
|
4230
|
+
};
|
|
3973
4231
|
try {
|
|
3974
4232
|
const msgId = `msg_${crypto.randomUUID()}`;
|
|
3975
4233
|
controller.enqueue(`event: message_start
|
|
@@ -3988,6 +4246,7 @@ data: ` + JSON.stringify({
|
|
|
3988
4246
|
}) + `
|
|
3989
4247
|
|
|
3990
4248
|
`);
|
|
4249
|
+
startHeartbeat();
|
|
3991
4250
|
let contentBlockIndex = 0;
|
|
3992
4251
|
let activeBlockType = null;
|
|
3993
4252
|
let titleTextBuffer = "";
|
|
@@ -4021,6 +4280,7 @@ data: ` + JSON.stringify({
|
|
|
4021
4280
|
`);
|
|
4022
4281
|
};
|
|
4023
4282
|
const processEvent = (event) => {
|
|
4283
|
+
lastActivity = Date.now();
|
|
4024
4284
|
if (event.type === "thinking_delta") {
|
|
4025
4285
|
ensureBlockStarted("thinking");
|
|
4026
4286
|
controller.enqueue(`event: content_block_delta
|
|
@@ -4102,8 +4362,22 @@ data: ` + JSON.stringify({
|
|
|
4102
4362
|
`);
|
|
4103
4363
|
}
|
|
4104
4364
|
closeActiveBlock();
|
|
4105
|
-
let finishReason = "end_turn";
|
|
4106
4365
|
const finalMsg = await kiroStream.result();
|
|
4366
|
+
if (finalMsg.stopReason === "error" || finalMsg.errorMessage) {
|
|
4367
|
+
controller.enqueue(`event: error
|
|
4368
|
+
data: ` + JSON.stringify({
|
|
4369
|
+
type: "error",
|
|
4370
|
+
error: {
|
|
4371
|
+
type: "api_error",
|
|
4372
|
+
message: finalMsg.errorMessage || "Kiro stream error"
|
|
4373
|
+
}
|
|
4374
|
+
}) + `
|
|
4375
|
+
|
|
4376
|
+
`);
|
|
4377
|
+
controller.close();
|
|
4378
|
+
return;
|
|
4379
|
+
}
|
|
4380
|
+
let finishReason = "end_turn";
|
|
4107
4381
|
if (finalMsg.content.some((b) => b.type === "toolCall")) {
|
|
4108
4382
|
finishReason = "tool_use";
|
|
4109
4383
|
}
|
|
@@ -4151,6 +4425,8 @@ data: ` + JSON.stringify({
|
|
|
4151
4425
|
|
|
4152
4426
|
`);
|
|
4153
4427
|
controller.close();
|
|
4428
|
+
} finally {
|
|
4429
|
+
stopHeartbeat();
|
|
4154
4430
|
}
|
|
4155
4431
|
}
|
|
4156
4432
|
});
|
|
@@ -4164,6 +4440,9 @@ data: ` + JSON.stringify({
|
|
|
4164
4440
|
});
|
|
4165
4441
|
} else {
|
|
4166
4442
|
const finalMsg = await kiroStream.result();
|
|
4443
|
+
if (finalMsg.stopReason === "error" || finalMsg.errorMessage) {
|
|
4444
|
+
return anthropicError(502, "api_error", `Kiro: ${finalMsg.errorMessage || "stream error"}`);
|
|
4445
|
+
}
|
|
4167
4446
|
const contentParts = finalMsg.content;
|
|
4168
4447
|
const anthropicContent = [];
|
|
4169
4448
|
for (const part of contentParts) {
|
|
@@ -4247,9 +4526,20 @@ function translateAnthropicToPi(messages) {
|
|
|
4247
4526
|
timestamp: Date.now()
|
|
4248
4527
|
});
|
|
4249
4528
|
} else if (Array.isArray(msg.content)) {
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4529
|
+
let pendingUserParts = [];
|
|
4530
|
+
const flushUserParts = () => {
|
|
4531
|
+
if (pendingUserParts.length > 0) {
|
|
4532
|
+
piMessages.push({
|
|
4533
|
+
role: "user",
|
|
4534
|
+
content: pendingUserParts,
|
|
4535
|
+
timestamp: Date.now()
|
|
4536
|
+
});
|
|
4537
|
+
pendingUserParts = [];
|
|
4538
|
+
}
|
|
4539
|
+
};
|
|
4540
|
+
for (const part of msg.content) {
|
|
4541
|
+
if (part.type === "tool_result") {
|
|
4542
|
+
flushUserParts();
|
|
4253
4543
|
piMessages.push({
|
|
4254
4544
|
role: "toolResult",
|
|
4255
4545
|
toolCallId: part.tool_use_id,
|
|
@@ -4258,24 +4548,13 @@ function translateAnthropicToPi(messages) {
|
|
|
4258
4548
|
isError: part.is_error || false,
|
|
4259
4549
|
timestamp: Date.now()
|
|
4260
4550
|
});
|
|
4551
|
+
} else if (part.type === "text") {
|
|
4552
|
+
pendingUserParts.push({ type: "text", text: part.text });
|
|
4553
|
+
} else if (part.type === "image" && part.source?.type === "base64") {
|
|
4554
|
+
pendingUserParts.push({ type: "image", mimeType: part.source.media_type, data: part.source.data });
|
|
4261
4555
|
}
|
|
4262
4556
|
}
|
|
4263
|
-
|
|
4264
|
-
if (part.type === "text") {
|
|
4265
|
-
return { type: "text", text: part.text };
|
|
4266
|
-
}
|
|
4267
|
-
if (part.type === "image" && part.source?.type === "base64") {
|
|
4268
|
-
return { type: "image", mimeType: part.source.media_type, data: part.source.data };
|
|
4269
|
-
}
|
|
4270
|
-
return null;
|
|
4271
|
-
}).filter(Boolean);
|
|
4272
|
-
if (otherParts.length > 0) {
|
|
4273
|
-
piMessages.push({
|
|
4274
|
-
role: "user",
|
|
4275
|
-
content: otherParts,
|
|
4276
|
-
timestamp: Date.now()
|
|
4277
|
-
});
|
|
4278
|
-
}
|
|
4557
|
+
flushUserParts();
|
|
4279
4558
|
}
|
|
4280
4559
|
} else if (msg.role === "assistant") {
|
|
4281
4560
|
const contentParts = [];
|
|
@@ -4321,7 +4600,7 @@ function translateAnthropicToolsToPi(tools) {
|
|
|
4321
4600
|
init_debug();
|
|
4322
4601
|
init_models();
|
|
4323
4602
|
process.env.KIRO_LOG = process.env.KIRO_LOG || "debug";
|
|
4324
|
-
process.env.KIRO_LOG_FILE = process.env.KIRO_LOG_FILE || "/tmp/
|
|
4603
|
+
process.env.KIRO_LOG_FILE = process.env.KIRO_LOG_FILE || "/tmp/kiro-logs/session-gateway.log";
|
|
4325
4604
|
var gatewayServer = null;
|
|
4326
4605
|
var KiroPlugin = async (input) => {
|
|
4327
4606
|
const client = input.client;
|