@songsid/agend 2.3.4-beta.8 → 2.3.4

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 (167) hide show
  1. package/THIRD_PARTY_NOTICES.md +37 -0
  2. package/dist/agent-cli-instructions.md +1 -1
  3. package/dist/agent-credentials.d.ts +24 -0
  4. package/dist/agent-credentials.js +66 -0
  5. package/dist/agent-credentials.js.map +1 -0
  6. package/dist/agent-endpoint.d.ts +3 -1
  7. package/dist/agent-endpoint.js +13 -0
  8. package/dist/agent-endpoint.js.map +1 -1
  9. package/dist/auth-api.js +5 -0
  10. package/dist/auth-api.js.map +1 -1
  11. package/dist/authority/session-authorizations.d.ts +28 -4
  12. package/dist/authority/session-authorizations.js +63 -17
  13. package/dist/authority/session-authorizations.js.map +1 -1
  14. package/dist/backend/antigravity.js +3 -1
  15. package/dist/backend/antigravity.js.map +1 -1
  16. package/dist/backend/codex-footer-hints.d.ts +4 -0
  17. package/dist/backend/codex-footer-hints.js +27 -0
  18. package/dist/backend/codex-footer-hints.js.map +1 -0
  19. package/dist/backend/codex-metadata.d.ts +14 -0
  20. package/dist/backend/codex-metadata.js +45 -0
  21. package/dist/backend/codex-metadata.js.map +1 -1
  22. package/dist/backend/codex-speed.d.ts +14 -0
  23. package/dist/backend/codex-speed.js +35 -0
  24. package/dist/backend/codex-speed.js.map +1 -0
  25. package/dist/backend/codex.js +17 -5
  26. package/dist/backend/codex.js.map +1 -1
  27. package/dist/backend/kiro.js +11 -1
  28. package/dist/backend/kiro.js.map +1 -1
  29. package/dist/backend/muse.js +5 -3
  30. package/dist/backend/muse.js.map +1 -1
  31. package/dist/cache-ledger.d.ts +95 -0
  32. package/dist/cache-ledger.js +276 -4
  33. package/dist/cache-ledger.js.map +1 -1
  34. package/dist/cache-service.d.ts +5 -0
  35. package/dist/cache-service.js +16 -4
  36. package/dist/cache-service.js.map +1 -1
  37. package/dist/channel/adapters/discord.js +11 -3
  38. package/dist/channel/adapters/discord.js.map +1 -1
  39. package/dist/channel/adapters/telegram.d.ts +51 -0
  40. package/dist/channel/adapters/telegram.js +235 -17
  41. package/dist/channel/adapters/telegram.js.map +1 -1
  42. package/dist/channel/mcp-tools.js +1 -1
  43. package/dist/channel/mcp-tools.js.map +1 -1
  44. package/dist/channel/types.d.ts +2 -0
  45. package/dist/config-validator.js +5 -0
  46. package/dist/config-validator.js.map +1 -1
  47. package/dist/cross-instance-notice.d.ts +10 -0
  48. package/dist/cross-instance-notice.js +67 -5
  49. package/dist/cross-instance-notice.js.map +1 -1
  50. package/dist/daemon.d.ts +3 -0
  51. package/dist/daemon.js +11 -4
  52. package/dist/daemon.js.map +1 -1
  53. package/dist/export-import.d.ts +7 -0
  54. package/dist/export-import.js +44 -10
  55. package/dist/export-import.js.map +1 -1
  56. package/dist/fleet-context.d.ts +2 -0
  57. package/dist/fleet-manager.d.ts +93 -4
  58. package/dist/fleet-manager.js +451 -39
  59. package/dist/fleet-manager.js.map +1 -1
  60. package/dist/general-knowledge/skills/concepts/SKILL.md +3 -0
  61. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +1 -0
  62. package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +1 -1
  63. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +7 -2
  64. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
  65. package/dist/instance-config-impact.js +2 -0
  66. package/dist/instance-config-impact.js.map +1 -1
  67. package/dist/instance-lifecycle.d.ts +6 -2
  68. package/dist/instance-lifecycle.js +11 -6
  69. package/dist/instance-lifecycle.js.map +1 -1
  70. package/dist/instance-topic-delete.d.ts +77 -0
  71. package/dist/instance-topic-delete.js +123 -0
  72. package/dist/instance-topic-delete.js.map +1 -0
  73. package/dist/instructions.d.ts +1 -1
  74. package/dist/instructions.js +1 -1
  75. package/dist/instructions.js.map +1 -1
  76. package/dist/locale.js +33 -1
  77. package/dist/locale.js.map +1 -1
  78. package/dist/log-service.d.ts +28 -0
  79. package/dist/log-service.js +29 -0
  80. package/dist/log-service.js.map +1 -1
  81. package/dist/log-steps.d.ts +4 -3
  82. package/dist/log-steps.js +32 -7
  83. package/dist/log-steps.js.map +1 -1
  84. package/dist/logger.d.ts +12 -0
  85. package/dist/logger.js +43 -1
  86. package/dist/logger.js.map +1 -1
  87. package/dist/missed-offline.d.ts +25 -0
  88. package/dist/missed-offline.js +79 -0
  89. package/dist/missed-offline.js.map +1 -0
  90. package/dist/needs-you-hub.d.ts +6 -1
  91. package/dist/needs-you-hub.js +30 -7
  92. package/dist/needs-you-hub.js.map +1 -1
  93. package/dist/needs-you.d.ts +16 -2
  94. package/dist/needs-you.js +9 -1
  95. package/dist/needs-you.js.map +1 -1
  96. package/dist/outbound-handlers.js +3 -3
  97. package/dist/outbound-handlers.js.map +1 -1
  98. package/dist/outbound-schemas.js +5 -2
  99. package/dist/outbound-schemas.js.map +1 -1
  100. package/dist/scheduler/db.d.ts +2 -1
  101. package/dist/scheduler/db.js +23 -9
  102. package/dist/scheduler/db.js.map +1 -1
  103. package/dist/settings-api.d.ts +13 -0
  104. package/dist/settings-api.js +22 -1
  105. package/dist/settings-api.js.map +1 -1
  106. package/dist/settings-change.js +16 -1
  107. package/dist/settings-change.js.map +1 -1
  108. package/dist/settings-instance-patch.js +27 -0
  109. package/dist/settings-instance-patch.js.map +1 -1
  110. package/dist/tool-progress.d.ts +2 -0
  111. package/dist/tool-progress.js +19 -3
  112. package/dist/tool-progress.js.map +1 -1
  113. package/dist/topic-commands.js +4 -0
  114. package/dist/topic-commands.js.map +1 -1
  115. package/dist/ui/chat-md-strings.js +9 -0
  116. package/dist/ui/chat-strings.js +2 -2
  117. package/dist/ui/chat-thread.js +53 -3
  118. package/dist/ui/fleet-usage.js +90 -27
  119. package/dist/ui/panel-chat.js +5 -2
  120. package/dist/ui/panel-needs.js +13 -5
  121. package/dist/ui/preview-panel.js +78 -2
  122. package/dist/ui/settings-codex-copy.js +19 -0
  123. package/dist/ui/settings-dialogs.js +8 -0
  124. package/dist/ui/settings-model.js +16 -2
  125. package/dist/ui/settings-strings.js +2 -2
  126. package/dist/ui/shared/app-i18n.js +2 -2
  127. package/dist/ui/shared/app.css +19 -0
  128. package/dist/ui/shared/panel-view.js +77 -4
  129. package/dist/ui/shared/usage-dialog.js +18 -1
  130. package/dist/ui/shared/view-history.css +17 -0
  131. package/dist/ui/shared/view-history.js +438 -0
  132. package/dist/ui/shared/view-strings.js +16 -0
  133. package/dist/usage/capacity.d.ts +69 -0
  134. package/dist/usage/capacity.js +119 -0
  135. package/dist/usage/capacity.js.map +1 -0
  136. package/dist/usage/i18n-keys.d.ts +1 -1
  137. package/dist/usage/i18n-keys.js +3 -0
  138. package/dist/usage/i18n-keys.js.map +1 -1
  139. package/dist/usage/profile-timeline.d.ts +16 -0
  140. package/dist/usage/profile-timeline.js +58 -0
  141. package/dist/usage/profile-timeline.js.map +1 -0
  142. package/dist/usage/quota-history.d.ts +43 -0
  143. package/dist/usage/quota-history.js +168 -0
  144. package/dist/usage/quota-history.js.map +1 -0
  145. package/dist/usage/usage-api.d.ts +31 -0
  146. package/dist/usage/usage-api.js +80 -1
  147. package/dist/usage/usage-api.js.map +1 -1
  148. package/dist/usage-report.d.ts +25 -1
  149. package/dist/usage-report.js +55 -4
  150. package/dist/usage-report.js.map +1 -1
  151. package/dist/usage-rollup.d.ts +6 -1
  152. package/dist/usage-rollup.js +16 -4
  153. package/dist/usage-rollup.js.map +1 -1
  154. package/dist/view-api.d.ts +16 -0
  155. package/dist/view-api.js +100 -16
  156. package/dist/view-api.js.map +1 -1
  157. package/dist/view-history.d.ts +84 -0
  158. package/dist/view-history.js +116 -0
  159. package/dist/view-history.js.map +1 -0
  160. package/dist/web-api.d.ts +6 -0
  161. package/dist/web-api.js +8 -2
  162. package/dist/web-api.js.map +1 -1
  163. package/dist/web-session-authorization.d.ts +13 -2
  164. package/dist/web-session-authorization.js +10 -3
  165. package/dist/web-session-authorization.js.map +1 -1
  166. package/launcher/private-home.cjs +1 -1
  167. package/package.json +1 -1
