| 1 | package main |
| 2 | |
| 3 | import "reasonix/internal/control" |
| 4 | |
| 5 | // tabRuntimeSnapshot is a consistent under-a.mu copy of the per-tab fields |
| 6 | // that bound methods and rebuild paths need after releasing the lock. |
| 7 | type tabRuntimeSnapshot struct { |
| 8 | ctrl control.SessionAPI |
| 9 | sink *tabEventSink |
| 10 | label string |
| 11 | ready bool |
| 12 | readOnly bool |
| 13 | startupErr string |
| 14 | historicalSource *SessionSourceRef |
| 15 | scope string |
| 16 | workspaceRoot string |
| 17 | sessionPath string |
| 18 | sessionID string |
| 19 | sessionGeneration uint64 |
| 20 | topicID string |
| 21 | topicTitle string |
| 22 | sharedHostKey string |
| 23 | model string |
| 24 | effort *string |
| 25 | tokenMode, qualityFloor, mode string |
| 26 | goal, toolApprovalMode string |
| 27 | } |
| 28 | |
| 29 | // normalizedTabRuntime is the internal, orthogonal runtime profile restored |
| 30 | // across controller rebuilds. Goal sidecars remain authoritative; legacyGoal is |
| 31 | // only a fallback for a running legacy Goal with no sidecar. |
| 32 | type normalizedTabRuntime struct { |
| 33 | collaborationMode, toolApprovalMode, tokenMode string |
| 34 | qualityFloor, legacyGoal string |
| 35 | } |
| 36 | |
| 37 | // snapshotTabRuntimeLocked copies the racy per-tab fields. Callers must hold |
| 38 | // a.mu (read or write side); controller methods run only after unlocking. |
| 39 | func snapshotTabRuntimeLocked(tab *WorkspaceTab) tabRuntimeSnapshot { |
| 40 | if tab == nil { |
| 41 | return tabRuntimeSnapshot{} |
| 42 | } |
| 43 | return tabRuntimeSnapshot{ |
| 44 | ctrl: tab.Ctrl, sink: tab.sink, label: tab.Label, ready: tab.Ready, |
| 45 | readOnly: tab.ReadOnly, startupErr: tab.StartupErr, scope: tab.Scope, |
| 46 | historicalSource: tab.HistoricalSource, |
| 47 | workspaceRoot: tab.WorkspaceRoot, sessionPath: tab.SessionPath, sessionID: tab.SessionID, |
| 48 | sessionGeneration: tab.SessionGeneration, topicID: tab.TopicID, topicTitle: tab.TopicTitle, |
| 49 | sharedHostKey: tab.SharedHostKey, model: tab.model, effort: cloneStringPtr(tab.effort), |
| 50 | tokenMode: currentTabTokenMode(tab), qualityFloor: tab.qualityFloor, mode: tab.mode, |
| 51 | goal: tab.goal, toolApprovalMode: tab.toolApprovalMode, |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | func (a *App) tabRuntimeSnapshot(tab *WorkspaceTab) tabRuntimeSnapshot { |
| 56 | if tab == nil { |
| 57 | return tabRuntimeSnapshot{} |
| 58 | } |
| 59 | a.mu.RLock() |
| 60 | defer a.mu.RUnlock() |
| 61 | return snapshotTabRuntimeLocked(tab) |
| 62 | } |
| 63 |