@i14ks/ccv 0.5.0 → 0.6.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/bundle/ccv.mjs +263 -19
- package/package.json +1 -1
- package/web/assets/{codemirror-BWZxIjMU.js → codemirror-YAsAN8OM.js} +1 -1
- package/web/assets/index-05EaTJ2I.js +129 -0
- package/web/assets/{index-9jmr6xz8.js → index-BANX3gfG.js} +1 -1
- package/web/assets/{index-DkpH15La.js → index-Bj-McB2J.js} +1 -1
- package/web/assets/{index-BQi6QI7Z.js → index-BqaOf3lL.js} +1 -1
- package/web/assets/{index-CjAk3Yb0.js → index-C6bp7gLM.js} +1 -1
- package/web/assets/index-CJKd_3HV.css +1 -0
- package/web/assets/{index-AGqthGr9.js → index-CStpUyOa.js} +1 -1
- package/web/assets/{index-Y1vKLu9k.js → index-CaI4eesN.js} +1 -1
- package/web/assets/{index-qPJ6SIgP.js → index-CbXyt5BH.js} +1 -1
- package/web/assets/{index-DEFc0MJB.js → index-CbkTwf-a.js} +1 -1
- package/web/assets/{index-DGCeaXoo.js → index-Cw0TSvLQ.js} +1 -1
- package/web/assets/{index-BP5TdYoo.js → index-DQD2YqR9.js} +1 -1
- package/web/assets/{index-Dbr7EU2n.js → index-DSWJFoDR.js} +1 -1
- package/web/assets/{index-CqqEs8q5.js → index-DWdTnlsP.js} +1 -1
- package/web/assets/{index-DdAPIomJ.js → index-Dr7O0ILC.js} +1 -1
- package/web/assets/{index-C9yuVPI-.js → index-DwCBmVAn.js} +1 -1
- package/web/assets/{index-Dr8ovngt.js → index-JfSeUxQA.js} +1 -1
- package/web/assets/{index-hQhZ9dRw.js → index-YTb4ol7S.js} +1 -1
- package/web/assets/{index-C_UZRXvz.js → index-bVu7H3ZX.js} +1 -1
- package/web/assets/{index-By-XqFTs.js → index-khA8Gu_K.js} +1 -1
- package/web/assets/{index-DYzVllNN.js → index-po851PkX.js} +1 -1
- package/web/index.html +2 -2
- package/web/assets/index-DnC6QasI.js +0 -128
- package/web/assets/index-xkYUlK3R.css +0 -1
package/bundle/ccv.mjs
CHANGED
|
@@ -953,11 +953,16 @@ var STOPPED_REASONS = /* @__PURE__ */ new Set([
|
|
|
953
953
|
"tool_deferred_unavailable",
|
|
954
954
|
"background_requested"
|
|
955
955
|
]);
|
|
956
|
+
var API_ERROR_REASON = "api_error";
|
|
957
|
+
var PERMANENT_API_STATUS = /* @__PURE__ */ new Set([400, 404, 413, 422, 429]);
|
|
958
|
+
var RECONNECT_DELAYS_MS = [1e3, 5e3, 2e4, 6e4];
|
|
956
959
|
function turnOutcome(terminalReason, subtype, interruptRequested) {
|
|
957
960
|
if (terminalReason && ABORTED_REASONS.has(terminalReason))
|
|
958
961
|
return "interrupted";
|
|
959
962
|
if (terminalReason && STOPPED_REASONS.has(terminalReason))
|
|
960
963
|
return "stopped";
|
|
964
|
+
if (terminalReason === API_ERROR_REASON)
|
|
965
|
+
return "failed";
|
|
961
966
|
if (subtype === "success")
|
|
962
967
|
return "completed";
|
|
963
968
|
if (interruptRequested)
|
|
@@ -1134,6 +1139,37 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1134
1139
|
/** Когда была последняя попытка его получить — удачная или нет. */
|
|
1135
1140
|
#contextAt = 0;
|
|
1136
1141
|
#contextInFlight = false;
|
|
1142
|
+
/**
|
|
1143
|
+
* Сессию закрывают намеренно — переподключаться не надо.
|
|
1144
|
+
*
|
|
1145
|
+
* Обрыв потока сам по себе не говорит, что случилось: и `close()`, и упавший
|
|
1146
|
+
* VPN выглядят одинаково — итератор кончился. Отличает их только этот флаг.
|
|
1147
|
+
*/
|
|
1148
|
+
#closing = false;
|
|
1149
|
+
/** Сколько раз подряд уже пробовали переподключиться; сбрасывается удачей. */
|
|
1150
|
+
#reconnectAttempt = 0;
|
|
1151
|
+
#reconnectTimer = null;
|
|
1152
|
+
/**
|
|
1153
|
+
* Промпты, которые не доехали: их отправят заново, когда сессия вернётся.
|
|
1154
|
+
*
|
|
1155
|
+
* Повторяем только то, что заведомо ничего не успело сделать (см.
|
|
1156
|
+
* `#turnDidWork`), — иначе повтор доделывал бы работу второй раз.
|
|
1157
|
+
*
|
|
1158
|
+
* Список, а не одно значение: пока сессия ищет себе новый процесс, человек
|
|
1159
|
+
* вполне может дописать ещё реплику — и в ленте она уже есть. Затереть ею
|
|
1160
|
+
* несостоявшуюся первую значило бы показывать вопрос, на который никто
|
|
1161
|
+
* никогда не ответит.
|
|
1162
|
+
*/
|
|
1163
|
+
#pendingRetry = [];
|
|
1164
|
+
/** Промпт текущего хода — то, что придётся отправить заново, если ход сорвётся. */
|
|
1165
|
+
#lastPrompt = null;
|
|
1166
|
+
/**
|
|
1167
|
+
* Ход успел что-то предпринять: вызвал инструмент или сказал что-то своё.
|
|
1168
|
+
*
|
|
1169
|
+
* Граница между «запрос не ушёл» и «работа началась». Повторять можно только
|
|
1170
|
+
* первое: ход, успевший записать файл, при повторе запишет его дважды.
|
|
1171
|
+
*/
|
|
1172
|
+
#turnDidWork = false;
|
|
1137
1173
|
#createdAt = Date.now();
|
|
1138
1174
|
#updatedAt = Date.now();
|
|
1139
1175
|
#options;
|
|
@@ -1214,6 +1250,17 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1214
1250
|
const until = Date.now();
|
|
1215
1251
|
this.#history = readTranscript(this.#options.resume, { dir: this.cwd, until });
|
|
1216
1252
|
}
|
|
1253
|
+
this.#spawn(false);
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Поднять сессии процесс CLI — первый раз или взамен оборвавшегося.
|
|
1257
|
+
*
|
|
1258
|
+
* Всё, что отличает переподключение от старта, собрано в `restarted`:
|
|
1259
|
+
* транскрипт при перезапуске не перечитывается (разговор уже в журнале),
|
|
1260
|
+
* продолжаем всегда свою собственную сессию, а настройки, выбранные человеком
|
|
1261
|
+
* на лету, отправляются в новый процесс заново — из файлов он их не возьмёт.
|
|
1262
|
+
*/
|
|
1263
|
+
#spawn(restarted) {
|
|
1217
1264
|
const options2 = {
|
|
1218
1265
|
cwd: this.cwd,
|
|
1219
1266
|
// Без этого SDK не читает с диска ничего: ни CLAUDE.md, ни кастомные
|
|
@@ -1256,9 +1303,10 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1256
1303
|
}
|
|
1257
1304
|
if (this.#model)
|
|
1258
1305
|
options2.model = this.#model;
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1306
|
+
const resume = this.#claudeSessionId ?? this.#options.resume;
|
|
1307
|
+
if (resume)
|
|
1308
|
+
options2.resume = resume;
|
|
1309
|
+
if (this.#options.fork && !this.#claudeSessionId)
|
|
1262
1310
|
options2.forkSession = true;
|
|
1263
1311
|
if (this.#options.outputFormat)
|
|
1264
1312
|
options2.outputFormat = this.#options.outputFormat;
|
|
@@ -1267,9 +1315,60 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1267
1315
|
options2.allowedTools = READ_ONLY_TOOLS;
|
|
1268
1316
|
options2.strictMcpConfig = true;
|
|
1269
1317
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
void this.#
|
|
1318
|
+
const q = query({ prompt: this.#inbox, options: options2 });
|
|
1319
|
+
this.#query = q;
|
|
1320
|
+
void this.#consume(q);
|
|
1321
|
+
if (restarted)
|
|
1322
|
+
void this.#resume(q);
|
|
1323
|
+
else
|
|
1324
|
+
void this.#loadCatalog();
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Довести поднятый заново процесс до рабочего состояния.
|
|
1328
|
+
*
|
|
1329
|
+
* Ждём, пока он отзовётся, возвращаем ему настройки, выбранные на лету, и
|
|
1330
|
+
* только потом отпускаем накопленные промпты.
|
|
1331
|
+
*
|
|
1332
|
+
* Ждать приходится явно: `system/init` в streaming-режиме приезжает вместе с
|
|
1333
|
+
* первым ответом, то есть уже после промпта, — и сессия, которая ждала бы
|
|
1334
|
+
* его, чтобы промпт отправить, ждала бы вечно. `initializationResult()`,
|
|
1335
|
+
* наоборот, поднимает CLI сам и не требует, чтобы его о чём-то спросили.
|
|
1336
|
+
*/
|
|
1337
|
+
async #resume(q) {
|
|
1338
|
+
const ready = await q.initializationResult().then(() => true, () => false);
|
|
1339
|
+
if (q !== this.#query)
|
|
1340
|
+
return;
|
|
1341
|
+
if (!ready) {
|
|
1342
|
+
this.#scheduleReconnect("\u043D\u043E\u0432\u044B\u0439 \u043F\u0440\u043E\u0446\u0435\u0441\u0441 CLI \u043D\u0435 \u043E\u0442\u043E\u0437\u0432\u0430\u043B\u0441\u044F");
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
try {
|
|
1346
|
+
await q.applyFlagSettings({
|
|
1347
|
+
...this.#effort ? { effortLevel: this.#effort } : {},
|
|
1348
|
+
ultracode: this.#ultracode,
|
|
1349
|
+
alwaysThinkingEnabled: this.#thinking,
|
|
1350
|
+
switchModelsOnFlag: this.#switchModelsOnFlag
|
|
1351
|
+
});
|
|
1352
|
+
} catch {
|
|
1353
|
+
}
|
|
1354
|
+
if (q !== this.#query)
|
|
1355
|
+
return;
|
|
1356
|
+
const retry = this.#pendingRetry;
|
|
1357
|
+
this.#pendingRetry = [];
|
|
1358
|
+
if (retry.length === 0) {
|
|
1359
|
+
this.#setState("idle");
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
this.#record({
|
|
1363
|
+
kind: "error",
|
|
1364
|
+
message: retry.length === 1 ? "\u0421\u0435\u0441\u0441\u0438\u044F \u043F\u043E\u0434\u043D\u044F\u0442\u0430 \u0437\u0430\u043D\u043E\u0432\u043E, \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u044E \u0437\u0430\u043F\u0440\u043E\u0441." : `\u0421\u0435\u0441\u0441\u0438\u044F \u043F\u043E\u0434\u043D\u044F\u0442\u0430 \u0437\u0430\u043D\u043E\u0432\u043E, \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u044F\u044E \u043D\u0430\u043A\u043E\u043F\u043B\u0435\u043D\u043D\u043E\u0435 (${retry.length}).`
|
|
1365
|
+
});
|
|
1366
|
+
this.#lastTurn = null;
|
|
1367
|
+
this.#turnDidWork = false;
|
|
1368
|
+
this.#lastPrompt = retry[retry.length - 1] ?? null;
|
|
1369
|
+
this.#setState("running");
|
|
1370
|
+
for (const text of retry)
|
|
1371
|
+
this.#push(text);
|
|
1273
1372
|
}
|
|
1274
1373
|
/**
|
|
1275
1374
|
* Что известно о сессии до system/init.
|
|
@@ -1294,6 +1393,22 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1294
1393
|
};
|
|
1295
1394
|
}
|
|
1296
1395
|
async close(reason = "closed by client") {
|
|
1396
|
+
this.#closing = true;
|
|
1397
|
+
if (this.#reconnectTimer) {
|
|
1398
|
+
clearTimeout(this.#reconnectTimer);
|
|
1399
|
+
this.#reconnectTimer = null;
|
|
1400
|
+
}
|
|
1401
|
+
this.#teardown(reason);
|
|
1402
|
+
this.#setState("closed");
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Отпустить текущий процесс CLI и всё, что на нём висело.
|
|
1406
|
+
*
|
|
1407
|
+
* Общее у закрытия и переподключения: и там, и там прежний процесс больше не
|
|
1408
|
+
* ответит, а незакрытые запросы прав, если их не разрешить, повиснут навсегда
|
|
1409
|
+
* — SDK ждёт промис, которому уже некому прийти.
|
|
1410
|
+
*/
|
|
1411
|
+
#teardown(reason) {
|
|
1297
1412
|
this.#inbox.close();
|
|
1298
1413
|
for (const [requestId, pending] of this.#pendingPermissions) {
|
|
1299
1414
|
pending.resolve({ behavior: "deny", message: reason });
|
|
@@ -1303,29 +1418,119 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1303
1418
|
this.#query?.close();
|
|
1304
1419
|
} catch {
|
|
1305
1420
|
}
|
|
1306
|
-
this.#
|
|
1421
|
+
this.#query = null;
|
|
1422
|
+
}
|
|
1423
|
+
// -------------------------------------------------------------------------
|
|
1424
|
+
// Переподключение
|
|
1425
|
+
// -------------------------------------------------------------------------
|
|
1426
|
+
/**
|
|
1427
|
+
* Поднять сессии новый процесс CLI, сохранив разговор.
|
|
1428
|
+
*
|
|
1429
|
+
* Зачем вообще: у CLI, которому запрос к API вернул 403 (упал VPN — сеть
|
|
1430
|
+
* отвечает вместо API), состояние аутентификации портится на всю жизнь
|
|
1431
|
+
* процесса. Разговор при этом цел, ключи целы, VPN через минуту вернулся — а
|
|
1432
|
+
* сессия так и отвечает «Failed to authenticate» на каждый промпт, пока её не
|
|
1433
|
+
* закроют и не откроют заново. Собственно, единственным лекарством и был этот
|
|
1434
|
+
* ручной круг: закрыть, открыть, продолжить.
|
|
1435
|
+
*
|
|
1436
|
+
* Здесь тот же круг делается сам. Новый процесс продолжает (`resume`) ту же
|
|
1437
|
+
* сессию Claude Code, поэтому разговор, файлы и контекст остаются на месте —
|
|
1438
|
+
* человек видит паузу, а не потерю.
|
|
1439
|
+
*/
|
|
1440
|
+
reconnect(reason) {
|
|
1441
|
+
this.#reconnectAttempt = 0;
|
|
1442
|
+
this.#restart(reason);
|
|
1443
|
+
}
|
|
1444
|
+
/** Собственно смена процесса — без разговора о том, чья это была идея. */
|
|
1445
|
+
#restart(reason) {
|
|
1446
|
+
if (this.#closing || this.#state === "closed")
|
|
1447
|
+
return;
|
|
1448
|
+
if (this.#reconnectTimer) {
|
|
1449
|
+
clearTimeout(this.#reconnectTimer);
|
|
1450
|
+
this.#reconnectTimer = null;
|
|
1451
|
+
}
|
|
1452
|
+
this.#setState("reconnecting");
|
|
1453
|
+
this.#teardown("\u043F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435");
|
|
1454
|
+
this.#inbox = new AsyncQueue();
|
|
1455
|
+
this.#streamMessageId = null;
|
|
1456
|
+
this.#blockCounts.clear();
|
|
1457
|
+
this.#record({ kind: "error", message: `\u041F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0430\u044E\u0441\u044C: ${reason}` });
|
|
1458
|
+
this.#spawn(true);
|
|
1459
|
+
}
|
|
1460
|
+
/**
|
|
1461
|
+
* Попробовать переподключиться ещё раз — с паузой и с оглядкой на счётчик.
|
|
1462
|
+
*
|
|
1463
|
+
* Процесс отпускаем сразу, а поднимаем новый через паузу: между этими двумя
|
|
1464
|
+
* моментами сессия должна быть заведомо без процесса, иначе промпт, посланный
|
|
1465
|
+
* в эту щель, уехал бы в очередь, которую никто не читает, и завис бы там
|
|
1466
|
+
* навсегда — `sendPrompt` отличает эти состояния именно по `#query`.
|
|
1467
|
+
*
|
|
1468
|
+
* Попытки конечны намеренно. Обрыв VPN лечится сам за секунды, а вот
|
|
1469
|
+
* отозванный ключ или снесённый CLI не вылечится никогда, и бесконечный
|
|
1470
|
+
* круг перезапусков в этом случае только жёг бы процессы, показывая человеку
|
|
1471
|
+
* ровно ту же ошибку. Кончились попытки — говорим об этом прямо и ждём
|
|
1472
|
+
* человека: и промпт, и кнопка «переподключить» начинают отсчёт заново.
|
|
1473
|
+
*/
|
|
1474
|
+
#scheduleReconnect(reason) {
|
|
1475
|
+
if (this.#closing || this.#reconnectTimer)
|
|
1476
|
+
return;
|
|
1477
|
+
this.#teardown("\u043F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435");
|
|
1478
|
+
const delay = RECONNECT_DELAYS_MS[this.#reconnectAttempt];
|
|
1479
|
+
if (delay === void 0) {
|
|
1480
|
+
this.#pendingRetry = [];
|
|
1481
|
+
this.#record({
|
|
1482
|
+
kind: "error",
|
|
1483
|
+
message: `\u041F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C: ${reason}. \u041F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435 \u0441\u0435\u0442\u044C \u0438 VPN, \u0430 \u043F\u043E\u0442\u043E\u043C \u043E\u0442\u043F\u0440\u0430\u0432\u044C\u0442\u0435 \u043F\u0440\u043E\u043C\u043F\u0442 \u0438\u043B\u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \xAB\u041F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u0442\u044C\xBB \u2014 \u0440\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D, \u0441\u0435\u0441\u0441\u0438\u044F \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442 \u0441 \u044D\u0442\u043E\u0433\u043E \u043C\u0435\u0441\u0442\u0430.`
|
|
1484
|
+
});
|
|
1485
|
+
this.#setState("error");
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
this.#reconnectAttempt += 1;
|
|
1489
|
+
this.#record({
|
|
1490
|
+
kind: "error",
|
|
1491
|
+
message: `\u0421\u0432\u044F\u0437\u044C \u0441 Claude \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u0430: ${reason}. \u041F\u0435\u0440\u0435\u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 ${Math.round(delay / 1e3)} \u0441 (\u043F\u043E\u043F\u044B\u0442\u043A\u0430 ${this.#reconnectAttempt} \u0438\u0437 ${RECONNECT_DELAYS_MS.length}).`
|
|
1492
|
+
});
|
|
1493
|
+
this.#setState("reconnecting");
|
|
1494
|
+
this.#reconnectTimer = setTimeout(() => {
|
|
1495
|
+
this.#reconnectTimer = null;
|
|
1496
|
+
this.#restart(reason);
|
|
1497
|
+
}, delay);
|
|
1498
|
+
this.#reconnectTimer.unref?.();
|
|
1307
1499
|
}
|
|
1308
1500
|
// -------------------------------------------------------------------------
|
|
1309
1501
|
// Ввод от пользователя
|
|
1310
1502
|
// -------------------------------------------------------------------------
|
|
1311
1503
|
sendPrompt(text) {
|
|
1312
|
-
if (this.#
|
|
1504
|
+
if (this.#closing)
|
|
1313
1505
|
throw new Error("session is closed");
|
|
1314
1506
|
this.#record({ kind: "user.message", text });
|
|
1315
1507
|
if (!this.#title || this.#titleIsDefault) {
|
|
1316
1508
|
this.#title = text.slice(0, 80);
|
|
1317
1509
|
this.#titleIsDefault = false;
|
|
1318
1510
|
}
|
|
1319
|
-
if (this.#state === "error") {
|
|
1320
|
-
this.#record({
|
|
1321
|
-
kind: "error",
|
|
1322
|
-
message: "\u0421\u0435\u0441\u0441\u0438\u044F \u043D\u0435 \u0437\u0430\u043F\u0443\u0449\u0435\u043D\u0430, \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u044F\u0442\u044C \u043F\u0440\u043E\u043C\u043F\u0442 \u043D\u0435\u043A\u0443\u0434\u0430 \u2014 \u0441\u043C. \u043E\u0448\u0438\u0431\u043A\u0443 \u0432\u044B\u0448\u0435."
|
|
1323
|
-
});
|
|
1324
|
-
return;
|
|
1325
|
-
}
|
|
1326
1511
|
this.#lastTurn = null;
|
|
1327
1512
|
this.#interruptRequested = false;
|
|
1513
|
+
this.#lastPrompt = text;
|
|
1514
|
+
this.#turnDidWork = false;
|
|
1515
|
+
if (!this.#query) {
|
|
1516
|
+
this.#pendingRetry.push(text);
|
|
1517
|
+
this.reconnect("\u0441\u0435\u0441\u0441\u0438\u0438 \u043D\u0443\u0436\u0435\u043D \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0434\u043B\u044F \u043D\u043E\u0432\u043E\u0433\u043E \u043F\u0440\u043E\u043C\u043F\u0442\u0430");
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1328
1520
|
this.#setState("running");
|
|
1521
|
+
this.#push(text);
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* Стоит ли вообще переподключаться из-за этого кода ответа.
|
|
1525
|
+
*
|
|
1526
|
+
* Отсутствие кода — тоже повод: ответа не было вовсе (сеть оборвалась на
|
|
1527
|
+
* полуслове), а это ровно тот случай, ради которого всё и затевалось.
|
|
1528
|
+
*/
|
|
1529
|
+
#retryable(status) {
|
|
1530
|
+
return status === void 0 || !PERMANENT_API_STATUS.has(status);
|
|
1531
|
+
}
|
|
1532
|
+
/** Промпт в очередь SDK — без записи в журнал: при повторе она уже там. */
|
|
1533
|
+
#push(text) {
|
|
1329
1534
|
this.#inbox.push({
|
|
1330
1535
|
type: "user",
|
|
1331
1536
|
message: { role: "user", content: [{ type: "text", text }] },
|
|
@@ -1694,13 +1899,27 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1694
1899
|
async #consume(q) {
|
|
1695
1900
|
try {
|
|
1696
1901
|
for await (const message of q) {
|
|
1902
|
+
if (q !== this.#query)
|
|
1903
|
+
return;
|
|
1697
1904
|
this.#handleMessage(message);
|
|
1698
1905
|
}
|
|
1699
|
-
this.#
|
|
1906
|
+
if (q !== this.#query)
|
|
1907
|
+
return;
|
|
1908
|
+
if (this.#closing)
|
|
1909
|
+
this.#setState("closed");
|
|
1910
|
+
else
|
|
1911
|
+
this.#scheduleReconnect("\u043F\u0440\u043E\u0446\u0435\u0441\u0441 CLI \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F");
|
|
1700
1912
|
} catch (error) {
|
|
1913
|
+
if (q !== this.#query || this.#closing)
|
|
1914
|
+
return;
|
|
1701
1915
|
const message = error instanceof Error ? error.message : String(error);
|
|
1702
|
-
|
|
1703
|
-
|
|
1916
|
+
if (!this.#capabilities) {
|
|
1917
|
+
this.#record({ kind: "error", message: message + this.#executableHint() });
|
|
1918
|
+
this.#setState("error");
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
this.#record({ kind: "error", message });
|
|
1922
|
+
this.#scheduleReconnect(message);
|
|
1704
1923
|
}
|
|
1705
1924
|
}
|
|
1706
1925
|
/**
|
|
@@ -1732,7 +1951,10 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1732
1951
|
const usage = message.message.usage;
|
|
1733
1952
|
this.#contextTokens = (usage.input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
|
|
1734
1953
|
}
|
|
1954
|
+
const synthetic2 = isApiErrorMessage(message);
|
|
1735
1955
|
for (const block of message.message.content) {
|
|
1956
|
+
if (!synthetic2)
|
|
1957
|
+
this.#turnDidWork = true;
|
|
1736
1958
|
const blockId = `${message.message.id}:${this.#nextBlockIndex(message.message.id)}`;
|
|
1737
1959
|
if (block.type === "text") {
|
|
1738
1960
|
this.#record({ kind: "assistant.text", text: block.text, blockId });
|
|
@@ -1784,8 +2006,21 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1784
2006
|
text: "result" in message ? message.result : null
|
|
1785
2007
|
});
|
|
1786
2008
|
}
|
|
1787
|
-
|
|
2009
|
+
const terminalReason = "terminal_reason" in message ? message.terminal_reason : void 0;
|
|
2010
|
+
this.#lastTurn = turnOutcome(terminalReason, message.subtype, this.#interruptRequested);
|
|
2011
|
+
const interrupted = this.#interruptRequested;
|
|
1788
2012
|
this.#interruptRequested = false;
|
|
2013
|
+
if (terminalReason === API_ERROR_REASON && !interrupted) {
|
|
2014
|
+
const status = "api_error_status" in message ? message.api_error_status : void 0;
|
|
2015
|
+
if (this.#retryable(status ?? void 0)) {
|
|
2016
|
+
if (!this.#turnDidWork && this.#lastPrompt !== null) {
|
|
2017
|
+
this.#pendingRetry.push(this.#lastPrompt);
|
|
2018
|
+
}
|
|
2019
|
+
this.#scheduleReconnect(apiErrorReason(status ?? void 0));
|
|
2020
|
+
break;
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
this.#reconnectAttempt = 0;
|
|
1789
2024
|
const alreadyIdle = this.#state === "idle";
|
|
1790
2025
|
this.#setState("idle");
|
|
1791
2026
|
if (alreadyIdle)
|
|
@@ -1956,6 +2191,12 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
1956
2191
|
function describe(error) {
|
|
1957
2192
|
return error instanceof Error ? error.message : String(error);
|
|
1958
2193
|
}
|
|
2194
|
+
function isApiErrorMessage(message) {
|
|
2195
|
+
return message.is_api_error_message === true;
|
|
2196
|
+
}
|
|
2197
|
+
function apiErrorReason(status) {
|
|
2198
|
+
return status === void 0 ? "\u0437\u0430\u043F\u0440\u043E\u0441 \u043A API \u043D\u0435 \u0434\u043E\u0448\u0451\u043B" : `API \u043E\u0442\u0432\u0435\u0442\u0438\u043B ${status}`;
|
|
2199
|
+
}
|
|
1959
2200
|
function toSlashCommand(command) {
|
|
1960
2201
|
return {
|
|
1961
2202
|
name: command.name,
|
|
@@ -4442,6 +4683,9 @@ var HostServer = class {
|
|
|
4442
4683
|
case "session.close":
|
|
4443
4684
|
await this.#dropSession(frame.sessionId, "closed by client");
|
|
4444
4685
|
return;
|
|
4686
|
+
case "session.reconnect":
|
|
4687
|
+
this.#requireSession(frame.sessionId).reconnect("\u043F\u043E \u043F\u0440\u043E\u0441\u044C\u0431\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F");
|
|
4688
|
+
return;
|
|
4445
4689
|
case "session.archive": {
|
|
4446
4690
|
const ids = new Set(frame.sessionIds);
|
|
4447
4691
|
for (const session of [...this.#sessions.values()]) {
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{s as r,H as a}from"./index-
|
|
1
|
+
import{s as r,H as a}from"./index-YTb4ol7S.js";import{E as e}from"./index-891wE-Rj.js";import{t as o}from"./index-05EaTJ2I.js";import"./index-BHot-lnj.js";const m=e.theme({"&":{height:"100%",color:"var(--text)",backgroundColor:"var(--bg)",fontSize:"13px"},".cm-scroller":{fontFamily:"var(--mono)",lineHeight:"1.6"},".cm-content":{caretColor:"var(--accent)"},".cm-cursor, .cm-dropCursor":{borderLeftColor:"var(--accent)"},"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":{backgroundColor:"var(--cm-selection)"},".cm-gutters":{backgroundColor:"var(--bg)",color:"var(--muted)",borderRight:"1px solid var(--border-soft)"},".cm-activeLineGutter":{backgroundColor:"var(--surface-2)",color:"var(--text-2)"},".cm-activeLine":{backgroundColor:"var(--cm-active)"},".cm-selectionMatch":{backgroundColor:"var(--cm-match)"},"&.cm-focused .cm-matchingBracket":{backgroundColor:"var(--cm-match)",outline:"1px solid var(--accent-line)"},".cm-foldPlaceholder":{backgroundColor:"var(--surface-2)",border:"1px solid var(--border)",color:"var(--muted)"},".cm-panels":{backgroundColor:"var(--surface)",color:"var(--text)",border:"1px solid var(--border)"},".cm-panels input, .cm-panels button, .cm-panels select":{fontFamily:"var(--sans)",fontSize:"12px",background:"var(--surface-2)",color:"var(--text)",border:"1px solid var(--border)",borderRadius:"6px",padding:"3px 6px"},".cm-searchMatch":{backgroundColor:"var(--cm-match)"},".cm-searchMatch.cm-searchMatch-selected":{backgroundColor:"var(--accent-soft)"},".cm-tooltip":{background:"var(--surface)",border:"1px solid var(--border)",borderRadius:"8px",boxShadow:"var(--shadow-md)",color:"var(--text)"},".cm-tooltip-autocomplete ul li[aria-selected]":{background:"var(--surface-2)",color:"var(--text)"}}),i=r(a.define([{tag:[o.keyword,o.modifier,o.controlKeyword,o.moduleKeyword],color:"var(--cm-key)"},{tag:[o.name,o.deleted,o.character,o.macroName],color:"var(--text)"},{tag:[o.propertyName],color:"var(--cm-prop)"},{tag:[o.function(o.variableName),o.labelName],color:"var(--cm-fn)"},{tag:[o.color,o.constant(o.name),o.standard(o.name),o.bool],color:"var(--cm-const)"},{tag:[o.definition(o.name),o.separator],color:"var(--text)"},{tag:[o.typeName,o.className,o.changed,o.annotation,o.self,o.namespace],color:"var(--cm-type)"},{tag:[o.number,o.integer,o.float],color:"var(--cm-num)"},{tag:[o.string,o.special(o.string),o.regexp],color:"var(--cm-str)"},{tag:[o.operator,o.operatorKeyword,o.escape],color:"var(--cm-op)"},{tag:[o.meta,o.comment],color:"var(--cm-comment)",fontStyle:"italic"},{tag:o.strong,fontWeight:"650"},{tag:o.emphasis,fontStyle:"italic"},{tag:o.strikethrough,textDecoration:"line-through"},{tag:o.link,color:"var(--cm-str)",textDecoration:"underline"},{tag:o.heading,fontWeight:"650",color:"var(--cm-key)"},{tag:o.invalid,color:"var(--danger)"}]));export{i as editorHighlight,m as editorTheme};
|