@@ -0,0 +1,69 @@
1
+ /**
2
+ * #3066 §1: how much is 100% of an account's weekly quota, measured — what this account's agents used while the vendor's
3
+ * weekly percent moved (the readings of quota-history.ts, the hours of the usage ledger). Pure: the fleet gathers both.
4
+ *
5
+ * Within one reset window (the same `resetsAt`, the percent never going down) the first and last readings bound an
6
+ * interval: capacity = consumed in it ÷ Δ%. Two uncertainties, both carried into the range, never hidden:
7
+ * - the vendors report whole percents, so Δ is ±1: the range divides by Δ+1 … Δ−1;
8
+ * - the ledger is hourly, so the two boundary hours are uncertain: without them is the low end, with them the high.
9
+ * A window counts only when Δ ≥ 10 points (rounding then costs ≤ ~±10%). Several windows: the median of their midpoints,
10
+ * the range their lowest low … highest high.
11
+ *
12
+ * Three bases, because the vendor's weighting is not published: the API-equivalent at list price (likely what the quota
13
+ * tracks — it weighs Opus over Sonnet, cache reads far below input), output tokens, all tokens. With three windows or
14
+ * more, the basis whose capacity varies least across weeks (coefficient of variation) is named as the best fit.
15
+ *
16
+ * What it cannot see: the same account used outside this fleet (claude.ai, another machine) spends the quota without a
17
+ * token here, so the estimate comes out LOW; a window whose bases disagree with each other by more than 2× is marked
18
+ * `mixed`. The page says so (usage.capacity.only_agend, usage.capacity.mixed).
19
+ */
20
+ import { type QuotaReading } from "./quota-history.js";
21
+ export declare const CAPACITY_MIN_DELTA = 10;
22
+ export declare const BASES: readonly ["api", "output", "all"];
23
+ export type CapacityBasis = typeof BASES[number];
24
+ /** One hour of one account's usage, on each basis (api null: a model in it has no list price). */
25
+ export interface CapacityHour {
26
+ hour: number;
27
+ api: number | null;
28
+ output: number;
29
+ all: number;
30
+ }
31
+ /** `windows`: how many reset windows this basis was measured over (a basis can miss one: an unpriced hour). */
32
+ export interface BasisEstimate {
33
+ median: number;
34
+ low: number;
35
+ high: number;
36
+ cv: number | null;
37
+ windows: number;
38
+ }
39
+ export type CapacityEstimate = {
40
+ status: "ok";
41
+ windows: number;
42
+ deltaPct: number;
43
+ bases: Partial<Record<CapacityBasis, BasisEstimate>>;
44
+ bestFit: CapacityBasis | null;
45
+ mixed: boolean;
46
+ } | {
47
+ status: "not_enough";
48
+ deltaPct: number;
49
+ };
50
+ interface Window {
51
+ t1: number;
52
+ p1: number;
53
+ t2: number;
54
+ p2: number;
55
+ }
56
+ /**
57
+ * The reset windows the readings span: a new `resetsAt`, or a percent that went down, starts the next. "New" is
58
+ * quota-history's sameReset (Prism #3073 P2): a vendor that gives only "resets in N seconds" drifts by seconds between
59
+ * reads and is still one window; compared with the window's latest reading, a real reset moves it by days.
60
+ */
61
+ export declare function quotaWindows(readings: readonly QuotaReading[]): Window[];
62
+ /**
63
+ * The estimate for one account's one weekly metric. `available(from, to)`: whether every hour from `from` to `to` is in
64
+ * the ledger — kept and scanned (Prism #3073 P2): the quota history keeps 12 weeks, the hours 31 days, so a window
65
+ * outside them would count as zero use and the estimate would come out absurdly low. Such a window is left out.
66
+ * Null: there are windows to measure, but no usage of this account in any of them on any basis — nothing to show.
67
+ */
68
+ export declare function estimateCapacity(readings: readonly QuotaReading[], hours: readonly CapacityHour[], available?: (from: number, to: number) => boolean): CapacityEstimate | null;
69
+ export {};
@@ -0,0 +1,119 @@
1
+ /**
2
+ * #3066 §1: how much is 100% of an account's weekly quota, measured — what this account's agents used while the vendor's
3
+ * weekly percent moved (the readings of quota-history.ts, the hours of the usage ledger). Pure: the fleet gathers both.
4
+ *
5
+ * Within one reset window (the same `resetsAt`, the percent never going down) the first and last readings bound an
6
+ * interval: capacity = consumed in it ÷ Δ%. Two uncertainties, both carried into the range, never hidden:
7
+ * - the vendors report whole percents, so Δ is ±1: the range divides by Δ+1 … Δ−1;
8
+ * - the ledger is hourly, so the two boundary hours are uncertain: without them is the low end, with them the high.
9
+ * A window counts only when Δ ≥ 10 points (rounding then costs ≤ ~±10%). Several windows: the median of their midpoints,
10
+ * the range their lowest low … highest high.
11
+ *
12
+ * Three bases, because the vendor's weighting is not published: the API-equivalent at list price (likely what the quota
13
+ * tracks — it weighs Opus over Sonnet, cache reads far below input), output tokens, all tokens. With three windows or
14
+ * more, the basis whose capacity varies least across weeks (coefficient of variation) is named as the best fit.
15
+ *
16
+ * What it cannot see: the same account used outside this fleet (claude.ai, another machine) spends the quota without a
17
+ * token here, so the estimate comes out LOW; a window whose bases disagree with each other by more than 2× is marked
18
+ * `mixed`. The page says so (usage.capacity.only_agend, usage.capacity.mixed).
19
+ */
20
+ import { sameReset } from "./quota-history.js";
21
+ export const CAPACITY_MIN_DELTA = 10;
22
+ export const BASES = ["api", "output", "all"];
23
+ const H = 3_600_000;
24
+ /**
25
+ * The reset windows the readings span: a new `resetsAt`, or a percent that went down, starts the next. "New" is
26
+ * quota-history's sameReset (Prism #3073 P2): a vendor that gives only "resets in N seconds" drifts by seconds between
27
+ * reads and is still one window; compared with the window's latest reading, a real reset moves it by days.
28
+ */
29
+ export function quotaWindows(readings) {
30
+ const out = [];
31
+ let cur = null;
32
+ for (const r of [...readings].sort((a, b) => a.t - b.t)) {
33
+ if (!cur || !sameReset(r.resetsAt, cur.resetsAt) || r.used < cur.p2) {
34
+ if (cur)
35
+ out.push(cur);
36
+ cur = { t1: r.t, p1: r.used, t2: r.t, p2: r.used, resetsAt: r.resetsAt };
37
+ }
38
+ else {
39
+ cur.t2 = r.t;
40
+ cur.p2 = r.used;
41
+ cur.resetsAt = r.resetsAt;
42
+ }
43
+ }
44
+ if (cur)
45
+ out.push(cur);
46
+ return out.map(({ t1, p1, t2, p2 }) => ({ t1, p1, t2, p2 }));
47
+ }
48
+ const median = (xs) => { const s = [...xs].sort((a, b) => a - b), m = s.length >> 1; return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2; };
49
+ const cv = (xs) => {
50
+ if (xs.length < 2)
51
+ return null;
52
+ const mean = xs.reduce((a, b) => a + b, 0) / xs.length;
53
+ return mean > 0 ? Math.sqrt(xs.reduce((a, x) => a + (x - mean) ** 2, 0) / xs.length) / mean : null;
54
+ };
55
+ /**
56
+ * The estimate for one account's one weekly metric. `available(from, to)`: whether every hour from `from` to `to` is in
57
+ * the ledger — kept and scanned (Prism #3073 P2): the quota history keeps 12 weeks, the hours 31 days, so a window
58
+ * outside them would count as zero use and the estimate would come out absurdly low. Such a window is left out.
59
+ * Null: there are windows to measure, but no usage of this account in any of them on any basis — nothing to show.
60
+ */
61
+ export function estimateCapacity(readings, hours, available = () => true) {
62
+ const windows = quotaWindows(readings).filter((w) => available(Math.floor(w.t1 / H) * H, w.t2));
63
+ const usable = windows.filter((w) => w.p2 - w.p1 >= CAPACITY_MIN_DELTA);
64
+ if (!usable.length) {
65
+ // No window inside the ledger at all: "not enough usage" would be a claim about weeks it cannot see. Say nothing.
66
+ if (!windows.length)
67
+ return null;
68
+ return { status: "not_enough", deltaPct: Math.max(0, ...windows.map((w) => w.p2 - w.p1)) };
69
+ }
70
+ const per = usable.map((w) => {
71
+ const delta = w.p2 - w.p1;
72
+ const inside = hours.filter((h) => h.hour >= w.t1 && h.hour + H <= w.t2); // whole hours within: low
73
+ const touching = hours.filter((h) => h.hour + H > w.t1 && h.hour < w.t2); // any hour touching: high
74
+ const sum = (list, b) => {
75
+ let n = 0;
76
+ for (const h of list) {
77
+ const v = h[b];
78
+ if (v === null)
79
+ return null;
80
+ n += v;
81
+ }
82
+ return n;
83
+ };
84
+ const out = {};
85
+ for (const b of BASES) {
86
+ const lo = sum(inside, b), hi = sum(touching, b);
87
+ if (lo === null || hi === null || hi <= 0)
88
+ continue; // a basis with an unpriced hour, or nothing used
89
+ out[b] = { low: lo / ((delta + 1) / 100), mid: ((lo + hi) / 2) / (delta / 100), high: hi / ((delta - 1) / 100) };
90
+ }
91
+ return out;
92
+ });
93
+ const bases = {};
94
+ for (const b of BASES) {
95
+ const ws = per.map((p) => p[b]).filter((x) => !!x);
96
+ if (!ws.length)
97
+ continue;
98
+ const mids = ws.map((x) => x.mid);
99
+ bases[b] = { median: median(mids), low: Math.min(...ws.map((x) => x.low)), high: Math.max(...ws.map((x) => x.high)), cv: cv(mids), windows: ws.length };
100
+ }
101
+ if (!BASES.some((b) => bases[b]))
102
+ return null; // Prism #3073 P1: no basis, no estimate
103
+ // Best fit: with three windows or more, the basis that varies least across them.
104
+ const scored = BASES.filter((b) => bases[b]?.cv !== null && bases[b]?.cv !== undefined && bases[b].windows >= 3);
105
+ const bestFit = scored.length ? scored.reduce((a, b) => (bases[b].cv < bases[a].cv ? b : a)) : null;
106
+ // Mixed: in some window the API-equivalent and the token count disagree (each against its own median) by over 2×.
107
+ let mixed = false;
108
+ if (bases.api && bases.all && usable.length >= 2) {
109
+ for (const p of per) {
110
+ if (!p.api || !p.all)
111
+ continue;
112
+ const ra = p.api.mid / bases.api.median, rt = p.all.mid / bases.all.median;
113
+ if (Math.max(ra / rt, rt / ra) > 2)
114
+ mixed = true;
115
+ }
116
+ }
117
+ return { status: "ok", windows: usable.length, deltaPct: Math.max(...usable.map((w) => w.p2 - w.p1)), bases, bestFit, mixed };
118
+ }
119
+ //# sourceMappingURL=capacity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capacity.js","sourceRoot":"","sources":["../../src/usage/capacity.ts"],"names":[],"mappings":"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"}
@@ -3,5 +3,5 @@
3
3
  * Keep this list explicit: the parity test rejects a key missing from either
