@azumag/opencode-rate-limit-fallback 1.0.16 → 1.0.18
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/index.js +25 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -316,35 +316,39 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
316
316
|
parts: parts,
|
|
317
317
|
model: { providerID: nextModel.providerID, modelID: nextModel.modelID },
|
|
318
318
|
};
|
|
319
|
-
// CRITICAL PATH:
|
|
319
|
+
// CRITICAL PATH: promptAsync BEFORE abort.
|
|
320
320
|
//
|
|
321
|
-
// In headless mode (opencode run),
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
//
|
|
321
|
+
// In headless mode (opencode run), abort → promptAsync fails because:
|
|
322
|
+
// 1. abort triggers server dispose sequence
|
|
323
|
+
// 2. promptAsync is accepted and message created
|
|
324
|
+
// 3. Server starts processing (busy) but dispose interrupts it (idle)
|
|
325
|
+
// 4. server.instance.disposed — all within ~6ms
|
|
325
326
|
//
|
|
326
|
-
//
|
|
327
|
-
//
|
|
327
|
+
// By sending promptAsync FIRST, the server knows there is pending work
|
|
328
|
+
// before abort triggers the dispose check. When abort cancels the retry
|
|
329
|
+
// loop and the session goes idle, the server should process the queued
|
|
330
|
+
// prompt instead of disposing.
|
|
328
331
|
//
|
|
329
|
-
//
|
|
330
|
-
//
|
|
332
|
+
// promptAsync: HTTP POST /session/{id}/prompt_async → 204 (SDK sdk.gen.js).
|
|
333
|
+
// prompt (sync): blocks until generation completes — do NOT use.
|
|
334
|
+
const t0 = Date.now();
|
|
335
|
+
await client.session.promptAsync({
|
|
336
|
+
path: { id: sessionID },
|
|
337
|
+
body: promptBody,
|
|
338
|
+
});
|
|
339
|
+
logToFile(`promptAsync completed for session ${sessionID} (${Date.now() - t0}ms) with model ${nextModel.providerID}/${nextModel.modelID}`);
|
|
340
|
+
const t1 = Date.now();
|
|
331
341
|
try {
|
|
332
342
|
await client.session.abort({ path: { id: sessionID } });
|
|
333
|
-
logToFile(`abort succeeded for session ${sessionID}`);
|
|
343
|
+
logToFile(`abort succeeded for session ${sessionID} (${Date.now() - t1}ms, total ${Date.now() - t0}ms)`);
|
|
334
344
|
}
|
|
335
345
|
catch (abortErr) {
|
|
336
|
-
|
|
337
|
-
// We still send promptAsync as best-effort: when the retry loop eventually
|
|
338
|
-
// completes (timeout or success), the queued prompt should be processed.
|
|
339
|
-
logToFile(`abort failed: ${abortErr} — sending promptAsync as best-effort`);
|
|
346
|
+
logToFile(`abort failed (${Date.now() - t1}ms): ${abortErr}`);
|
|
340
347
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
logToFile(`promptAsync sent for session ${sessionID} with model ${nextModel.providerID}/${nextModel.modelID}`);
|
|
346
|
-
// Toasts are fire-and-forget: placed AFTER the critical path so they cannot
|
|
347
|
-
// interfere with the abort→promptAsync timing, even if they fail in headless.
|
|
348
|
+
// Toast is best-effort notification. The toast() function (line ~185) has
|
|
349
|
+
// built-in fallback: showToast failure → app.log. After promptAsync the
|
|
350
|
+
// server may already be disposing, so both showToast and app.log could fail.
|
|
351
|
+
// The outer .catch() ensures even total toast() failure never blocks or throws.
|
|
348
352
|
toast("Fallback Active", `Now using ${nextModel.modelID}`, "success").catch(() => { });
|
|
349
353
|
retryState.delete(stateKey);
|
|
350
354
|
// Clear fallback flag to allow next fallback if needed
|
package/package.json
CHANGED