@i14ks/ccv 0.20.0 → 0.21.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/bundle/ccv.mjs +102 -20
- package/package.json +1 -1
- package/web/assets/{codemirror-DOvy1hit.js → codemirror-B6QVSqxx.js} +1 -1
- package/web/assets/{index-0mOmG1OB.js → index-0l0v5ZjK.js} +1 -1
- package/web/assets/{index-ChjkRA1v.js → index-1VuadINO.js} +1 -1
- package/web/assets/index-3BSpF-Pn.js +134 -0
- package/web/assets/{index-zjOh8oJE.js → index-B8GLobgh.js} +1 -1
- package/web/assets/{index-BMWh-izi.js → index-B9qDsSjC.js} +1 -1
- package/web/assets/{index-C4uljLPL.js → index-BYERpa5Q.js} +1 -1
- package/web/assets/{index-C4IoqL6u.js → index-Bf6VCb0a.js} +1 -1
- package/web/assets/{index-x3nfLW_t.js → index-BlPPlrvg.js} +1 -1
- package/web/assets/{index-BhAQ1Td-.js → index-BqED4I4f.js} +1 -1
- package/web/assets/{index-Dkiy3bA7.js → index-BwuRWAfa.js} +1 -1
- package/web/assets/{index-CB5sx8v_.js → index-C5hGXrTC.js} +1 -1
- package/web/assets/{index-C37WshJ1.js → index-CCRhGn6u.js} +1 -1
- package/web/assets/{index-BUfDT07G.js → index-CTe-duo7.js} +1 -1
- package/web/assets/{index-DuclA_NV.js → index-Cgi4XmXk.js} +1 -1
- package/web/assets/{index-CIbEaXb2.js → index-CqM8bKzd.js} +1 -1
- package/web/assets/{index-DTjRu-SE.js → index-DdFPrUTi.js} +1 -1
- package/web/assets/{index-C_10amyS.js → index-DmwQZoSG.js} +1 -1
- package/web/assets/{index-Bf9yoJhl.js → index-Zo5hIeFa.js} +1 -1
- package/web/assets/{index-R1dnxvPf.js → index-oOWAdIqY.js} +1 -1
- package/web/assets/{index-CsP-muVq.js → index-rsg6TYOk.js} +1 -1
- package/web/index.html +1 -1
- package/web/assets/index-Bt29MBdY.js +0 -134
package/bundle/ccv.mjs
CHANGED
|
@@ -2376,6 +2376,7 @@ var ClaudeSession = class extends EventEmitter {
|
|
|
2376
2376
|
#handleMessage(message) {
|
|
2377
2377
|
if (this.#state === "idle" && (message.type === "assistant" || message.type === "stream_event") && message.parent_tool_use_id === null) {
|
|
2378
2378
|
this.#setState("running");
|
|
2379
|
+
this.emit("unpromptedTurn");
|
|
2379
2380
|
}
|
|
2380
2381
|
switch (message.type) {
|
|
2381
2382
|
case "system":
|
|
@@ -6334,6 +6335,10 @@ var LimitStopState = class {
|
|
|
6334
6335
|
return false;
|
|
6335
6336
|
return resets === null || Math.abs(resets - until) <= SAME_WINDOW_MS;
|
|
6336
6337
|
}
|
|
6338
|
+
/** До какого момента окно `limit` отмечено сработавшим; 0 — не срабатывало. */
|
|
6339
|
+
until(limit) {
|
|
6340
|
+
return this.#fired[limit] ?? 0;
|
|
6341
|
+
}
|
|
6337
6342
|
/** Запомнить срабатывание до `until` (момент сброса окна). */
|
|
6338
6343
|
async mark(limit, until) {
|
|
6339
6344
|
this.#fired[limit] = until;
|
|
@@ -7551,6 +7556,17 @@ var HostServer = class {
|
|
|
7551
7556
|
#limitTimer = null;
|
|
7552
7557
|
/** Идёт остановка у лимита — второй снимок лимитов её не запускает. */
|
|
7553
7558
|
#limitStopping = false;
|
|
7559
|
+
/** Последний снимок лимитов Claude — им сверяется ход без промпта. */
|
|
7560
|
+
#lastUsage = null;
|
|
7561
|
+
/**
|
|
7562
|
+
* Сессии, которым человек сам написал после срабатывания порога: id
|
|
7563
|
+
* сессии → окно → срок, до которого оно было отмечено сработавшим. Это его
|
|
7564
|
+
* решение продолжать, и ход без промпта такую сессию до сброса того же окна
|
|
7565
|
+
* не останавливает. В памяти: живые сессии рестарт хоста не переживают.
|
|
7566
|
+
*/
|
|
7567
|
+
#limitApproved = /* @__PURE__ */ new Map();
|
|
7568
|
+
/** Живые id сессий, у которых сейчас пишется хэндофф у лимита. */
|
|
7569
|
+
#limitHandoffBusy = /* @__PURE__ */ new Set();
|
|
7554
7570
|
/** Живые id сессий, у которых сейчас пишется ручной хэндофф. */
|
|
7555
7571
|
#handoffBusy = /* @__PURE__ */ new Set();
|
|
7556
7572
|
#hostId = randomUUID5();
|
|
@@ -8074,9 +8090,11 @@ var HostServer = class {
|
|
|
8074
8090
|
return;
|
|
8075
8091
|
}
|
|
8076
8092
|
case "prompt.send":
|
|
8093
|
+
this.#approveAfterLimit(frame.sessionId);
|
|
8077
8094
|
this.#requireSession(frame.sessionId).sendPrompt(frame.text, frame.images);
|
|
8078
8095
|
return;
|
|
8079
8096
|
case "prompt.edit":
|
|
8097
|
+
this.#approveAfterLimit(frame.sessionId);
|
|
8080
8098
|
await this.#requireSession(frame.sessionId).editPrompt(frame.userIndex, frame.text, frame.images);
|
|
8081
8099
|
return;
|
|
8082
8100
|
case "permission.respond":
|
|
@@ -8637,6 +8655,7 @@ var HostServer = class {
|
|
|
8637
8655
|
await this.#closeSide(sessionId);
|
|
8638
8656
|
await session.close(reason);
|
|
8639
8657
|
this.#sessions.delete(sessionId);
|
|
8658
|
+
this.#limitApproved.delete(sessionId);
|
|
8640
8659
|
this.#broadcast({ type: "session.closed", sessionId, reason });
|
|
8641
8660
|
this.#sendLive();
|
|
8642
8661
|
return true;
|
|
@@ -8722,6 +8741,7 @@ var HostServer = class {
|
|
|
8722
8741
|
void busy.refreshUsage();
|
|
8723
8742
|
}
|
|
8724
8743
|
async #checkLimit(usage) {
|
|
8744
|
+
this.#lastUsage = usage;
|
|
8725
8745
|
if (this.#limitStopping)
|
|
8726
8746
|
return;
|
|
8727
8747
|
const hit = crossedLimit(usage, this.#settings.current, (limit, resets) => this.#limitStop.fired(limit, resets));
|
|
@@ -8737,6 +8757,57 @@ var HostServer = class {
|
|
|
8737
8757
|
this.#limitStopping = false;
|
|
8738
8758
|
}
|
|
8739
8759
|
}
|
|
8760
|
+
/**
|
|
8761
|
+
* Человек сам пишет в сессию — после сработавшего порога это его решение
|
|
8762
|
+
* продолжать (окно перед отправкой его спросило, см. `limitWarning` в
|
|
8763
|
+
* вебе). Запоминаем, до какого срока сработало каждое окно: подтверждение
|
|
8764
|
+
* действует только до его сброса, новое окно спросит заново.
|
|
8765
|
+
*/
|
|
8766
|
+
#approveAfterLimit(sessionId) {
|
|
8767
|
+
const now = Date.now();
|
|
8768
|
+
const approved = {};
|
|
8769
|
+
for (const limit of ["fiveHour", "sevenDay"]) {
|
|
8770
|
+
const until = this.#limitStop.until(limit);
|
|
8771
|
+
if (until > now)
|
|
8772
|
+
approved[limit] = until;
|
|
8773
|
+
}
|
|
8774
|
+
if (Object.keys(approved).length > 0) {
|
|
8775
|
+
this.#limitApproved.set(sessionId, { ...this.#limitApproved.get(sessionId), ...approved });
|
|
8776
|
+
}
|
|
8777
|
+
}
|
|
8778
|
+
/**
|
|
8779
|
+
* Сессия начала ход сама, без промпта: её разбудило уведомление фоновой
|
|
8780
|
+
* задачи, таймер или ответ фонового субагента. Порог в окне уже сработал —
|
|
8781
|
+
* сам он до сброса больше не проверяется, и такой ход дожёг бы окно до
|
|
8782
|
+
* 100 % и оборвался без хэндоффа (15.10 в 19:32 так разбудило MIREA).
|
|
8783
|
+
* Останавливаем его, как остановили бы на пороге, — если только человек
|
|
8784
|
+
* сам не писал в эту сессию после срабатывания (`#approveAfterLimit`).
|
|
8785
|
+
*
|
|
8786
|
+
* Снимок — последний пришедший: в пределах окна процент только растёт, а
|
|
8787
|
+
* сброшенное окно `crossedLimit` отбросит по сроку. Окно, которое ещё не
|
|
8788
|
+
* срабатывало, здесь не трогаем: его поймает обычный `#checkLimit`.
|
|
8789
|
+
*/
|
|
8790
|
+
async #checkUnpromptedTurn(session) {
|
|
8791
|
+
if (!this.#lastUsage || this.#limitStopping)
|
|
8792
|
+
return;
|
|
8793
|
+
if (this.#limitHandoffBusy.has(session.id) || this.#handoffBusy.has(session.id))
|
|
8794
|
+
return;
|
|
8795
|
+
if (!this.#claudeUserSessions().includes(session))
|
|
8796
|
+
return;
|
|
8797
|
+
const approved = this.#limitApproved.get(session.id) ?? {};
|
|
8798
|
+
const hit = crossedLimit(this.#lastUsage, this.#settings.current, (limit, resets) => !this.#limitStop.fired(limit, resets) || approved[limit] === this.#limitStop.until(limit));
|
|
8799
|
+
if (!hit)
|
|
8800
|
+
return;
|
|
8801
|
+
const saved = await this.#limitHandoff(session, hit);
|
|
8802
|
+
this.#broadcast({
|
|
8803
|
+
type: "limit.stopped",
|
|
8804
|
+
limit: hit.limit,
|
|
8805
|
+
utilization: hit.window.utilization,
|
|
8806
|
+
resetsAt: hit.window.resetsAt,
|
|
8807
|
+
stopped: 1,
|
|
8808
|
+
handoffs: saved ? 1 : 0
|
|
8809
|
+
});
|
|
8810
|
+
}
|
|
8740
8811
|
/**
|
|
8741
8812
|
* Прервать работу у лимита: занятые сессии пишут хэндофф (или он
|
|
8742
8813
|
* собирается из журнала, если агент не ответил), простаивающие не
|
|
@@ -8749,9 +8820,25 @@ var HostServer = class {
|
|
|
8749
8820
|
if (sessions.length === 0)
|
|
8750
8821
|
return;
|
|
8751
8822
|
const now = Date.now();
|
|
8752
|
-
const busy = sessions.filter((s) => this.#isBusy(s) || now - s.lastActiveAt < LIMIT_RECENT_MS);
|
|
8753
|
-
const
|
|
8754
|
-
|
|
8823
|
+
const busy = sessions.filter((s) => !this.#limitHandoffBusy.has(s.id) && (this.#isBusy(s) || now - s.lastActiveAt < LIMIT_RECENT_MS));
|
|
8824
|
+
const results = await Promise.all(busy.map((session) => this.#limitHandoff(session, hit)));
|
|
8825
|
+
this.#broadcast({
|
|
8826
|
+
type: "limit.stopped",
|
|
8827
|
+
limit: hit.limit,
|
|
8828
|
+
utilization: hit.window.utilization,
|
|
8829
|
+
resetsAt: hit.window.resetsAt,
|
|
8830
|
+
stopped: results.length,
|
|
8831
|
+
handoffs: results.filter(Boolean).length
|
|
8832
|
+
});
|
|
8833
|
+
}
|
|
8834
|
+
/**
|
|
8835
|
+
* Хэндофф одной сессии у лимита: ход хэндоффа (или журнал, если агент не
|
|
8836
|
+
* ответил), сохранение и рассылка. `true` — хэндофф сохранён.
|
|
8837
|
+
*/
|
|
8838
|
+
async #limitHandoff(session, hit) {
|
|
8839
|
+
this.#limitHandoffBusy.add(session.id);
|
|
8840
|
+
try {
|
|
8841
|
+
const prompt = hit.window.utilization >= 100 ? null : handoffPrompt(hit);
|
|
8755
8842
|
const text = await session.runHandoff(prompt, HANDOFF_TIMEOUT_MS);
|
|
8756
8843
|
const summary = session.summary();
|
|
8757
8844
|
const handoff = {
|
|
@@ -8765,23 +8852,15 @@ var HostServer = class {
|
|
|
8765
8852
|
source: text ? "agent" : "journal",
|
|
8766
8853
|
text: text ?? journalHandoff(session.journal.since(0))
|
|
8767
8854
|
};
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
8775
|
-
|
|
8776
|
-
}
|
|
8777
|
-
this.#broadcast({
|
|
8778
|
-
type: "limit.stopped",
|
|
8779
|
-
limit: hit.limit,
|
|
8780
|
-
utilization: hit.window.utilization,
|
|
8781
|
-
resetsAt: hit.window.resetsAt,
|
|
8782
|
-
stopped: results.length,
|
|
8783
|
-
handoffs: results.filter(Boolean).length
|
|
8784
|
-
});
|
|
8855
|
+
await this.#handoffs.save(handoff);
|
|
8856
|
+
this.#broadcast({ type: "handoff.state", sessionId: handoff.sessionId, handoff });
|
|
8857
|
+
return true;
|
|
8858
|
+
} catch (error) {
|
|
8859
|
+
console.error("[limit-stop]", error instanceof Error ? error.message : String(error));
|
|
8860
|
+
return false;
|
|
8861
|
+
} finally {
|
|
8862
|
+
this.#limitHandoffBusy.delete(session.id);
|
|
8863
|
+
}
|
|
8785
8864
|
}
|
|
8786
8865
|
/**
|
|
8787
8866
|
* «Хэндофф и новая сессия»: тот же ход хэндоффа, что у лимита, только по
|
|
@@ -9032,6 +9111,9 @@ var HostServer = class {
|
|
|
9032
9111
|
if (session instanceof ClaudeSession)
|
|
9033
9112
|
void this.#checkLimit(usage);
|
|
9034
9113
|
});
|
|
9114
|
+
if (session instanceof ClaudeSession) {
|
|
9115
|
+
session.on("unpromptedTurn", () => void this.#checkUnpromptedTurn(session));
|
|
9116
|
+
}
|
|
9035
9117
|
session.on("context", (context) => {
|
|
9036
9118
|
this.#broadcast({ type: "context.state", sessionId: session.id, context });
|
|
9037
9119
|
});
|
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-Zo5hIeFa.js";import{E as e}from"./index-891wE-Rj.js";import{t as o}from"./index-3BSpF-Pn.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};
|