@serkanalgur/opencodev2-slim 2.0.12 → 2.0.13
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/README.md +7 -0
- package/package.json +1 -1
- package/src/tui.tsx +63 -7
package/README.md
CHANGED
|
@@ -141,6 +141,13 @@ Note: Compression is performed by the AI assistant using the `compress` tool. Th
|
|
|
141
141
|
|
|
142
142
|
## Changelog
|
|
143
143
|
|
|
144
|
+
### 2.0.13
|
|
145
|
+
|
|
146
|
+
- Fix `/panel` in the TUI not reflecting real context usage:
|
|
147
|
+
- The TUI command now reads live server measurements (`Session.Info.tokens` + `cost` + `model` + context window) via `context.client.session.get()` and prints them (measured tokens, %, cost, model) at the bottom of the panel, matching what the `panel` tool reports.
|
|
148
|
+
- Resolve the active session from `context.ui.router.current()` instead of a non-existent `context.router`, so the panel targets the focused session rather than always the first one.
|
|
149
|
+
- Call `context.data.session.message.sync()` before reading the transcript so stats aren't computed from an empty/stale cache.
|
|
150
|
+
|
|
144
151
|
### 2.0.12
|
|
145
152
|
|
|
146
153
|
- Bind panel/nudge to real OpenCode context measurements:
|
package/package.json
CHANGED
package/src/tui.tsx
CHANGED
|
@@ -94,7 +94,14 @@ export function deriveStats(messages: readonly unknown[]): PanelStats {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
// Builds a human-readable panel as plain text (injected into the message stream).
|
|
97
|
-
function renderPanelText(
|
|
97
|
+
function renderPanelText(
|
|
98
|
+
sessionID: string,
|
|
99
|
+
stats: PanelStats,
|
|
100
|
+
real?: MeasuredReal | null,
|
|
101
|
+
): string {
|
|
102
|
+
const limit = real?.contextLimit ?? 0
|
|
103
|
+
const pct = real?.usagePercent ?? 0
|
|
104
|
+
const status = real ? (pct >= 90 ? "critical" : pct >= 70 ? "warning" : "healthy") : "n/a"
|
|
98
105
|
const lines: string[] = []
|
|
99
106
|
lines.push("┌─────────────────────────────────────────────────────────────┐")
|
|
100
107
|
lines.push("│ SLIM CONTEXT PANEL │")
|
|
@@ -107,22 +114,67 @@ function renderPanelText(sessionID: string, stats: PanelStats): string {
|
|
|
107
114
|
lines.push(`│ Tool calls: ${stats.toolCalls} Compactions: ${stats.compactionCount}`)
|
|
108
115
|
lines.push(`│ Tokens (est): User ${stats.tokensByRole.user} | Assistant ${stats.tokensByRole.assistant} | System ${stats.tokensByRole.system}`)
|
|
109
116
|
lines.push(`│ Total token estimate: ${stats.totalTokens}`)
|
|
117
|
+
if (real) {
|
|
118
|
+
lines.push("├─────────────────────────────────────────────────────────────┤")
|
|
119
|
+
lines.push(`│ Measured tokens: ${real.tokens} (${pct}% of ${limit}) [${status}]`)
|
|
120
|
+
if (real.cost > 0) lines.push(`│ Cost: $${real.cost.toFixed(6)}`)
|
|
121
|
+
lines.push(`│ Model: ${real.model}`)
|
|
122
|
+
}
|
|
110
123
|
lines.push("└─────────────────────────────────────────────────────────────┘")
|
|
111
124
|
return lines.join("\n")
|
|
112
125
|
}
|
|
113
126
|
|
|
114
|
-
// Resolves the "current" session: the focused session if any, else the most recent.
|
|
127
|
+
// Resolves the "current" session: the router-focused session if any, else the most recent.
|
|
115
128
|
function resolveCurrentSession(context: any): string | null {
|
|
116
129
|
const sessions = context.data.session.list() || []
|
|
117
130
|
if (sessions.length === 0) return null
|
|
118
|
-
//
|
|
119
|
-
const
|
|
120
|
-
if (
|
|
121
|
-
return
|
|
131
|
+
// The TUI host exposes the active route via context.ui.router (not context.router).
|
|
132
|
+
const route = context.ui?.router?.current?.()
|
|
133
|
+
if (route && typeof route === "object" && "sessionID" in route) {
|
|
134
|
+
return route.sessionID as string
|
|
122
135
|
}
|
|
123
136
|
return sessions[0].id
|
|
124
137
|
}
|
|
125
138
|
|
|
139
|
+
// Server-measured context numbers for a session (Session.Info.tokens + cost + model),
|
|
140
|
+
// mirroring what the `panel` tool in index.ts reads via ctx.session.get().
|
|
141
|
+
interface MeasuredReal {
|
|
142
|
+
tokens: number
|
|
143
|
+
cost: number
|
|
144
|
+
contextLimit: number
|
|
145
|
+
model: string
|
|
146
|
+
usagePercent: number
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function measureSession(context: any, sessionID: string): Promise<MeasuredReal | null> {
|
|
150
|
+
try {
|
|
151
|
+
const info: any = await context.client.session.get({ sessionID })
|
|
152
|
+
if (!info) return null
|
|
153
|
+
const tokens: any = info.tokens ?? {}
|
|
154
|
+
const tokenCount =
|
|
155
|
+
(typeof tokens.input === "number" ? tokens.input : 0) +
|
|
156
|
+
(typeof tokens.output === "number" ? tokens.output : 0) +
|
|
157
|
+
(typeof tokens.reasoning === "number" ? tokens.reasoning : 0) +
|
|
158
|
+
(typeof tokens.cache?.read === "number" ? tokens.cache.read : 0) +
|
|
159
|
+
(typeof tokens.cache?.write === "number" ? tokens.cache.write : 0)
|
|
160
|
+
const contextLimit: number =
|
|
161
|
+
typeof info.model?.limit?.context === "number" && info.model.limit.context > 0
|
|
162
|
+
? info.model.limit.context
|
|
163
|
+
: 200000
|
|
164
|
+
const usagePercent =
|
|
165
|
+
contextLimit > 0 ? Math.min(100, Math.round((tokenCount / contextLimit) * 100)) : 0
|
|
166
|
+
return {
|
|
167
|
+
tokens: tokenCount,
|
|
168
|
+
cost: typeof info.cost === "number" ? info.cost : 0,
|
|
169
|
+
contextLimit,
|
|
170
|
+
model: info.model?.id || "unknown",
|
|
171
|
+
usagePercent,
|
|
172
|
+
}
|
|
173
|
+
} catch {
|
|
174
|
+
return null
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
126
178
|
export default Plugin.define({
|
|
127
179
|
id: "opencodev2-slim.cli",
|
|
128
180
|
setup(context) {
|
|
@@ -160,10 +212,14 @@ export default Plugin.define({
|
|
|
160
212
|
}
|
|
161
213
|
|
|
162
214
|
try {
|
|
215
|
+
// Make sure the cached transcript is loaded before reading it.
|
|
216
|
+
await context.data.session.message.sync(sessionID)
|
|
163
217
|
const messages =
|
|
164
218
|
context.data.session.message.list(sessionID) || []
|
|
219
|
+
// Prefer live server-measured context numbers when available.
|
|
220
|
+
const real = await measureSession(context, sessionID)
|
|
165
221
|
const stats = deriveStats(messages)
|
|
166
|
-
const text = renderPanelText(sessionID, stats)
|
|
222
|
+
const text = renderPanelText(sessionID, stats, real)
|
|
167
223
|
// Inject the panel as plain text into the session stream,
|
|
168
224
|
// so it doesn't take over OpenCode's own panel UI.
|
|
169
225
|
await context.client.session.synthetic({
|