4
4
  * locale surface before a mixed-language release can ship.
5
5
  */
6
- export declare const USAGE_I18N_KEYS: readonly ["usage.title", "usage.title_plain", "usage.empty_active", "usage.not_logged_in", "usage.default_login", "usage.error_fallback", "usage.no_data", "usage.reset.in", "usage.reset.soon", "usage.duration.days_hours", "usage.duration.hours_minutes", "usage.duration.minutes", "usage.metric.session", "usage.metric.weekly", "usage.metric.monthly", "usage.metric.days_window", "usage.metric.named", "usage.metric.named_session", "usage.metric.named_weekly", "usage.metric.named_monthly", "usage.metric.named_days", "usage.metric.extra_usage", "usage.metric.rate_limit_resets", "usage.ticket_expiry", "usage.metric.credits", "usage.metric.weekly_limit", "usage.metric.pay_as_you_go", "usage.metric.bonus_credits", "usage.metric.overage_charges", "usage.metric.pay_per_use", "usage.unit.available", "usage.unit.credits", "usage.metric.usage_monthly", "usage.note.used_limit_unit", "usage.note.bonus_codes", "usage.value.disabled", "usage.value.cap", "usage.value.unlimited", "usage.note.binding", "usage.note.binding_severity", "usage.note.busiest_model", "usage.note.busiest_models", "usage.metric.agy_claude_others", "usage.metric.agy_claude_others_session", "usage.metric.agy_claude_others_weekly", "usage.hint.statusline_now", "usage.hint.statusline_minutes", "usage.hint.claude_api_key", "usage.hint.login_claude", "usage.hint.login_agy", "usage.hint.no_quota", "usage.hint.login_codex", "usage.hint.codex_no_oauth", "usage.hint.login_grok", "usage.hint.grok_no_login", "usage.hint.login_kiro", "usage.hint.kiro_signed_out", "usage.hint.claude_profile_signed_out", "usage.hint.claude_profile_signed_out_chat", "usage.hint.token_refreshing", "usage.hint.subscription_no_credit", "usage.hint.no_quota_account", "usage.error.unreachable", "usage.error.token_rejected", "usage.error.rate_limited", "usage.error.rate_limited_transient", "usage.stale_rate_limited", "usage.stale.expired", "usage.stale.transient", "usage.stale.error", "usage.metric.reset_since", "usage.error.http", "usage.error.billing_http", "usage.error.invalid_response", "usage.error.invalid_billing_response", "usage.error.invalid_kiro_response", "usage.error.claude_token_expired", "usage.error.codex_token_expired", "usage.error.grok_expired", "usage.error.grok_response_changed", "usage.error.agy_cap", "usage.error.agy_no_refresh", "usage.error.agy_token_expired", "usage.error.agy_login_expired", "usage.error.agy_refresh_empty", "usage.error.grok_auth_readonly", "usage.error.grok_no_refresh", "usage.error.grok_refresh_unreachable", "usage.error.grok_invalid_refresh", "usage.error.grok_persist"];
6
+ export declare const USAGE_I18N_KEYS: readonly ["usage.title", "usage.title_plain", "usage.empty_active", "usage.not_logged_in", "usage.default_login", "usage.error_fallback", "usage.no_data", "usage.reset.in", "usage.reset.soon", "usage.duration.days_hours", "usage.duration.hours_minutes", "usage.duration.minutes", "usage.metric.session", "usage.metric.weekly", "usage.metric.monthly", "usage.metric.days_window", "usage.metric.named", "usage.metric.named_session", "usage.metric.named_weekly", "usage.metric.named_monthly", "usage.metric.named_days", "usage.metric.extra_usage", "usage.metric.rate_limit_resets", "usage.ticket_expiry", "usage.metric.credits", "usage.metric.weekly_limit", "usage.metric.pay_as_you_go", "usage.metric.bonus_credits", "usage.metric.overage_charges", "usage.metric.pay_per_use", "usage.unit.available", "usage.unit.credits", "usage.metric.usage_monthly", "usage.note.used_limit_unit", "usage.note.bonus_codes", "usage.value.disabled", "usage.value.cap", "usage.value.unlimited", "usage.note.binding", "usage.note.binding_severity", "usage.note.busiest_model", "usage.note.busiest_models", "usage.metric.agy_claude_others", "usage.metric.agy_claude_others_session", "usage.metric.agy_claude_others_weekly", "usage.hint.statusline_now", "usage.hint.statusline_minutes", "usage.hint.claude_api_key", "usage.hint.login_claude", "usage.hint.login_agy", "usage.hint.no_quota", "usage.hint.login_codex", "usage.hint.codex_no_oauth", "usage.hint.login_grok", "usage.hint.grok_no_login", "usage.hint.login_kiro", "usage.hint.kiro_signed_out", "usage.hint.claude_profile_signed_out", "usage.hint.claude_profile_signed_out_chat", "usage.hint.token_refreshing", "usage.hint.subscription_no_credit", "usage.hint.no_quota_account", "usage.error.unreachable", "usage.error.token_rejected", "usage.error.rate_limited", "usage.error.rate_limited_transient", "usage.stale_rate_limited", "usage.stale.expired", "usage.stale.transient", "usage.stale.error", "usage.metric.reset_since", "usage.error.http", "usage.error.billing_http", "usage.error.invalid_response", "usage.error.invalid_billing_response", "usage.error.invalid_kiro_response", "usage.error.claude_token_expired", "usage.error.codex_token_expired", "usage.error.grok_expired", "usage.error.grok_response_changed", "usage.error.agy_cap", "usage.error.agy_no_refresh", "usage.error.agy_token_expired", "usage.error.agy_login_expired", "usage.error.agy_refresh_empty", "usage.error.grok_auth_readonly", "usage.error.grok_no_refresh", "usage.error.grok_refresh_unreachable", "usage.error.grok_invalid_refresh", "usage.error.grok_persist", "usage.capacity.line", "usage.capacity.line_tokens", "usage.capacity.not_enough", "usage.capacity.only_agend", "usage.capacity.mixed", "usage.capacity.best_fit_api", "usage.capacity.best_fit_output", "usage.capacity.best_fit_all"];
7
7
  export type UsageI18nKey = typeof USAGE_I18N_KEYS[number];
