@victor-software-house/pi-multicodex 1.0.9 → 1.0.10
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/README.md +53 -5
- package/package.json +1 -1
- package/status.ts +15 -10
package/README.md
CHANGED
|
@@ -69,11 +69,59 @@ Current direction:
|
|
|
69
69
|
|
|
70
70
|
Current next step:
|
|
71
71
|
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
- refine the footer color palette with small visual adjustments only
|
|
73
|
+
- document the account-rotation behavior contract explicitly
|
|
74
|
+
- improve the `/multicodex-use` and `/multicodex-status` everyday UX
|
|
75
|
+
|
|
76
|
+
## Behavior contract
|
|
77
|
+
|
|
78
|
+
The current runtime behavior is:
|
|
79
|
+
|
|
80
|
+
### Account selection priority
|
|
81
|
+
|
|
82
|
+
1. Use the manual account selected with `/multicodex-use` when it is still available.
|
|
83
|
+
2. Otherwise clear the stale manual override and select the best available managed account.
|
|
84
|
+
3. Best-account selection prefers:
|
|
85
|
+
- untouched accounts with usage data
|
|
86
|
+
- then the account whose weekly reset window ends first
|
|
87
|
+
- then a random available account as fallback
|
|
88
|
+
|
|
89
|
+
### Quota exhaustion semantics
|
|
90
|
+
|
|
91
|
+
- Quota and rate-limit style failures are detected from provider error text.
|
|
92
|
+
- When a request fails before any output is streamed, MultiCodex marks that account exhausted and retries on another account.
|
|
93
|
+
- Exhaustion lasts until the next known reset time.
|
|
94
|
+
- If usage data does not provide a reset time, exhaustion falls back to a 1 hour cooldown.
|
|
95
|
+
|
|
96
|
+
### Retry policy
|
|
97
|
+
|
|
98
|
+
- MultiCodex retries account rotation up to 5 times for a single request.
|
|
99
|
+
- Retries only happen for quota/rate-limit style failures that occur before output is forwarded.
|
|
100
|
+
- Once output has started streaming, the original error is surfaced instead of rotating.
|
|
101
|
+
|
|
102
|
+
### Manual override behavior
|
|
103
|
+
|
|
104
|
+
- `/multicodex-use <identifier>` sets the manual account override immediately.
|
|
105
|
+
- `/multicodex-use` with no argument opens the account picker and sets the selected manual override.
|
|
106
|
+
- Manual override is session-local state.
|
|
107
|
+
- Manual override clears automatically when the selected account is no longer available or when it hits quota during rotation.
|
|
108
|
+
|
|
109
|
+
### Usage cache and refresh rules
|
|
110
|
+
|
|
111
|
+
- Usage is cached in memory for 5 minutes per account.
|
|
112
|
+
- Footer updates render cached usage immediately and refresh in the background when needed.
|
|
113
|
+
- Rapid `model_select` changes debounce background refresh work so non-Codex model switching clears the footer immediately.
|
|
114
|
+
|
|
115
|
+
### Error classification
|
|
116
|
+
|
|
117
|
+
Quota rotation currently treats these error classes as interchangeable:
|
|
118
|
+
|
|
119
|
+
- HTTP `429`
|
|
120
|
+
- `quota`
|
|
121
|
+
- `usage limit`
|
|
122
|
+
- `rate limit`
|
|
123
|
+
- `too many requests`
|
|
124
|
+
- `limit reached`
|
|
77
125
|
|
|
78
126
|
## Release validation
|
|
79
127
|
|
package/package.json
CHANGED
package/status.ts
CHANGED
|
@@ -23,6 +23,7 @@ const SETTINGS_FILE = path.join(os.homedir(), ".pi", "agent", "settings.json");
|
|
|
23
23
|
const REFRESH_INTERVAL_MS = 60_000;
|
|
24
24
|
const MODEL_SELECT_REFRESH_DEBOUNCE_MS = 250;
|
|
25
25
|
const UNKNOWN_PERCENT = "--";
|
|
26
|
+
const BRAND_LABEL = "Codex";
|
|
26
27
|
const FIVE_HOUR_LABEL = "5h:";
|
|
27
28
|
const SEVEN_DAY_LABEL = "7d:";
|
|
28
29
|
|
|
@@ -139,13 +140,21 @@ function usedToDisplayPercent(
|
|
|
139
140
|
return mode === "left" ? left : clampPercent(100 - left);
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
function formatBrand(ctx: ExtensionContext): string {
|
|
144
|
+
return ctx.ui.theme.fg("muted", BRAND_LABEL);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function formatLoading(ctx: ExtensionContext): string {
|
|
148
|
+
return ctx.ui.theme.fg("muted", "loading...");
|
|
149
|
+
}
|
|
150
|
+
|
|
142
151
|
function formatPercent(
|
|
143
152
|
ctx: ExtensionContext,
|
|
144
153
|
displayPercent: number | undefined,
|
|
145
154
|
mode: PercentDisplayMode,
|
|
146
155
|
): string {
|
|
147
156
|
if (typeof displayPercent !== "number" || Number.isNaN(displayPercent)) {
|
|
148
|
-
return ctx.ui.theme.fg("
|
|
157
|
+
return ctx.ui.theme.fg("dim", UNKNOWN_PERCENT);
|
|
149
158
|
}
|
|
150
159
|
|
|
151
160
|
const text = `${Math.round(clampPercent(displayPercent))}% ${mode}`;
|
|
@@ -201,7 +210,7 @@ function formatUsageSegment(
|
|
|
201
210
|
if (showReset) {
|
|
202
211
|
const countdown = formatResetCountdown(resetAt);
|
|
203
212
|
if (countdown) {
|
|
204
|
-
parts.push(ctx.ui.theme.fg("
|
|
213
|
+
parts.push(ctx.ui.theme.fg("muted", `(↺${countdown})`));
|
|
205
214
|
}
|
|
206
215
|
}
|
|
207
216
|
return parts.join(" ");
|
|
@@ -221,11 +230,7 @@ export function formatActiveAccountStatus(
|
|
|
221
230
|
? ctx.ui.theme.fg("muted", accountEmail)
|
|
222
231
|
: undefined;
|
|
223
232
|
if (!usage) {
|
|
224
|
-
return [
|
|
225
|
-
ctx.ui.theme.fg("dim", "Codex"),
|
|
226
|
-
accountText,
|
|
227
|
-
ctx.ui.theme.fg("dim", "loading..."),
|
|
228
|
-
]
|
|
233
|
+
return [formatBrand(ctx), accountText, formatLoading(ctx)]
|
|
229
234
|
.filter(Boolean)
|
|
230
235
|
.join(" ");
|
|
231
236
|
}
|
|
@@ -249,8 +254,8 @@ export function formatActiveAccountStatus(
|
|
|
249
254
|
|
|
250
255
|
const leading =
|
|
251
256
|
preferences.order === "account-first"
|
|
252
|
-
? [ctx
|
|
253
|
-
: [ctx
|
|
257
|
+
? [formatBrand(ctx), accountText]
|
|
258
|
+
: [formatBrand(ctx)];
|
|
254
259
|
const trailing =
|
|
255
260
|
preferences.order === "account-first" ? [] : [accountText].filter(Boolean);
|
|
256
261
|
|
|
@@ -498,7 +503,7 @@ export function createUsageStatusController(accountManager: AccountManager) {
|
|
|
498
503
|
draft: FooterPreferences,
|
|
499
504
|
): string {
|
|
500
505
|
const previewText =
|
|
501
|
-
getStatusText(ctx, draft) ?? ctx
|
|
506
|
+
getStatusText(ctx, draft) ?? `${formatBrand(ctx)} ${formatLoading(ctx)}`;
|
|
502
507
|
return `${theme.fg("dim", "Preview")}: ${previewText}`;
|
|
503
508
|
}
|
|
504
509
|
|