handleProbe consumes an activation-time Probe result. Success stores the live context window and prints the activation line; failure surfaces the error inline and leaves the active profile set. Late probes for a no-longer-active profile still update liveContextSize, ready for when the user switches
(msg probeMsg)
| 56 | // context window and prints the activation line; failure surfaces the error |
| 57 | // inline and leaves the active profile set. Late probes for a no-longer-active |
| 58 | // profile still update liveContextSize, ready for when the user switches back. |
| 59 | // |
| 60 | // Connection-state mutations (m.connected, m.budget) are gated on the probe's |
| 61 | // client still being the live one (rebuildClient swaps the pointer on every |
| 62 | // re-activation, so identity is exact). A probe for profile a finishing after |
| 63 | // a /models switch to b, or a hung probe finishing after a SAME-profile |
| 64 | // re-activation already succeeded, can't flip the fresh state on a stale |
| 65 | // outcome. The liveContextSize cache write below stays keyed on the profile |
| 66 | // name alone: seeding an inactive profile's window is deliberately allowed. |
| 67 | func (m Model) handleProbe(msg probeMsg) (tea.Model, tea.Cmd) { |
| 68 | active := msg.cli == m.cli && msg.profile == m.cfg.Active |
| 69 | if msg.err != nil { |
| 70 | if active { |
| 71 | m.connected = false |
| 72 | // A 402 carries the depleted budget snapshot (Set=true, Remaining=0). |
| 73 | // Paint it now; otherwise the status bar shows no "% pass" segment |
| 74 | // until the first chat call also 402s. |
| 75 | if msg.budget.Set { |
| 76 | m.budget = msg.budget |
| 77 | } |
| 78 | } |
| 79 | // Silent startup probes print no banner either way; an offline launch |
| 80 | // shouldn't greet the user with "⚠ probe". connected=false suffices; |
| 81 | // the next user action surfaces the real failure. Stale probes stay |
| 82 | // quiet too: a superseded failure landing after the fresh probe's |
| 83 | // "✓ active" would read as the backend dying right after coming up. |
| 84 | if !msg.silent && active { |
| 85 | m.appendLine(styleError.Render("⚠ probe " + msg.profile + ": " + probeErrorMessage(msg.err))) |
| 86 | } |
| 87 | return m, nil |
| 88 | } |
| 89 | if active { |
| 90 | m.connected = true |
| 91 | } |
| 92 | if msg.budget.Set && active { |
| 93 | m.budget = msg.budget |
| 94 | } |
| 95 | p, ok := m.cfg.Models[msg.profile] |
| 96 | if !ok { |
| 97 | // Profile vanished between dispatch and return (hand-edited config or |
| 98 | // pruned by /models). Skip the cache write: an orphan key would |
| 99 | // accumulate across a long session. |
| 100 | return m, nil |
| 101 | } |
| 102 | if msg.contextWindow > 0 { |
| 103 | m.liveContextSize[msg.profile] = msg.contextWindow |
| 104 | } |
| 105 | // Don't print "✓ active: <profile>" for a stale probe whose profile is no |
| 106 | // longer active. (liveContextSize is set above.) |
| 107 | if msg.silent || !active { |
| 108 | return m, nil |
| 109 | } |