| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "time" |
| 5 | |
| 6 | "reasonix/internal/agent" |
| 7 | "reasonix/internal/billing" |
| 8 | ) |
| 9 | |
| 10 | // ContextInfo is the prompt-vs-window gauge payload plus session totals. Used |
| 11 | // and Window both zero means no context-window data yet. |
| 12 | type ContextInfo struct { |
| 13 | Used int `json:"used"` |
| 14 | Window int `json:"window"` |
| 15 | SessionTokens int `json:"sessionTokens"` |
| 16 | CompactRatio float64 `json:"compactRatio,omitempty"` |
| 17 | SessionCost float64 `json:"sessionCost,omitempty"` |
| 18 | SessionCurrency string `json:"sessionCurrency,omitempty"` |
| 19 | CacheHitTokens int `json:"cacheHitTokens,omitempty"` |
| 20 | CacheMissTokens int `json:"cacheMissTokens,omitempty"` |
| 21 | Estimated bool `json:"estimated,omitempty"` |
| 22 | SessionCostComplete bool `json:"sessionCostComplete,omitempty"` |
| 23 | SessionCostQuote *billing.CostQuote `json:"sessionCostQuote,omitempty"` |
| 24 | Sources map[string]usageSourceStats `json:"sources,omitempty"` |
| 25 | Maintenance *ContextMaintenanceInfo `json:"maintenance,omitempty"` |
| 26 | ContextBudget *ContextBudgetInfo `json:"contextBudget,omitempty"` |
| 27 | } |
| 28 | |
| 29 | // ContextMaintenanceInfo is the bridge-safe current-view snapshot. Optional |
| 30 | // fields preserve compatibility with older desktop/front-end combinations. |
| 31 | type ContextMaintenanceInfo struct { |
| 32 | CanonicalTokens int `json:"canonicalTokens,omitempty"` |
| 33 | ProjectedTokens int `json:"projectedTokens,omitempty"` |
| 34 | SummaryTokens int `json:"summaryTokens,omitempty"` |
| 35 | LastSavedTokens int `json:"lastSavedTokens,omitempty"` |
| 36 | SnipTrigger int `json:"snipTrigger,omitempty"` // always 0; legacy compatibility |
| 37 | FoldTrigger int `json:"foldTrigger,omitempty"` // alias of TriggerTokens |
| 38 | ForceTrigger int `json:"forceTrigger,omitempty"` // always 0; legacy compatibility |
| 39 | TriggerTokens int `json:"triggerTokens,omitempty"` |
| 40 | CheckpointState string `json:"checkpointState,omitempty"` // none|restored|applied |
| 41 | HardInputCeiling int `json:"hardInputCeiling,omitempty"` |
| 42 | Headroom int `json:"headroom,omitempty"` |
| 43 | ProjectionVersion uint64 `json:"projectionVersion,omitempty"` |
| 44 | Blocked bool `json:"blocked,omitempty"` |
| 45 | LastReceipt *ContextMaintenanceReceiptInfo `json:"lastReceipt,omitempty"` |
| 46 | ContextBudget *ContextBudgetInfo `json:"contextBudget,omitempty"` |
| 47 | } |
| 48 | |
| 49 | // ContextBudgetInfo is the optional send-time admission view. Older desktop |
| 50 | // builds ignore unknown fields; omission is the compatibility default. |
| 51 | type ContextBudgetInfo struct { |
| 52 | WindowMode string `json:"windowMode,omitempty"` |
| 53 | LimitMode string `json:"limitMode,omitempty"` |
| 54 | Source string `json:"source,omitempty"` |
| 55 | WindowTokens int `json:"windowTokens,omitempty"` |
| 56 | PromptTokens int `json:"promptTokens,omitempty"` |
| 57 | AutoOutputTokens int `json:"autoOutputTokens,omitempty"` |
| 58 | MaxOutputTokens int `json:"maxOutputTokens,omitempty"` |
| 59 | RequestedOutputTokens int `json:"requestedOutputTokens,omitempty"` |
| 60 | EffectiveOutputTokens int `json:"effectiveOutputTokens,omitempty"` |
| 61 | ReserveTokens int `json:"reserveTokens,omitempty"` |
| 62 | PhysicalRemaining int `json:"physicalRemaining,omitempty"` |
| 63 | Clipped bool `json:"clipped,omitempty"` |
| 64 | LastRecovery string `json:"lastRecovery,omitempty"` |
| 65 | ObservedWindow int `json:"observedWindow,omitempty"` |
| 66 | ObservedPrompt int `json:"observedPrompt,omitempty"` |
| 67 | ObservedCompletion int `json:"observedCompletion,omitempty"` |
| 68 | } |
| 69 | |
| 70 | type ContextMaintenanceReceiptInfo struct { |
| 71 | OperationID string `json:"operationId,omitempty"` |
| 72 | Status string `json:"status,omitempty"` |
| 73 | Action string `json:"action,omitempty"` |
| 74 | Trigger string `json:"trigger,omitempty"` |
| 75 | SourceProjection uint64 `json:"sourceProjection,omitempty"` |
| 76 | ProjectionVersion uint64 `json:"projectionVersion,omitempty"` |
| 77 | CoveredCount int `json:"coveredCount,omitempty"` |
| 78 | CoveredPrefixHash string `json:"coveredPrefixHash,omitempty"` |
| 79 | InputHash string `json:"inputHash,omitempty"` |
| 80 | OutputHash string `json:"outputHash,omitempty"` |
| 81 | InputTokens int `json:"inputTokens,omitempty"` |
| 82 | ResultTokens int `json:"resultTokens,omitempty"` |
| 83 | SavedTokens int `json:"savedTokens,omitempty"` |
| 84 | AffectedToolResults int `json:"affectedToolResults,omitempty"` |
| 85 | SummaryHash string `json:"summaryHash,omitempty"` |
| 86 | Archive string `json:"archive,omitempty"` |
| 87 | CacheBreak bool `json:"cacheBreak,omitempty"` |
| 88 | Reason string `json:"reason,omitempty"` |
| 89 | BlockedInputHash string `json:"blockedInputHash,omitempty"` |
| 90 | CreatedAt string `json:"createdAt,omitempty"` |
| 91 | } |
| 92 | |
| 93 | func contextMaintenanceInfo(snapshot agent.ContextMaintenanceSnapshot) *ContextMaintenanceInfo { |
| 94 | if snapshot.CanonicalTokens == 0 && snapshot.ProjectedTokens == 0 && snapshot.LastReceipt == nil { |
| 95 | return nil |
| 96 | } |
| 97 | info := &ContextMaintenanceInfo{ |
| 98 | CanonicalTokens: snapshot.CanonicalTokens, ProjectedTokens: snapshot.ProjectedTokens, |
| 99 | SummaryTokens: snapshot.SummaryTokens, LastSavedTokens: snapshot.LastSavedTokens, |
| 100 | // Snip/Force remain zero for one-version compatibility with older frontends. |
| 101 | FoldTrigger: snapshot.TriggerTokens, TriggerTokens: snapshot.TriggerTokens, |
| 102 | CheckpointState: snapshot.CheckpointState, HardInputCeiling: snapshot.HardInputCeiling, |
| 103 | Headroom: snapshot.Headroom, ProjectionVersion: snapshot.ProjectionVersion, |
| 104 | Blocked: snapshot.Blocked, |
| 105 | } |
| 106 | if snapshot.ContextBudget != nil { |
| 107 | info.ContextBudget = contextBudgetInfo(snapshot.ContextBudget) |
| 108 | } |
| 109 | if source := snapshot.LastReceipt; source != nil { |
| 110 | info.LastReceipt = contextMaintenanceReceiptInfo(source) |
| 111 | } |
| 112 | return info |
| 113 | } |
| 114 | |
| 115 | func contextBudgetInfo(source *agent.ContextBudgetSnapshot) *ContextBudgetInfo { |
| 116 | if source == nil { |
| 117 | return nil |
| 118 | } |
| 119 | return &ContextBudgetInfo{ |
| 120 | WindowMode: source.WindowMode, LimitMode: source.LimitMode, Source: source.Source, |
| 121 | WindowTokens: source.WindowTokens, PromptTokens: source.PromptTokens, |
| 122 | AutoOutputTokens: source.AutoOutputTokens, MaxOutputTokens: source.MaxOutputTokens, |
| 123 | RequestedOutputTokens: source.RequestedOutputTokens, EffectiveOutputTokens: source.EffectiveOutputTokens, |
| 124 | ReserveTokens: source.ReserveTokens, PhysicalRemaining: source.PhysicalRemaining, |
| 125 | Clipped: source.Clipped, LastRecovery: source.LastRecovery, |
| 126 | ObservedWindow: source.ObservedWindow, ObservedPrompt: source.ObservedPrompt, |
| 127 | ObservedCompletion: source.ObservedCompletion, |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | func contextMaintenanceReceiptInfo(source *agent.ContextMaintenanceReceipt) *ContextMaintenanceReceiptInfo { |
| 132 | receipt := &ContextMaintenanceReceiptInfo{ |
| 133 | OperationID: source.OperationID, Status: source.Status, Action: source.Action, Trigger: source.Trigger, |
| 134 | SourceProjection: source.SourceProjection, ProjectionVersion: source.ProjectionVersion, |
| 135 | CoveredCount: source.CoveredCount, CoveredPrefixHash: source.CoveredPrefixHash, |
| 136 | InputHash: source.InputHash, OutputHash: source.OutputHash, |
| 137 | InputTokens: source.InputTokens, ResultTokens: source.ResultTokens, SavedTokens: source.SavedTokens, |
| 138 | AffectedToolResults: source.AffectedToolResults, SummaryHash: source.SummaryHash, Archive: source.Archive, |
| 139 | CacheBreak: source.CacheBreak, Reason: source.Reason, BlockedInputHash: source.BlockedInputHash, |
| 140 | } |
| 141 | if !source.CreatedAt.IsZero() { |
| 142 | receipt.CreatedAt = source.CreatedAt.Format(time.RFC3339Nano) |
| 143 | } |
| 144 | return receipt |
| 145 | } |
| 146 |