@@ -31,5 +31,8 @@ export const USAGE_I18N_KEYS = [
31
31
  "usage.error.agy_no_refresh", "usage.error.agy_token_expired", "usage.error.agy_login_expired",
32
32
  "usage.error.agy_refresh_empty", "usage.error.grok_auth_readonly", "usage.error.grok_no_refresh",
33
33
  "usage.error.grok_refresh_unreachable", "usage.error.grok_invalid_refresh", "usage.error.grok_persist",
34
+ // #3066 §1: a weekly quota's measured 100%.
35
+ "usage.capacity.line", "usage.capacity.line_tokens", "usage.capacity.not_enough", "usage.capacity.only_agend", "usage.capacity.mixed",
36
+ "usage.capacity.best_fit_api", "usage.capacity.best_fit_output", "usage.capacity.best_fit_all",
34
37
  ];
35
38
  //# sourceMappingURL=i18n-keys.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"i18n-keys.js","sourceRoot":"","sources":["../../src/usage/i18n-keys.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,aAAa,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,qBAAqB;IACtG,sBAAsB,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB;IAC7E,2BAA2B,EAAE,8BAA8B,EAAE,wBAAwB;IACrF,sBAAsB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,0BAA0B;IACjG,oBAAoB,EAAE,4BAA4B,EAAE,2BAA2B;IAC/E,4BAA4B,EAAE,yBAAyB,EAAE,0BAA0B;IACnF,gCAAgC,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,2BAA2B;IAC5G,4BAA4B,EAAE,4BAA4B,EAAE,8BAA8B;IAC1F,0BAA0B,EAAE,sBAAsB,EAAE,oBAAoB;IACxE,4BAA4B,EAAE,4BAA4B,EAAE,wBAAwB;IACpF,sBAAsB,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,oBAAoB;IACxF,6BAA6B,EAAE,0BAA0B,EAAE,2BAA2B;IACtF,gCAAgC,EAAE,wCAAwC,EAAE,uCAAuC;IACnH,2BAA2B,EAAE,+BAA+B,EAAE,2BAA2B;IACzF,yBAAyB,EAAE,sBAAsB,EAAE,qBAAqB;IACxE,wBAAwB,EAAE,2BAA2B,EAAE,uBAAuB;IAC9E,0BAA0B,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,sCAAsC,EAAE,2CAA2C;IACtK,6BAA6B,EAAE,mCAAmC,EAAE,6BAA6B;IACjG,yBAAyB,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,oCAAoC,EAAE,0BAA0B;IACrJ,qBAAqB,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,0BAA0B;IAC/F,kBAAkB,EAAE,0BAA0B,EAAE,8BAA8B;IAC9E,sCAAsC,EAAE,mCAAmC;IAC3E,kCAAkC,EAAE,iCAAiC;IACrE,0BAA0B,EAAE,mCAAmC,EAAE,qBAAqB;IACtF,4BAA4B,EAAE,+BAA+B,EAAE,+BAA+B;IAC9F,+BAA+B,EAAE,gCAAgC,EAAE,6BAA6B;IAChG,sCAAsC,EAAE,kCAAkC,EAAE,0BAA0B;CAC9F,CAAC"}
