@songsid/agend 2.1.12-beta.1 → 2.1.12-beta.2
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/agent-cli.js +36 -4
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.d.ts +3 -1
- package/dist/agent-endpoint.js +7 -1
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/backend/claude-code.d.ts +2 -0
- package/dist/backend/claude-code.js +2 -2
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/session-signals.d.ts +154 -0
- package/dist/backend/session-signals.js +430 -0
- package/dist/backend/session-signals.js.map +1 -0
- package/dist/channel/adapters/discord.d.ts +38 -1
- package/dist/channel/adapters/discord.js +131 -8
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +19 -1
- package/dist/channel/adapters/telegram.js +55 -0
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-tools.js +5 -1
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/message-queue.d.ts +6 -0
- package/dist/channel/message-queue.js +21 -0
- package/dist/channel/message-queue.js.map +1 -1
- package/dist/channel/sticker-download.d.ts +14 -0
- package/dist/channel/sticker-download.js +42 -0
- package/dist/channel/sticker-download.js.map +1 -0
- package/dist/channel/tool-router.js +12 -5
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/channel/types.d.ts +52 -0
- package/dist/cli-flags.d.ts +8 -0
- package/dist/cli-flags.js +22 -0
- package/dist/cli-flags.js.map +1 -0
- package/dist/config-validator.js +25 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.d.ts +20 -0
- package/dist/daemon.js +155 -3
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +8 -0
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/event-loop-watch.d.ts +25 -0
- package/dist/event-loop-watch.js +40 -0
- package/dist/event-loop-watch.js.map +1 -0
- package/dist/fleet-manager.d.ts +38 -3
- package/dist/fleet-manager.js +248 -12
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/persona-emoji/SKILL.md +4 -2
- package/dist/locale.js +4 -0
- package/dist/locale.js.map +1 -1
- package/dist/outbound-schemas.d.ts +15 -1
- package/dist/outbound-schemas.js +19 -1
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/tool-permissions.js +8 -1
- package/dist/tool-permissions.js.map +1 -1
- package/dist/turn-reply-guard.d.ts +7 -0
- package/dist/turn-reply-guard.js +11 -0
- package/dist/turn-reply-guard.js.map +1 -1
- package/dist/turn-resume.d.ts +80 -0
- package/dist/turn-resume.js +217 -0
- package/dist/turn-resume.js.map +1 -0
- package/dist/ui/view.html +8 -2
- package/dist/usage/format-rich.js +6 -1
- package/dist/usage/format-rich.js.map +1 -1
- package/dist/usage/i18n.d.ts +2 -0
- package/dist/usage/i18n.js +14 -1
- package/dist/usage/i18n.js.map +1 -1
- package/dist/usage/providers.d.ts +10 -0
- package/dist/usage/providers.js +2 -0
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage/usage-api.d.ts +9 -0
- package/dist/usage/usage-api.js +31 -2
- package/dist/usage/usage-api.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1209 ACP-resume-like cross-restart turn resume, stacked on the
|
|
3
|
+
* TurnFingerprint seam (`session-signals.ts`, #1227).
|
|
4
|
+
*
|
|
5
|
+
* This is tmux-outer resume-*like* state machinery, not the ACP protocol:
|
|
6
|
+
* when a channel turn is armed, a one-shot `in-flight-turn.json` marker is
|
|
7
|
+
* written (with a seam fingerprint checkpoint); whatever survives a restart
|
|
8
|
+
* is, by construction, an interrupted turn. After boot + CLI spawn, the gate
|
|
9
|
+
* below decides exactly once whether to re-inject one bounded continuation
|
|
10
|
+
* tied to the original correlation.
|
|
11
|
+
*
|
|
12
|
+
* Decision order (all in `decideTurnResume`, pure and matrix-tested):
|
|
13
|
+
* marker absent → none (covers cancel: cancel deletes the marker, #1199);
|
|
14
|
+
* crash-loop boot → skip (clean start, #835);
|
|
15
|
+
* marker delivery still pending in the outbox → skip (durable path owns it);
|
|
16
|
+
* store session id changed → skip (conversation is gone);
|
|
17
|
+
* seam reengaged → skip (CLI-native resume already drives; never double-drive);
|
|
18
|
+
* seam unknown → wait out the flush grace, then skip (default-deny);
|
|
19
|
+
* seam quiet → inject (compare only reports quiet outside the grace).
|
|
20
|
+
*
|
|
21
|
+
* The marker is consumed BEFORE gating (episode-once per interrupted turn —
|
|
22
|
+
* a second boot never repeats), and completion/cancel paths delete it so a
|
|
23
|
+
* finished turn never resumes. Backends without a seam reader resolve to
|
|
24
|
+
* unknown and therefore never continue.
|
|
25
|
+
*
|
|
26
|
+
* Hit-rate note (P3c): post-boot writes that look like turns — the warmup
|
|
27
|
+
* steering reload, durable outbox redelivery — advance the turn tail and
|
|
28
|
+
* read as reengaged, so this gate skips. Safe (never double-drives) at the
|
|
29
|
+
* cost of a missed resume; the seam cannot tell "the CLI resumed the work"
|
|
30
|
+
* from "the daemon just drove the CLI".
|
|
31
|
+
*/
|
|
32
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
33
|
+
import { homedir } from "node:os";
|
|
34
|
+
import { join } from "node:path";
|
|
35
|
+
import { CodexBackend } from "./backend/codex.js";
|
|
36
|
+
import { compareFingerprints, claudeFingerprint, codexFingerprint, DEFAULT_FLUSH_GRACE_MS, museFingerprint, } from "./backend/session-signals.js";
|
|
37
|
+
export const IN_FLIGHT_TURN_FILE = "in-flight-turn.json";
|
|
38
|
+
/** Extra settle past the flush grace before a held decision is retaken. */
|
|
39
|
+
export const RESUME_GRACE_SETTLE_MS = 1_000;
|
|
40
|
+
/** Outbox states in which the durable path may still drive the delivery. */
|
|
41
|
+
export const RESUME_BLOCKING_DELIVERY_STATES = [
|
|
42
|
+
"queued",
|
|
43
|
+
"delivering",
|
|
44
|
+
"submission_started",
|
|
45
|
+
"reconciliation_pending",
|
|
46
|
+
"retry_wait",
|
|
47
|
+
];
|
|
48
|
+
/** Accept both factory ids and binary names; anything else has no seam reader. */
|
|
49
|
+
export function normalizeResumeBackend(backend) {
|
|
50
|
+
const key = (backend ?? "").trim().toLowerCase();
|
|
51
|
+
if (key === "claude" || key === "claude-code")
|
|
52
|
+
return "claude";
|
|
53
|
+
if (key === "codex")
|
|
54
|
+
return "codex";
|
|
55
|
+
if (key === "muse")
|
|
56
|
+
return "muse";
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Best-effort current fingerprint for a backend; null when unsupported or
|
|
61
|
+
* unreadable (which the gate treats as unknown → default-deny). Synchronous
|
|
62
|
+
* blocking I/O — arm-time only, never on a hot loop.
|
|
63
|
+
*/
|
|
64
|
+
export function readResumeFingerprint(backend, roots) {
|
|
65
|
+
if (!backend || !roots.workingDirectory)
|
|
66
|
+
return null;
|
|
67
|
+
try {
|
|
68
|
+
if (backend === "claude") {
|
|
69
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR?.trim() || join(homedir(), ".claude");
|
|
70
|
+
return claudeFingerprint({ configDir, cwd: roots.workingDirectory });
|
|
71
|
+
}
|
|
72
|
+
if (backend === "codex") {
|
|
73
|
+
return codexFingerprint({
|
|
74
|
+
stateDbPath: join(CodexBackend.shortHomeFor(roots.instanceDir), "state_5.sqlite"),
|
|
75
|
+
cwd: roots.workingDirectory,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return museFingerprint({
|
|
79
|
+
sessionsRoot: join(homedir(), ".local", "share", "muse", "sessions"),
|
|
80
|
+
cwd: roots.workingDirectory,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Persist the in-flight marker (best effort; sync so a crash cannot interleave). */
|
|
88
|
+
export function writeInFlightTurnMarker(instanceDir, marker) {
|
|
89
|
+
try {
|
|
90
|
+
writeFileSync(join(instanceDir, IN_FLIGHT_TURN_FILE), JSON.stringify(marker));
|
|
91
|
+
}
|
|
92
|
+
catch { /* best effort — no marker just means no resume candidate */ }
|
|
93
|
+
}
|
|
94
|
+
/** A finished or cancelled turn must never resume after a later restart. */
|
|
95
|
+
export function clearInFlightTurnMarker(instanceDir) {
|
|
96
|
+
try {
|
|
97
|
+
unlinkSync(join(instanceDir, IN_FLIGHT_TURN_FILE));
|
|
98
|
+
}
|
|
99
|
+
catch { /* already gone */ }
|
|
100
|
+
}
|
|
101
|
+
function parseInFlightTurnMarker(raw) {
|
|
102
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
103
|
+
return null;
|
|
104
|
+
const m = raw;
|
|
105
|
+
if (m.version !== 1 || typeof m.chatId !== "string" || m.chatId === "")
|
|
106
|
+
return null;
|
|
107
|
+
if (typeof m.cwd !== "string" || typeof m.backend !== "string")
|
|
108
|
+
return null;
|
|
109
|
+
const str = (v) => (typeof v === "string" && v !== "" ? v : undefined);
|
|
110
|
+
const before = m.before;
|
|
111
|
+
return {
|
|
112
|
+
version: 1,
|
|
113
|
+
deliveryId: str(m.deliveryId),
|
|
114
|
+
correlationId: str(m.correlationId),
|
|
115
|
+
messageId: str(m.messageId),
|
|
116
|
+
chatId: m.chatId,
|
|
117
|
+
threadId: str(m.threadId),
|
|
118
|
+
adapterId: str(m.adapterId),
|
|
119
|
+
backend: m.backend,
|
|
120
|
+
cwd: m.cwd,
|
|
121
|
+
armedAt: typeof m.armedAt === "number" ? m.armedAt : 0,
|
|
122
|
+
before: before && typeof before === "object" ? before : null,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Read and consume the marker in one step: episode-once whatever happens
|
|
127
|
+
* after. A corrupt file is still unlinked (crash-state.json semantics) so it
|
|
128
|
+
* cannot suppress or repeat forever.
|
|
129
|
+
*/
|
|
130
|
+
export function consumeInFlightTurnMarker(instanceDir) {
|
|
131
|
+
const path = join(instanceDir, IN_FLIGHT_TURN_FILE);
|
|
132
|
+
try {
|
|
133
|
+
if (!existsSync(path))
|
|
134
|
+
return null;
|
|
135
|
+
let marker = null;
|
|
136
|
+
try {
|
|
137
|
+
marker = parseInFlightTurnMarker(JSON.parse(readFileSync(path, "utf-8")));
|
|
138
|
+
}
|
|
139
|
+
catch { /* corrupt — still consumed below */ }
|
|
140
|
+
try {
|
|
141
|
+
unlinkSync(path);
|
|
142
|
+
}
|
|
143
|
+
catch { /* best effort */ }
|
|
144
|
+
return marker;
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Pure #1209 gate. Only a quiet seam OUTSIDE the flush grace injects; a
|
|
152
|
+
* changed session id and a reengaged CLI both skip (never double-drive);
|
|
153
|
+
* unknown holds through the grace window (wait) and denies after it.
|
|
154
|
+
*/
|
|
155
|
+
export function decideTurnResume(input) {
|
|
156
|
+
const flushGraceMs = input.flushGraceMs ?? DEFAULT_FLUSH_GRACE_MS;
|
|
157
|
+
if (!input.marker)
|
|
158
|
+
return { action: "skip", reason: "none", detail: "no interrupted turn" };
|
|
159
|
+
if (input.crashLoopBoot) {
|
|
160
|
+
return { action: "skip", reason: "crash-loop", detail: "crash-loop boot stays a clean start" };
|
|
161
|
+
}
|
|
162
|
+
if (input.deliveryPending) {
|
|
163
|
+
return { action: "skip", reason: "delivery-pending", detail: "durable delivery path still owns the turn" };
|
|
164
|
+
}
|
|
165
|
+
const beforeId = input.before?.sessionId ?? null;
|
|
166
|
+
const afterId = input.after?.sessionId ?? null;
|
|
167
|
+
if (beforeId && afterId && beforeId !== afterId) {
|
|
168
|
+
return { action: "skip", reason: "session-changed", detail: "store now belongs to a different conversation" };
|
|
169
|
+
}
|
|
170
|
+
const verdict = compareFingerprints(input.before, input.after, {
|
|
171
|
+
daemonCausedMtimeMs: input.daemonCausedMtimeMs,
|
|
172
|
+
nowMs: input.nowMs,
|
|
173
|
+
flushGraceMs,
|
|
174
|
+
});
|
|
175
|
+
if (verdict === "reengaged") {
|
|
176
|
+
return { action: "skip", reason: "cli-reengaged", detail: "CLI-native resume already drives the turn" };
|
|
177
|
+
}
|
|
178
|
+
const inGrace = input.daemonCausedMtimeMs >= 0 && input.nowMs - input.daemonCausedMtimeMs <= flushGraceMs;
|
|
179
|
+
if (verdict === "unknown") {
|
|
180
|
+
if (inGrace) {
|
|
181
|
+
return {
|
|
182
|
+
action: "wait",
|
|
183
|
+
waitMs: flushGraceMs - (input.nowMs - input.daemonCausedMtimeMs) + RESUME_GRACE_SETTLE_MS,
|
|
184
|
+
detail: "holding the flush-grace window before deciding",
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return { action: "skip", reason: "unobservable", detail: "store unreadable or unsupported; default-deny" };
|
|
188
|
+
}
|
|
189
|
+
// A quiet verdict is only reachable outside the grace (compare holds
|
|
190
|
+
// inside-grace reads as unknown), so it is safe to inject here.
|
|
191
|
+
return { action: "inject", detail: "interrupted turn, CLI idle, nothing else drives it" };
|
|
192
|
+
}
|
|
193
|
+
/** One bounded continuation tied to the original request — never a re-paste. */
|
|
194
|
+
export function buildResumeContinuation(marker) {
|
|
195
|
+
const ref = marker.correlationId ?? marker.messageId ?? marker.deliveryId ?? "unknown";
|
|
196
|
+
const text = "[RESUME — the previous process died mid-turn. "
|
|
197
|
+
+ "Continue exactly this interrupted work, nothing else. "
|
|
198
|
+
+ `Original request: ${ref}.]`;
|
|
199
|
+
const meta = {
|
|
200
|
+
user: "instance:resume",
|
|
201
|
+
user_id: "instance:resume",
|
|
202
|
+
from_instance: "",
|
|
203
|
+
chat_id: marker.chatId,
|
|
204
|
+
message_id: `resume-${marker.armedAt}`,
|
|
205
|
+
};
|
|
206
|
+
if (marker.threadId)
|
|
207
|
+
meta.thread_id = marker.threadId;
|
|
208
|
+
if (marker.adapterId)
|
|
209
|
+
meta.adapter_id = marker.adapterId;
|
|
210
|
+
if (marker.correlationId)
|
|
211
|
+
meta.correlation_id = marker.correlationId;
|
|
212
|
+
if (marker.deliveryId ?? marker.messageId) {
|
|
213
|
+
meta.resumedContinuationOf = String(marker.deliveryId ?? marker.messageId);
|
|
214
|
+
}
|
|
215
|
+
return { text, meta };
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=turn-resume.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-resume.js","sourceRoot":"","sources":["../src/turn-resume.ts"],"names":[],"mappings":"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"}
|
package/dist/ui/view.html
CHANGED
|
@@ -159,6 +159,7 @@
|
|
|
159
159
|
/* Neutral note (subscription plan, or "sign-in needed") — deliberately not the
|
|
160
160
|
amber error colour, since neither state is a failure. */
|
|
161
161
|
.u-note { color: var(--dim); font-size: 13px; margin: 8px 0; }
|
|
162
|
+
.u-next { font-size: 13px; font-weight: 600; margin: 4px 0 10px; line-height: 1.5; }
|
|
162
163
|
.u-foot { color: var(--dim); font-size: 11px; margin-top: 14px; display: flex; gap: 8px; align-items: baseline; }
|
|
163
164
|
.u-foot button { background: none; border: 1px solid var(--border); border-radius: 5px; color: var(--dim); cursor: pointer; font-size: 11px; padding: 2px 8px; }
|
|
164
165
|
.u-foot button:hover { color: var(--fg); border-color: var(--accent); }
|
|
@@ -251,7 +252,7 @@
|
|
|
251
252
|
usageResets: "resets in {t}", usageSoon: "resets soon", usageFail: "Could not load usage data.",
|
|
252
253
|
"usage.title": "AI Subscription Usage", "usage.title_plain": "AI subscription usage", "usage.empty_active": "No active backends with usage tracking",
|
|
253
254
|
"usage.not_logged_in": "not logged in", "usage.error_fallback": "error", "usage.no_data": "no data",
|
|
254
|
-
"usage.reset.in": "resets in {0}", "usage.reset.soon": "resets soon",
|
|
255
|
+
"usage.reset.in": "resets in {0}", "usage.reset.soon": "resets soon", "usage.next_resets": "⏳ Next reset",
|
|
255
256
|
"usage.duration.days_hours": "{0}d {1}h", "usage.duration.hours_minutes": "{0}h {1}m", "usage.duration.minutes": "{0}m",
|
|
256
257
|
"usage.metric.session": "Session", "usage.metric.weekly": "Weekly", "usage.metric.monthly": "Monthly",
|
|
257
258
|
"usage.metric.days_window": "{0}-day window", "usage.metric.named": "{0}",
|
|
@@ -315,7 +316,7 @@
|
|
|
315
316
|
usageResets: "{t}後重置", usageSoon: "即將重置", usageFail: "無法載入用量資料。",
|
|
316
317
|
"usage.title": "AI 訂閱用量", "usage.title_plain": "AI 訂閱用量", "usage.empty_active": "沒有使用中且支援用量追蹤的 Backend",
|
|
317
318
|
"usage.not_logged_in": "未登入", "usage.error_fallback": "錯誤", "usage.no_data": "無資料",
|
|
318
|
-
"usage.reset.in": "{0} 後重置", "usage.reset.soon": "即將重置",
|
|
319
|
+
"usage.reset.in": "{0} 後重置", "usage.reset.soon": "即將重置", "usage.next_resets": "⏳ 下次重置",
|
|
319
320
|
"usage.duration.days_hours": "{0}d{1}h", "usage.duration.hours_minutes": "{0}h{1}m", "usage.duration.minutes": "{0}m",
|
|
320
321
|
"usage.metric.session": "Session", "usage.metric.weekly": "每週", "usage.metric.monthly": "每月",
|
|
321
322
|
"usage.metric.days_window": "{0} 天視窗", "usage.metric.named": "{0}",
|
|
@@ -1050,6 +1051,11 @@
|
|
|
1050
1051
|
let html = `<h3>${USAGE_ICON}${T("usage.title")}</h3>`;
|
|
1051
1052
|
const providers = orderedUsageProviders(usageData.providers);
|
|
1052
1053
|
if (!providers.length) html += `<div class="u-err">${T("usage.empty_active")}</div>`;
|
|
1054
|
+
// #1232: each backend's soonest reset, in one line at the top — "⏳ Next reset · Claude resets in 5h 12m · Codex …".
|
|
1055
|
+
const nextResets = providers
|
|
1056
|
+
.filter(p => p.status === "ok" && p.nextResetAt && new Date(p.nextResetAt).getTime() > Date.now())
|
|
1057
|
+
.map(p => `${esc(p.name)} ${esc(usageResetText(p.nextResetAt))}`);
|
|
1058
|
+
if (nextResets.length) html += `<div class="u-next">${esc(T("usage.next_resets"))} · ${nextResets.join(" · ")}</div>`;
|
|
1053
1059
|
for (let index = 0; index < providers.length; index++) {
|
|
1054
1060
|
const p = providers[index];
|
|
1055
1061
|
const key = providerKey(p);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isVisibleUsageMetric } from "./usage-api.js";
|
|
2
2
|
import { t } from "../locale.js";
|
|
3
|
-
import { usageResetText, usageText } from "./i18n.js";
|
|
3
|
+
import { usageNextResetText, usageResetText, usageText } from "./i18n.js";
|
|
4
4
|
const BAR_WIDTH = 10;
|
|
5
5
|
/** `████░░░░░░` for 40%. Clamped — a vendor "105% used" must not overflow. */
|
|
6
6
|
export function usageBar(percent) {
|
|
@@ -68,6 +68,7 @@ function toBlocks(payload) {
|
|
|
68
68
|
: p.status === "error" ? `⚠️ ${usageText(p.error ?? t("usage.error_fallback"), p.errorI18n)}`
|
|
69
69
|
: null,
|
|
70
70
|
okHint: p.status === "ok" && p.hint ? usageText(p.hint, p.hintI18n) : null,
|
|
71
|
+
nextReset: p.status === "ok" ? usageNextResetText(p.nextResetAt) || null : null,
|
|
71
72
|
lines: p.status === "ok"
|
|
72
73
|
? p.metrics.filter(isVisibleUsageMetric).map(metricLine).filter((l) => l !== null)
|
|
73
74
|
: [],
|
|
@@ -82,6 +83,8 @@ export function renderUsageMarkdown(payload) {
|
|
|
82
83
|
out.push(`> ${b.note}`);
|
|
83
84
|
continue;
|
|
84
85
|
}
|
|
86
|
+
if (b.nextReset)
|
|
87
|
+
out.push(b.nextReset);
|
|
85
88
|
if (b.lines.length === 0) {
|
|
86
89
|
out.push(b.okHint ? `> ${b.okHint}` : `> ${t("usage.no_data")}`);
|
|
87
90
|
continue;
|
|
@@ -110,6 +113,8 @@ export function renderUsageHtml(payload) {
|
|
|
110
113
|
out.push(escapeHtml(b.note));
|
|
111
114
|
continue;
|
|
112
115
|
}
|
|
116
|
+
if (b.nextReset)
|
|
117
|
+
out.push(escapeHtml(b.nextReset));
|
|
113
118
|
if (b.lines.length === 0) {
|
|
114
119
|
out.push(b.okHint ? escapeHtml(b.okHint) : escapeHtml(t("usage.no_data")));
|
|
115
120
|
continue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-rich.js","sourceRoot":"","sources":["../../src/usage/format-rich.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,oBAAoB,EAAqB,MAAM,gBAAgB,CAAC;AACzE,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"format-rich.js","sourceRoot":"","sources":["../../src/usage/format-rich.ts"],"names":[],"mappings":"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"}
|
package/dist/usage/i18n.d.ts
CHANGED
|
@@ -3,3 +3,5 @@ import type { UsageI18nRef } from "./providers.js";
|
|
|
3
3
|
export declare function usageText(fallback: string, ref?: UsageI18nRef): string;
|
|
4
4
|
/** Minute-precision duration shared by the chat and MCP usage renderers. */
|
|
5
5
|
export declare function usageResetText(resetsAt?: string | null): string;
|
|
6
|
+
/** "⏳ Next reset: 5h 12m" for a provider's nearest reset (#1232); "" when there is none still ahead. */
|
|
7
|
+
export declare function usageNextResetText(nextResetAt?: string | null): string;
|
package/dist/usage/i18n.js
CHANGED
|
@@ -13,6 +13,19 @@ export function usageResetText(resetsAt) {
|
|
|
13
13
|
const remainingMs = at.getTime() - Date.now();
|
|
14
14
|
if (remainingMs <= 0)
|
|
15
15
|
return t("usage.reset.soon");
|
|
16
|
+
return t("usage.reset.in", usageDuration(remainingMs));
|
|
17
|
+
}
|
|
18
|
+
/** "⏳ Next reset: 5h 12m" for a provider's nearest reset (#1232); "" when there is none still ahead. */
|
|
19
|
+
export function usageNextResetText(nextResetAt) {
|
|
20
|
+
if (!nextResetAt)
|
|
21
|
+
return "";
|
|
22
|
+
const remainingMs = new Date(nextResetAt).getTime() - Date.now();
|
|
23
|
+
if (!(remainingMs > 0))
|
|
24
|
+
return "";
|
|
25
|
+
return t("usage.next_reset", usageDuration(remainingMs));
|
|
26
|
+
}
|
|
27
|
+
/** "2d 3h" from two days, "5h 12m" from an hour, "7m" below that — minute precision, rounded up. */
|
|
28
|
+
function usageDuration(remainingMs) {
|
|
16
29
|
const totalMinutes = Math.max(1, Math.ceil(remainingMs / 60_000));
|
|
17
30
|
const days = Math.floor(totalMinutes / 1_440);
|
|
18
31
|
const hours = Math.floor((totalMinutes % 1_440) / 60);
|
|
@@ -22,6 +35,6 @@ export function usageResetText(resetsAt) {
|
|
|
22
35
|
: totalMinutes >= 60
|
|
23
36
|
? t("usage.duration.hours_minutes", Math.floor(totalMinutes / 60), minutes)
|
|
24
37
|
: t("usage.duration.minutes", totalMinutes);
|
|
25
|
-
return
|
|
38
|
+
return duration;
|
|
26
39
|
}
|
|
27
40
|
//# sourceMappingURL=i18n.js.map
|
package/dist/usage/i18n.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../src/usage/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAC;AAGjC,mFAAmF;AACnF,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,GAAkB;IAC5D,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC1D,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,cAAc,CAAC,QAAwB;IACrD,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzB,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9B,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC9C,IAAI,WAAW,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../src/usage/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAC;AAGjC,mFAAmF;AACnF,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,GAAkB;IAC5D,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC1D,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,cAAc,CAAC,QAAwB;IACrD,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzB,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9B,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC9C,IAAI,WAAW,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,CAAC;IACnD,OAAO,CAAC,CAAC,gBAAgB,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,kBAAkB,CAAC,WAA2B;IAC5D,IAAI,CAAC,WAAW;QAAE,OAAO,EAAE,CAAC;IAC5B,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACjE,IAAI,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,OAAO,CAAC,CAAC,kBAAkB,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,oGAAoG;AACpG,SAAS,aAAa,CAAC,WAAmB;IACxC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAClC,MAAM,QAAQ,GAAG,YAAY,IAAI,KAAK;QACpC,CAAC,CAAC,CAAC,CAAC,2BAA2B,EAAE,IAAI,EAAE,KAAK,CAAC;QAC7C,CAAC,CAAC,YAAY,IAAI,EAAE;YAClB,CAAC,CAAC,CAAC,CAAC,8BAA8B,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,EAAE,OAAO,CAAC;YAC3E,CAAC,CAAC,CAAC,CAAC,wBAAwB,EAAE,YAAY,CAAC,CAAC;IAChD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -19,6 +19,11 @@ export interface UsageMetric {
|
|
|
19
19
|
note?: string;
|
|
20
20
|
noteI18n?: UsageI18nRef;
|
|
21
21
|
resetsAt?: string | null;
|
|
22
|
+
/**
|
|
23
|
+
* "expiry": `resetsAt` is when this allowance runs out for good (Kiro bonus credits), not when it refills. Such a
|
|
24
|
+
* time is never a provider's next reset (#1232 review). Absent: a genuine reset.
|
|
25
|
+
*/
|
|
26
|
+
resetKind?: "expiry";
|
|
22
27
|
windowMs?: number | null;
|
|
23
28
|
}
|
|
24
29
|
export interface ProviderUsage {
|
|
@@ -33,6 +38,11 @@ export interface ProviderUsage {
|
|
|
33
38
|
hint?: string;
|
|
34
39
|
hintI18n?: UsageI18nRef;
|
|
35
40
|
metrics: UsageMetric[];
|
|
41
|
+
/**
|
|
42
|
+
* The soonest reset still ahead among the visible metrics' windows (#1232), as an ISO time; null when none of them
|
|
43
|
+
* says when it resets. Set by getUsageSnapshot on every call (never cached), absent on a provider that is not ok.
|
|
44
|
+
*/
|
|
45
|
+
nextResetAt?: string | null;
|
|
36
46
|
}
|
|
37
47
|
export interface UsageI18nRef {
|
|
38
48
|
key: UsageI18nKey;
|
package/dist/usage/providers.js
CHANGED
|
@@ -1093,7 +1093,9 @@ export async function fetchKiroUsage(storeHome) {
|
|
|
1093
1093
|
labelI18n: i18n("usage.metric.bonus_credits"),
|
|
1094
1094
|
type: "percent",
|
|
1095
1095
|
used: Math.min(100, (bUsed / bLimit) * 100),
|
|
1096
|
+
// When the first code expires — the credits go, nothing refills.
|
|
1096
1097
|
resetsAt: expiries.length ? kiroEpochIso(Math.min(...expiries)) : null,
|
|
1098
|
+
resetKind: "expiry",
|
|
1097
1099
|
note: `${+bUsed.toFixed(1)} / ${+bLimit.toFixed(0)} ${unit} · ${bonuses.length} codes`,
|
|
1098
1100
|
noteI18n: i18n("usage.note.bonus_codes", +bUsed.toFixed(1), +bLimit.toFixed(0), unit, bonuses.length),
|
|
1099
1101
|
});
|