()
| 487 | } |
| 488 | |
| 489 | const tick = async () => { |
| 490 | if (cancelled) return |
| 491 | const method = nextMethod |
| 492 | const instanceId = getFreebuffInstanceId() |
| 493 | const model = getSelectedFreebuffModel() |
| 494 | try { |
| 495 | const next = await callSession(method, token, { |
| 496 | signal: abortController.signal, |
| 497 | instanceId, |
| 498 | model, |
| 499 | }) |
| 500 | if (cancelled) return |
| 501 | // After any successful call, default back to GET polling. The |
| 502 | // takeover and model_locked branches below override this when they |
| 503 | // need another POST. |
| 504 | nextMethod = 'GET' |
| 505 | |
| 506 | // Race recovery: user picked a different model in the waiting room at |
| 507 | // the exact moment the server admitted them with the original model. |
| 508 | // Silently revert the local selection and re-tick so the next call |
| 509 | // (a GET) lands the actual active session. Users who really want to |
| 510 | // switch can /end-session deliberately. |
| 511 | if (next.status === 'model_locked') { |
| 512 | useFreebuffModelStore.getState().setSelectedModel(next.currentModel) |
| 513 | schedule(0) |
| 514 | return |
| 515 | } |
| 516 | if (next.status === 'model_unavailable') { |
| 517 | // Server says the requested model isn't available right now. Flip |
| 518 | // to the always-available fallback for this run. In-memory only — |
| 519 | // `setSelectedModel` doesn't persist, so the user's saved preference |
| 520 | // is preserved for their next launch. |
| 521 | useFreebuffModelStore |
| 522 | .getState() |
| 523 | .setSelectedModel(FALLBACK_FREEBUFF_MODEL_ID) |
| 524 | // The unavailable response came from a POST attempt. Re-POST with |
| 525 | // the fallback model; a GET would only redisplay the old ended row |
| 526 | // and leave the restart banner stuck in its pending state. |
| 527 | nextMethod = 'POST' |
| 528 | schedule(0) |
| 529 | return |
| 530 | } |
| 531 | |
| 532 | // Startup takeover: the initial probe GET saw we already hold a seat |
| 533 | // (from a prior CLI instance). Stop here and ask before POSTing to |
| 534 | // rotate our instance id; otherwise opening a second freebuff would |
| 535 | // immediately supersede the first one. |
| 536 | // `previousStatus === null` fences this to the very first tick only. |
| 537 | // Pin the selected model to whatever the server thinks we're on so |
| 538 | // an explicit takeover preserves our queue position instead of |
| 539 | // switching queues. |
| 540 | if ( |
| 541 | method === 'GET' && |
| 542 | previousStatus === null && |
| 543 | (next.status === 'queued' || next.status === 'active') |
| 544 | ) { |
| 545 | useFreebuffModelStore.getState().setSelectedModel(next.model) |
| 546 | // A fast restart after Ctrl+C can observe the old server row before |
no test coverage detected