1
+ {"version":3,"file":"i18n-keys.js","sourceRoot":"","sources":["../../src/usage/i18n-keys.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,aAAa,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,qBAAqB;IACtG,sBAAsB,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB;IAC7E,2BAA2B,EAAE,8BAA8B,EAAE,wBAAwB;IACrF,sBAAsB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,0BAA0B;IACjG,oBAAoB,EAAE,4BAA4B,EAAE,2BAA2B;IAC/E,4BAA4B,EAAE,yBAAyB,EAAE,0BAA0B;IACnF,gCAAgC,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,2BAA2B;IAC5G,4BAA4B,EAAE,4BAA4B,EAAE,8BAA8B;IAC1F,0BAA0B,EAAE,sBAAsB,EAAE,oBAAoB;IACxE,4BAA4B,EAAE,4BAA4B,EAAE,wBAAwB;IACpF,sBAAsB,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,oBAAoB;IACxF,6BAA6B,EAAE,0BAA0B,EAAE,2BAA2B;IACtF,gCAAgC,EAAE,wCAAwC,EAAE,uCAAuC;IACnH,2BAA2B,EAAE,+BAA+B,EAAE,2BAA2B;IACzF,yBAAyB,EAAE,sBAAsB,EAAE,qBAAqB;IACxE,wBAAwB,EAAE,2BAA2B,EAAE,uBAAuB;IAC9E,0BAA0B,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,sCAAsC,EAAE,2CAA2C;IACtK,6BAA6B,EAAE,mCAAmC,EAAE,6BAA6B;IACjG,yBAAyB,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,oCAAoC,EAAE,0BAA0B;IACrJ,qBAAqB,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,0BAA0B;IAC/F,kBAAkB,EAAE,0BAA0B,EAAE,8BAA8B;IAC9E,sCAAsC,EAAE,mCAAmC;IAC3E,kCAAkC,EAAE,iCAAiC;IACrE,0BAA0B,EAAE,mCAAmC,EAAE,qBAAqB;IACtF,4BAA4B,EAAE,+BAA+B,EAAE,+BAA+B;IAC9F,+BAA+B,EAAE,gCAAgC,EAAE,6BAA6B;IAChG,sCAAsC,EAAE,kCAAkC,EAAE,0BAA0B;IACtG,4CAA4C;IAC5C,qBAAqB,EAAE,4BAA4B,EAAE,2BAA2B,EAAE,2BAA2B,EAAE,sBAAsB;IACrI,6BAA6B,EAAE,gCAAgC,EAAE,6BAA6B;CACtF,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface ProfileSpan {
2
+ t: number;
3
+ profile: string;
4
+ }
5
+ export declare const PROFILE_TIMELINE_FILE = "codex-profile-timeline.json";
6
+ export declare const PROFILE_TIMELINE_MAX = 200;
7
+ export declare const PROFILE_UNRECORDED = "unrecorded";
8
+ /** The file's entries, checked and sorted (an unreadable or missing file: none). */
9
+ export declare function readProfileTimeline(path: string): Promise<ProfileSpan[]>;
10
+ /**
11
+ * A launch under `profile` at `t`. One write at a time per file; nothing is added when the last entry already names the
12
+ * same profile. Never throws: a lost entry leaves that run's turns under the previous entry's profile (or unrecorded).
13
+ */
14
+ export declare function recordProfileLaunch(path: string, profile: string, t?: number): Promise<void>;
15
+ /** The profile in effect at `t`: the latest entry at or before it, else "unrecorded". */
16
+ export declare function profileAt(spans: readonly ProfileSpan[], t: number): string;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * #3066 §1d: which credential profile a Codex instance ran under, and since when — so its usage is told by account. A
3
+ * Codex rollout does not say which login wrote it (so its hours were all "unrecorded"); AgEnD does know which profile it
4
+ * launched each Codex under. Each launch appends `{ t, profile }` to the instance's `codex-profile-timeline.json`; the
5
+ * usage scan tags each turn with the profile in effect at its time (the latest entry at or before it). A switch of
6
+ * profile is a relaunch, so a turn is never between two entries of one run. Turns from before the first entry stay
7
+ * "unrecorded" — history is never guessed.
8
+ *
9
+ * Small (≤ 200 entries, the oldest dropped) and written off the fleet's thread, atomically.
10
+ */
11
+ import { readFile, rename, writeFile } from "node:fs/promises";
12
+ export const PROFILE_TIMELINE_FILE = "codex-profile-timeline.json";
13
+ export const PROFILE_TIMELINE_MAX = 200;
14
+ export const PROFILE_UNRECORDED = "unrecorded";
15
+ /** The file's entries, checked and sorted (an unreadable or missing file: none). */
16
+ export async function readProfileTimeline(path) {
17
+ let v;
18
+ try {
19
+ v = JSON.parse(await readFile(path, "utf8"));
20
+ }
21
+ catch {
22
+ return [];
23
+ }
24
+ const list = v && typeof v === "object" && !Array.isArray(v) && Array.isArray(v.spans) ? v.spans : [];
25
+ return list.filter((e) => !!e && typeof e === "object" && typeof e.t === "number"
26
+ && Number.isFinite(e.t) && typeof e.profile === "string" && e.profile !== "")
27
+ .map((e) => ({ t: e.t, profile: e.profile })).sort((a, b) => a.t - b.t);
28
+ }
29
+ const writes = new Map();
30
+ /**
31
+ * A launch under `profile` at `t`. One write at a time per file; nothing is added when the last entry already names the
32
+ * same profile. Never throws: a lost entry leaves that run's turns under the previous entry's profile (or unrecorded).
33
+ */
34
+ export function recordProfileLaunch(path, profile, t = Date.now()) {
35
+ const next = (writes.get(path) ?? Promise.resolve()).then(async () => {
36
+ const spans = await readProfileTimeline(path);
37
+ if (spans.at(-1)?.profile === profile)
38
+ return;
39
+ const kept = [...spans, { t, profile }].slice(-PROFILE_TIMELINE_MAX);
40
+ const tmp = `${path}.${process.pid}.tmp`;
41
+ await writeFile(tmp, JSON.stringify({ v: 1, spans: kept }), { mode: 0o600 });
42
+ await rename(tmp, path);
43
+ }).catch(() => { });
44
+ writes.set(path, next);
45
+ return next;
46
+ }
47
+ /** The profile in effect at `t`: the latest entry at or before it, else "unrecorded". */
48
+ export function profileAt(spans, t) {
49
+ let found = PROFILE_UNRECORDED;
50
+ for (const s of spans) {
51
+ if (s.t <= t)
52
+ found = s.profile;
53
+ else
54
+ break;
55
+ }
56
+ return found;
57
+ }
58
+ //# sourceMappingURL=profile-timeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-timeline.js","sourceRoot":"","sources":["../../src/usage/profile-timeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { ProviderUsage } from "./providers.js";
2
+ export interface QuotaReading {
3
+ /** When the reading was made (ms). */
4
+ t: number;
5
+ /** The usage row's id: "claude", "claude:hsinlin", "codex:yanxianchen", … — one account. */
6
+ provider: string;
7
+ /** The credential profile, when the row has one. */
8
+ profile?: string;
9
+ /** The metric's label: "Weekly", "Sonnet (weekly)", … */
10
+ metric: string;
11
+ /** "model": the vendor's per-model quota (UsageMetric.scope) — its label names the model. Absent: not one. */
12
+ scope?: "model";
13
+ /** Percent used (0–100, as the vendor reports it). */
14
+ used: number;
15
+ /** When this window resets (ISO), or null when the vendor did not say. */
16
+ resetsAt: string | null;
17
+ windowMs: number | null;
18
+ }
19
+ export declare const QUOTA_HISTORY_KEEP_MS: number;
20
+ export declare const QUOTA_HISTORY_MAX_BYTES: number;
21
+ /**
22
+ * The same reset (Fable r1 #5): a vendor that gives only "resets in N seconds" yields a time that drifts by the read's own
23
+ * timing; within 10 minutes it is the same window, never a new line on every read. A real reset moves it by days.
24
+ */
25
+ export declare const RESET_TOLERANCE_MS: number;
26
+ export declare function sameReset(a: string | null, b: string | null): boolean;
27
+ /** A line from the file, checked whole (it is not trusted): null when it is not a reading. */
28
+ export declare function parseQuotaReading(line: string): QuotaReading | null;
29
+ /** The weekly-or-longer percent metrics of a good read, as readings at `now`. */
30
+ export declare function quotaReadingsOf(p: ProviderUsage, now: number): QuotaReading[];
31
+ /**
32
+ * Record a good read's weekly readings: a line for each metric whose percent or reset time changed since its last line.
33
+ * Never throws and never blocks the caller: the write is queued (one at a time) and a failure only loses this reading.
34
+ */
35
+ export declare function recordQuotaReadings(p: ProviderUsage, now?: number): void;
36
+ /** Every reading kept, oldest first (§1 reads it). Unreadable lines are skipped. */
37
+ export declare function readQuotaHistory(): Promise<QuotaReading[]>;
38
+ /** Tests: write to `path` (null: nowhere), starting from what that file holds; resolves when queued writes are done. */
39
+ export declare function setQuotaHistoryFileForTests(path: string | null): Promise<void>;
40
+ /** Tests (usage-api's fetcher seam): record nothing, at once — never into the real AGEND_HOME. */
41
+ export declare function quotaHistoryOffForTests(): void;
42
+ /** Tests: wait for the queued writes. */
43
+ export declare function flushQuotaHistory(): Promise<void>;
@@ -0,0 +1,168 @@
1
+ /**
2
+ * #3066 §1d: the weekly quota readings over time — the history the capacity estimate (§1) matches against the tokens each
3
+ * account used. Only the readings AgEnD already makes (get_usage, the usage page, the Discord presence refresh, the
4
+ * 30-minute background read): never an extra vendor call of its own.
5
+ *
6
+ * One JSON line per change: a weekly metric's percent or its reset time moved since the last line for that (provider,
7
+ * metric). A week is at most ~100 steps of a whole percent, so ≤ ~110 lines an account-week (~1–3 KB). Kept 12 weeks
8
+ * (older lines are dropped on the first write of each day) and never over 2 MB (the oldest lines go first, whenever the
9
+ * file is over it). A reset time within 10 minutes of the last is the same reset. Written off the fleet's thread
10
+ * (fs/promises), one write at a time.
11
+ * `usage-quota-history.jsonl` in AGEND_HOME.
12
+ */
13
+ import { appendFile, readFile, rename, stat, writeFile } from "node:fs/promises";
14
+ import { join } from "node:path";
15
+ import { getAgendHome } from "../paths.js";
16
+ export const QUOTA_HISTORY_KEEP_MS = 12 * 7 * 24 * 3_600_000;
17
+ export const QUOTA_HISTORY_MAX_BYTES = 2 * 1024 * 1024;
18
+ /** A window of a day or longer is a quota worth matching (the five-hour session window is not). */
19
+ const MIN_WINDOW_MS = 24 * 3_600_000;
20
+ let fileOf = () => join(getAgendHome(), "usage-quota-history.jsonl");
21
+ /** The last reading written per (provider, metric): what a new one is compared with. */
22
+ const last = new Map();
23
+ let loaded = false;
24
+ let chain = Promise.resolve();
25
+ const keyOf = (provider, metric) => `${provider}\u0000${metric}`;
26
+ /**
27
+ * The same reset (Fable r1 #5): a vendor that gives only "resets in N seconds" yields a time that drifts by the read's own
28
+ * timing; within 10 minutes it is the same window, never a new line on every read. A real reset moves it by days.
29
+ */
30
+ export const RESET_TOLERANCE_MS = 10 * 60_000;
31
+ export function sameReset(a, b) {
32
+ if (a === b)
33
+ return true;
34
+ if (a === null || b === null)
35
+ return false;
36
+ const ta = Date.parse(a), tb = Date.parse(b);
37
+ return Number.isFinite(ta) && Number.isFinite(tb) && Math.abs(ta - tb) < RESET_TOLERANCE_MS;
38
+ }
39
+ /** A line from the file, checked whole (it is not trusted): null when it is not a reading. */
40
+ export function parseQuotaReading(line) {
41
+ let v;
42
+ try {
43
+ v = JSON.parse(line);
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ if (!v || typeof v !== "object" || Array.isArray(v))
49
+ return null;
50
+ const r = v;
51
+ if (typeof r.t !== "number" || !Number.isFinite(r.t) || typeof r.provider !== "string" || typeof r.metric !== "string"
52
+ || typeof r.used !== "number" || !Number.isFinite(r.used) || !(r.resetsAt === null || typeof r.resetsAt === "string")
53
+ || !(r.windowMs === null || (typeof r.windowMs === "number" && Number.isFinite(r.windowMs)))
54
+ || !(r.profile === undefined || typeof r.profile === "string") || !(r.scope === undefined || r.scope === "model"))
55
+ return null;
56
+ return { t: r.t, provider: r.provider, ...(r.profile !== undefined ? { profile: r.profile } : {}), metric: r.metric, ...(r.scope === "model" ? { scope: "model" } : {}), used: r.used,
57
+ resetsAt: r.resetsAt, windowMs: r.windowMs };
58
+ }
59
+ /** The weekly-or-longer percent metrics of a good read, as readings at `now`. */
60
+ export function quotaReadingsOf(p, now) {
61
+ if (p.status !== "ok")
62
+ return [];
63
+ const out = [];
64
+ for (const m of p.metrics) {
65
+ if (m.type !== "percent" || typeof m.used !== "number" || !Number.isFinite(m.used))
66
+ continue;
67
+ if (typeof m.windowMs !== "number" || m.windowMs < MIN_WINDOW_MS)
68
+ continue;
69
+ out.push({ t: now, provider: p.id, ...(p.profile ? { profile: p.profile } : {}), metric: m.label, ...(m.scope === "model" ? { scope: "model" } : {}), used: m.used,
70
+ resetsAt: typeof m.resetsAt === "string" ? m.resetsAt : null, windowMs: m.windowMs });
71
+ }
72
+ return out;
73
+ }
74
+ async function load(path) {
75
+ if (loaded)
76
+ return;
77
+ loaded = true;
78
+ let text = "";
79
+ try {
80
+ text = await readFile(path, "utf8");
81
+ }
82
+ catch {
83
+ return;
84
+ } // none yet
85
+ for (const line of text.split("\n")) {
86
+ const r = line ? parseQuotaReading(line) : null;
87
+ if (r)
88
+ last.set(keyOf(r.provider, r.metric), { used: r.used, resetsAt: r.resetsAt });
89
+ }
90
+ }
91
+ /** When lines older than 12 weeks were last dropped: once a day (Fable r1 #3), and whenever the file is over its cap. */
92
+ let lastAgePrune = Number.NEGATIVE_INFINITY;
93
+ const AGE_PRUNE_EVERY_MS = 24 * 3_600_000;
94
+ /** Keep the last 12 weeks, and the file under its cap (oldest first out). Rewritten atomically. */
95
+ async function prune(path, now) {
96
+ let size = 0;
97
+ try {
98
+ size = (await stat(path)).size;
99
+ }
100
+ catch {
101
+ return;
102
+ }
103
+ if (size <= QUOTA_HISTORY_MAX_BYTES && now - lastAgePrune < AGE_PRUNE_EVERY_MS)
104
+ return;
105
+ lastAgePrune = now;
106
+ const lines = (await readFile(path, "utf8")).split("\n").filter((l) => {
107
+ const r = l ? parseQuotaReading(l) : null;
108
+ return r !== null && r.t >= now - QUOTA_HISTORY_KEEP_MS;
109
+ });
110
+ let bytes = lines.reduce((n, l) => n + Buffer.byteLength(l) + 1, 0), from = 0;
111
+ while (bytes > QUOTA_HISTORY_MAX_BYTES * 0.9 && from < lines.length)
112
+ bytes -= Buffer.byteLength(lines[from++]) + 1;
113
+ const tmp = `${path}.${process.pid}.tmp`;
114
+ await writeFile(tmp, lines.slice(from).map((l) => `${l}\n`).join(""), { mode: 0o600 });
115
+ await rename(tmp, path);
116
+ }
117
+ /**
118
+ * Record a good read's weekly readings: a line for each metric whose percent or reset time changed since its last line.
119
+ * Never throws and never blocks the caller: the write is queued (one at a time) and a failure only loses this reading.
120
+ */
121
+ export function recordQuotaReadings(p, now = Date.now()) {
122
+ if (!fileOf)
123
+ return;
124
+ const readings = quotaReadingsOf(p, now);
125
+ if (!readings.length)
126
+ return;
127
+ const path = fileOf();
128
+ chain = chain.then(async () => {
129
+ await load(path);
130
+ const fresh = readings.filter((r) => {
131
+ const was = last.get(keyOf(r.provider, r.metric));
132
+ return !was || was.used !== r.used || !sameReset(was.resetsAt, r.resetsAt);
133
+ });
134
+ if (!fresh.length)
135
+ return;
136
+ await appendFile(path, fresh.map((r) => `${JSON.stringify(r)}\n`).join(""), { mode: 0o600 });
137
+ for (const r of fresh)
138
+ last.set(keyOf(r.provider, r.metric), { used: r.used, resetsAt: r.resetsAt });
139
+ await prune(path, now);
140
+ }).catch(() => { });
141
+ }
142
+ /** Every reading kept, oldest first (§1 reads it). Unreadable lines are skipped. */
143
+ export async function readQuotaHistory() {
144
+ if (!fileOf)
145
+ return [];
146
+ await chain;
147
+ let text = "";
148
+ try {
149
+ text = await readFile(fileOf(), "utf8");
150
+ }
151
+ catch {
152
+ return [];
153
+ }
154
+ return text.split("\n").map((l) => (l ? parseQuotaReading(l) : null)).filter((r) => r !== null).sort((a, b) => a.t - b.t);
155
+ }
156
+ /** Tests: write to `path` (null: nowhere), starting from what that file holds; resolves when queued writes are done. */
157
+ export async function setQuotaHistoryFileForTests(path) {
158
+ await chain;
159
+ fileOf = path === null ? null : () => path;
160
+ last.clear();
161
+ loaded = false;
162
+ lastAgePrune = Number.NEGATIVE_INFINITY;
163
+ }
164
+ /** Tests (usage-api's fetcher seam): record nothing, at once — never into the real AGEND_HOME. */
165
+ export function quotaHistoryOffForTests() { fileOf = null; last.clear(); loaded = false; }
166
+ /** Tests: wait for the queued writes. */
167
+ export function flushQuotaHistory() { return chain; }
168
+ //# sourceMappingURL=quota-history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota-history.js","sourceRoot":"","sources":["../../src/usage/quota-history.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,8 @@ import type { IncomingMessage, ServerResponse } from "node:http";
15
15
  import type { FleetConfig } from "../types.js";
16
16
  import type { Logger } from "pino";
17
17
  import { type ProviderUsage, type UsageMetric } from "./providers.js";
18
+ import type { CapacityBasis, CapacityEstimate } from "./capacity.js";
19
+ import type { UsageI18nRef } from "./providers.js";
18
20
  export interface UsageApiContext {
19
21
  readonly fleetConfig: FleetConfig | null;
20
22
  readonly logger: Logger;
@@ -50,6 +52,35 @@ export declare function setUsageLastGoodFileForTests(path: string | null): Promi
50
52
  export declare function getUsageSnapshot(force?: boolean, providerIds?: Iterable<string>, opts?: {
51
53
  allowStale?: boolean;
52
54
  }): Promise<UsagePayload>;
55
+ /** A weekly metric's measured 100%, as served: the numbers, and its lines in words (text + key, like a metric's note). */
56
+ export interface UsageCapacity {
57
+ status: "ok" | "not_enough";
58
+ deltaPct: number;
59
+ windows?: number;
60
+ basis?: CapacityBasis;
61
+ median?: number;
62
+ low?: number;
63
+ high?: number;
64
+ /** All tokens at 100% (median), beside an API-equivalent line. */
65
+ tokens?: number;
66
+ bestFit?: CapacityBasis | null;
67
+ mixed?: boolean;
68
+ line: {
69
+ text: string;
70
+ i18n: UsageI18nRef;
71
+ };
72
+ notes: Array<{
73
+ text: string;
74
+ i18n: UsageI18nRef;
75
+ }>;
76
+ }
77
+ export declare function setCapacityLookup(fn: ((provider: string, metric: string) => CapacityEstimate | null) | null): void;
78
+ export declare const compactTokens: (n: number) => string;
79
+ /**
80
+ * The words for an estimate (English text; the page shows its own language from the key). Null when an "ok" estimate
81
+ * has no basis (Prism #3073 P1: estimateCapacity never makes one, but a read must not fail on it).
82
+ */
83
+ export declare function capacityText(est: CapacityEstimate): UsageCapacity | null;
53
84
  /**
54
85
  * Render a usage payload as one compact chat message.
55
86
  *