@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.
Files changed (71) hide show
  1. package/dist/agent-cli.js +36 -4
  2. package/dist/agent-cli.js.map +1 -1
  3. package/dist/agent-endpoint.d.ts +3 -1
  4. package/dist/agent-endpoint.js +7 -1
  5. package/dist/agent-endpoint.js.map +1 -1
  6. package/dist/backend/claude-code.d.ts +2 -0
  7. package/dist/backend/claude-code.js +2 -2
  8. package/dist/backend/claude-code.js.map +1 -1
  9. package/dist/backend/session-signals.d.ts +154 -0
  10. package/dist/backend/session-signals.js +430 -0
  11. package/dist/backend/session-signals.js.map +1 -0
  12. package/dist/channel/adapters/discord.d.ts +38 -1
  13. package/dist/channel/adapters/discord.js +131 -8
  14. package/dist/channel/adapters/discord.js.map +1 -1
  15. package/dist/channel/adapters/telegram.d.ts +19 -1
  16. package/dist/channel/adapters/telegram.js +55 -0
  17. package/dist/channel/adapters/telegram.js.map +1 -1
  18. package/dist/channel/mcp-tools.js +5 -1
  19. package/dist/channel/mcp-tools.js.map +1 -1
  20. package/dist/channel/message-queue.d.ts +6 -0
  21. package/dist/channel/message-queue.js +21 -0
  22. package/dist/channel/message-queue.js.map +1 -1
  23. package/dist/channel/sticker-download.d.ts +14 -0
  24. package/dist/channel/sticker-download.js +42 -0
  25. package/dist/channel/sticker-download.js.map +1 -0
  26. package/dist/channel/tool-router.js +12 -5
  27. package/dist/channel/tool-router.js.map +1 -1
  28. package/dist/channel/types.d.ts +52 -0
  29. package/dist/cli-flags.d.ts +8 -0
  30. package/dist/cli-flags.js +22 -0
  31. package/dist/cli-flags.js.map +1 -0
  32. package/dist/config-validator.js +25 -0
  33. package/dist/config-validator.js.map +1 -1
  34. package/dist/daemon.d.ts +20 -0
  35. package/dist/daemon.js +155 -3
  36. package/dist/daemon.js.map +1 -1
  37. package/dist/delivery-outbox.d.ts +8 -0
  38. package/dist/delivery-outbox.js.map +1 -1
  39. package/dist/event-loop-watch.d.ts +25 -0
  40. package/dist/event-loop-watch.js +40 -0
  41. package/dist/event-loop-watch.js.map +1 -0
  42. package/dist/fleet-manager.d.ts +38 -3
  43. package/dist/fleet-manager.js +248 -12
  44. package/dist/fleet-manager.js.map +1 -1
  45. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +4 -2
  46. package/dist/locale.js +4 -0
  47. package/dist/locale.js.map +1 -1
  48. package/dist/outbound-schemas.d.ts +15 -1
  49. package/dist/outbound-schemas.js +19 -1
  50. package/dist/outbound-schemas.js.map +1 -1
  51. package/dist/tool-permissions.js +8 -1
  52. package/dist/tool-permissions.js.map +1 -1
  53. package/dist/turn-reply-guard.d.ts +7 -0
  54. package/dist/turn-reply-guard.js +11 -0
  55. package/dist/turn-reply-guard.js.map +1 -1
  56. package/dist/turn-resume.d.ts +80 -0
  57. package/dist/turn-resume.js +217 -0
  58. package/dist/turn-resume.js.map +1 -0
  59. package/dist/ui/view.html +8 -2
  60. package/dist/usage/format-rich.js +6 -1
  61. package/dist/usage/format-rich.js.map +1 -1
  62. package/dist/usage/i18n.d.ts +2 -0
  63. package/dist/usage/i18n.js +14 -1
  64. package/dist/usage/i18n.js.map +1 -1
  65. package/dist/usage/providers.d.ts +10 -0
  66. package/dist/usage/providers.js +2 -0
  67. package/dist/usage/providers.js.map +1 -1
  68. package/dist/usage/usage-api.d.ts +9 -0
  69. package/dist/usage/usage-api.js +31 -2
  70. package/dist/usage/usage-api.js.map +1 -1
  71. 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":"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"}
1
+ {"version":3,"file":"format-rich.js","sourceRoot":"","sources":["../../src/usage/format-rich.ts"],"names":[],"mappings":"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"}
@@ -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;
@@ -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 t("usage.reset.in", duration);
38
+ return duration;
26
39
  }
27
40
  //# sourceMappingURL=i18n.js.map
@@ -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;IAEnD,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,CAAC,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;AACvC,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;
@@ -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
  });