@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.
Files changed (27) hide show
  1. package/bundle/ccv.mjs +263 -19
  2. package/package.json +1 -1
  3. package/web/assets/{codemirror-BWZxIjMU.js → codemirror-YAsAN8OM.js} +1 -1
  4. package/web/assets/index-05EaTJ2I.js +129 -0
  5. package/web/assets/{index-9jmr6xz8.js → index-BANX3gfG.js} +1 -1
  6. package/web/assets/{index-DkpH15La.js → index-Bj-McB2J.js} +1 -1
  7. package/web/assets/{index-BQi6QI7Z.js → index-BqaOf3lL.js} +1 -1
  8. package/web/assets/{index-CjAk3Yb0.js → index-C6bp7gLM.js} +1 -1
  9. package/web/assets/index-CJKd_3HV.css +1 -0
  10. package/web/assets/{index-AGqthGr9.js → index-CStpUyOa.js} +1 -1
  11. package/web/assets/{index-Y1vKLu9k.js → index-CaI4eesN.js} +1 -1
  12. package/web/assets/{index-qPJ6SIgP.js → index-CbXyt5BH.js} +1 -1
  13. package/web/assets/{index-DEFc0MJB.js → index-CbkTwf-a.js} +1 -1
  14. package/web/assets/{index-DGCeaXoo.js → index-Cw0TSvLQ.js} +1 -1
  15. package/web/assets/{index-BP5TdYoo.js → index-DQD2YqR9.js} +1 -1
  16. package/web/assets/{index-Dbr7EU2n.js → index-DSWJFoDR.js} +1 -1
  17. package/web/assets/{index-CqqEs8q5.js → index-DWdTnlsP.js} +1 -1
  18. package/web/assets/{index-DdAPIomJ.js → index-Dr7O0ILC.js} +1 -1
  19. package/web/assets/{index-C9yuVPI-.js → index-DwCBmVAn.js} +1 -1
  20. package/web/assets/{index-Dr8ovngt.js → index-JfSeUxQA.js} +1 -1
  21. package/web/assets/{index-hQhZ9dRw.js → index-YTb4ol7S.js} +1 -1
  22. package/web/assets/{index-C_UZRXvz.js → index-bVu7H3ZX.js} +1 -1
  23. package/web/assets/{index-By-XqFTs.js → index-khA8Gu_K.js} +1 -1
  24. package/web/assets/{index-DYzVllNN.js → index-po851PkX.js} +1 -1
  25. package/web/index.html +2 -2
  26. package/web/assets/index-DnC6QasI.js +0 -128
  27. 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
- if (this.#options.resume)
1260
- options2.resume = this.#options.resume;
1261
- if (this.#options.fork)
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
- this.#query = query({ prompt: this.#inbox, options: options2 });
1271
- void this.#consume(this.#query);
1272
- void this.#loadCatalog();
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.#setState("closed");
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.#inbox.closed)
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.#setState("closed");
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
- this.#record({ kind: "error", message: message + this.#executableHint() });
1703
- this.#setState("error");
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
- this.#lastTurn = turnOutcome("terminal_reason" in message ? message.terminal_reason : void 0, message.subtype, this.#interruptRequested);
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@i14ks/ccv",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "license": "UNLICENSED",
5
5
  "description": "GUI для Claude Code: сессии агента в каталогах проектов, дерево файлов и редактор рядом с разговором.",
6
6
  "homepage": "https://claudevisual.com",
@@ -1 +1 @@
1
- import{s as r,H as a}from"./index-hQhZ9dRw.js";import{E as e}from"./index-891wE-Rj.js";import{t as o}from"./index-DnC6QasI.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};
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};