@royalcat/opencode-dcp-rc 4.0.0 → 4.0.1
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/dist/index.js +539 -21
- package/dist/index.js.map +1 -1
- package/dist/lib/commands/stats.d.ts +3 -1
- package/dist/lib/commands/stats.d.ts.map +1 -1
- package/dist/lib/compress/rc.d.ts.map +1 -1
- package/dist/lib/compress/summary.d.ts +11 -1
- package/dist/lib/compress/summary.d.ts.map +1 -1
- package/dist/lib/compress/types.d.ts +10 -1
- package/dist/lib/compress/types.d.ts.map +1 -1
- package/dist/lib/compress/usage.d.ts +45 -0
- package/dist/lib/compress/usage.d.ts.map +1 -0
- package/dist/lib/state/persistence.d.ts +2 -0
- package/dist/lib/state/persistence.d.ts.map +1 -1
- package/dist/lib/state/state.d.ts.map +1 -1
- package/dist/lib/state/types.d.ts +3 -0
- package/dist/lib/state/types.d.ts.map +1 -1
- package/dist/lib/tui/dialogs.d.ts.map +1 -1
- package/dist/lib/v2/index.d.ts.map +1 -1
- package/dist/lib/v2/rpc.d.ts +20 -0
- package/dist/lib/v2/rpc.d.ts.map +1 -1
- package/dist/lib/v2/usage.d.ts +44 -0
- package/dist/lib/v2/usage.d.ts.map +1 -0
- package/lib/commands/stats.ts +21 -0
- package/lib/compress/rc.ts +43 -7
- package/lib/compress/summary.ts +28 -1
- package/lib/compress/types.ts +14 -1
- package/lib/compress/usage.ts +168 -0
- package/lib/state/persistence.ts +18 -2
- package/lib/state/state.ts +4 -0
- package/lib/state/types.ts +3 -0
- package/lib/tui/dialogs.tsx +24 -0
- package/lib/v2/index.ts +52 -5
- package/lib/v2/rpc.ts +12 -0
- package/lib/v2/usage.ts +423 -0
- package/package.json +1 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage accounting for the hidden compression (summary) request.
|
|
3
|
+
*
|
|
4
|
+
* Provider-reported usage is preferred: OpenCode V2 generates the summary
|
|
5
|
+
* through `session.generate`, which does not surface token usage to the
|
|
6
|
+
* caller, so the numbers are captured by wrapping the resolved language
|
|
7
|
+
* model (`ctx.aisdk.hook("language")`) and reading the `finish` usage.
|
|
8
|
+
* When that path is unavailable, local tokenizer estimates are recorded
|
|
9
|
+
* instead so the plugin can still report an approximate cost.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export type CompressionUsageSource = "provider" | "estimated"
|
|
13
|
+
|
|
14
|
+
/** Usage of a single hidden compression request. */
|
|
15
|
+
export interface CompressionUsage {
|
|
16
|
+
inputTokens: number
|
|
17
|
+
outputTokens: number
|
|
18
|
+
cacheReadTokens: number
|
|
19
|
+
cacheWriteTokens: number
|
|
20
|
+
reasoningTokens: number
|
|
21
|
+
source: CompressionUsageSource
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Aggregated usage of all hidden compression requests in a session. */
|
|
25
|
+
export interface CompressionUsageTotals {
|
|
26
|
+
calls: number
|
|
27
|
+
providerCalls: number
|
|
28
|
+
estimatedCalls: number
|
|
29
|
+
inputTokens: number
|
|
30
|
+
outputTokens: number
|
|
31
|
+
cacheReadTokens: number
|
|
32
|
+
cacheWriteTokens: number
|
|
33
|
+
reasoningTokens: number
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function emptyCompressionUsage(): CompressionUsageTotals {
|
|
37
|
+
return {
|
|
38
|
+
calls: 0,
|
|
39
|
+
providerCalls: 0,
|
|
40
|
+
estimatedCalls: 0,
|
|
41
|
+
inputTokens: 0,
|
|
42
|
+
outputTokens: 0,
|
|
43
|
+
cacheReadTokens: 0,
|
|
44
|
+
cacheWriteTokens: 0,
|
|
45
|
+
reasoningTokens: 0,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function toCount(value: unknown, fallback = 0): number {
|
|
50
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
51
|
+
return fallback
|
|
52
|
+
}
|
|
53
|
+
return Math.round(value)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Normalize AI SDK v3 usage (`{ inputTokens: { total, noCache, cacheRead,
|
|
58
|
+
* cacheWrite }, outputTokens: { total, text, reasoning } }`) into the plugin
|
|
59
|
+
* shape. Returns undefined when there is nothing usable to record.
|
|
60
|
+
*/
|
|
61
|
+
export function normalizeProviderUsage(raw: unknown): CompressionUsage | undefined {
|
|
62
|
+
if (!raw || typeof raw !== "object") {
|
|
63
|
+
return undefined
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const value = raw as { inputTokens?: unknown; outputTokens?: unknown }
|
|
67
|
+
const input =
|
|
68
|
+
value.inputTokens && typeof value.inputTokens === "object"
|
|
69
|
+
? (value.inputTokens as Record<string, unknown>)
|
|
70
|
+
: {}
|
|
71
|
+
const output =
|
|
72
|
+
value.outputTokens && typeof value.outputTokens === "object"
|
|
73
|
+
? (value.outputTokens as Record<string, unknown>)
|
|
74
|
+
: {}
|
|
75
|
+
|
|
76
|
+
const cacheRead = toCount(input.cacheRead)
|
|
77
|
+
const cacheWrite = toCount(input.cacheWrite)
|
|
78
|
+
const uncached = toCount(input.noCache)
|
|
79
|
+
const inputFromParts = uncached + cacheRead + cacheWrite
|
|
80
|
+
const inputTotal = Math.max(toCount(input.total, inputFromParts), inputFromParts)
|
|
81
|
+
|
|
82
|
+
const reasoning = toCount(output.reasoning)
|
|
83
|
+
const outputTotal = Math.max(toCount(output.total, toCount(output.text) + reasoning), reasoning)
|
|
84
|
+
|
|
85
|
+
if (
|
|
86
|
+
inputTotal === 0 &&
|
|
87
|
+
outputTotal === 0 &&
|
|
88
|
+
cacheRead === 0 &&
|
|
89
|
+
cacheWrite === 0 &&
|
|
90
|
+
reasoning === 0
|
|
91
|
+
) {
|
|
92
|
+
return undefined
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
inputTokens: inputTotal,
|
|
97
|
+
outputTokens: outputTotal,
|
|
98
|
+
cacheReadTokens: cacheRead,
|
|
99
|
+
cacheWriteTokens: cacheWrite,
|
|
100
|
+
reasoningTokens: reasoning,
|
|
101
|
+
source: "provider",
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Local fallback when the provider did not report usage. */
|
|
106
|
+
export function estimatedCompressionUsage(
|
|
107
|
+
inputTokens: number,
|
|
108
|
+
outputTokens: number,
|
|
109
|
+
): CompressionUsage {
|
|
110
|
+
return {
|
|
111
|
+
inputTokens: toCount(inputTokens),
|
|
112
|
+
outputTokens: toCount(outputTokens),
|
|
113
|
+
cacheReadTokens: 0,
|
|
114
|
+
cacheWriteTokens: 0,
|
|
115
|
+
reasoningTokens: 0,
|
|
116
|
+
source: "estimated",
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function addCompressionUsage(
|
|
121
|
+
totals: CompressionUsageTotals,
|
|
122
|
+
usage: CompressionUsage,
|
|
123
|
+
): CompressionUsageTotals {
|
|
124
|
+
return {
|
|
125
|
+
calls: totals.calls + 1,
|
|
126
|
+
providerCalls: totals.providerCalls + (usage.source === "provider" ? 1 : 0),
|
|
127
|
+
estimatedCalls: totals.estimatedCalls + (usage.source === "estimated" ? 1 : 0),
|
|
128
|
+
inputTokens: totals.inputTokens + usage.inputTokens,
|
|
129
|
+
outputTokens: totals.outputTokens + usage.outputTokens,
|
|
130
|
+
cacheReadTokens: totals.cacheReadTokens + usage.cacheReadTokens,
|
|
131
|
+
cacheWriteTokens: totals.cacheWriteTokens + usage.cacheWriteTokens,
|
|
132
|
+
reasoningTokens: totals.reasoningTokens + usage.reasoningTokens,
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function addCompressionUsageTotals(
|
|
137
|
+
totals: CompressionUsageTotals,
|
|
138
|
+
other: CompressionUsageTotals,
|
|
139
|
+
): CompressionUsageTotals {
|
|
140
|
+
return {
|
|
141
|
+
calls: totals.calls + other.calls,
|
|
142
|
+
providerCalls: totals.providerCalls + other.providerCalls,
|
|
143
|
+
estimatedCalls: totals.estimatedCalls + other.estimatedCalls,
|
|
144
|
+
inputTokens: totals.inputTokens + other.inputTokens,
|
|
145
|
+
outputTokens: totals.outputTokens + other.outputTokens,
|
|
146
|
+
cacheReadTokens: totals.cacheReadTokens + other.cacheReadTokens,
|
|
147
|
+
cacheWriteTokens: totals.cacheWriteTokens + other.cacheWriteTokens,
|
|
148
|
+
reasoningTokens: totals.reasoningTokens + other.reasoningTokens,
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Backward-compatible load of persisted totals. */
|
|
153
|
+
export function normalizeCompressionUsageTotals(value: unknown): CompressionUsageTotals {
|
|
154
|
+
if (!value || typeof value !== "object") {
|
|
155
|
+
return emptyCompressionUsage()
|
|
156
|
+
}
|
|
157
|
+
const raw = value as Record<string, unknown>
|
|
158
|
+
return {
|
|
159
|
+
calls: toCount(raw.calls),
|
|
160
|
+
providerCalls: toCount(raw.providerCalls),
|
|
161
|
+
estimatedCalls: toCount(raw.estimatedCalls),
|
|
162
|
+
inputTokens: toCount(raw.inputTokens),
|
|
163
|
+
outputTokens: toCount(raw.outputTokens),
|
|
164
|
+
cacheReadTokens: toCount(raw.cacheReadTokens),
|
|
165
|
+
cacheWriteTokens: toCount(raw.cacheWriteTokens),
|
|
166
|
+
reasoningTokens: toCount(raw.reasoningTokens),
|
|
167
|
+
}
|
|
168
|
+
}
|
package/lib/state/persistence.ts
CHANGED
|
@@ -10,6 +10,12 @@ import { homedir } from "os"
|
|
|
10
10
|
import { join } from "path"
|
|
11
11
|
import type { CompressionBlock, PrunedMessageEntry, SessionState, SessionStats } from "./types"
|
|
12
12
|
import type { Logger } from "../logger"
|
|
13
|
+
import {
|
|
14
|
+
addCompressionUsageTotals,
|
|
15
|
+
emptyCompressionUsage,
|
|
16
|
+
normalizeCompressionUsageTotals,
|
|
17
|
+
type CompressionUsageTotals,
|
|
18
|
+
} from "../compress/usage"
|
|
13
19
|
import { serializePruneMessagesState } from "./utils"
|
|
14
20
|
|
|
15
21
|
/** Prune state as stored on disk */
|
|
@@ -227,6 +233,7 @@ function emptyPersistedState(manualMode: boolean): PersistedSessionState {
|
|
|
227
233
|
stats: {
|
|
228
234
|
pruneTokenCounter: 0,
|
|
229
235
|
totalPruneTokens: 0,
|
|
236
|
+
compressionUsage: emptyCompressionUsage(),
|
|
230
237
|
},
|
|
231
238
|
lastUpdated: new Date().toISOString(),
|
|
232
239
|
}
|
|
@@ -257,6 +264,7 @@ export interface AggregatedStats {
|
|
|
257
264
|
totalTools: number
|
|
258
265
|
totalMessages: number
|
|
259
266
|
sessionCount: number
|
|
267
|
+
compressionUsage: CompressionUsageTotals
|
|
260
268
|
}
|
|
261
269
|
|
|
262
270
|
export async function loadAllSessionStats(logger: Logger): Promise<AggregatedStats> {
|
|
@@ -265,6 +273,7 @@ export async function loadAllSessionStats(logger: Logger): Promise<AggregatedSta
|
|
|
265
273
|
totalTools: 0,
|
|
266
274
|
totalMessages: 0,
|
|
267
275
|
sessionCount: 0,
|
|
276
|
+
compressionUsage: emptyCompressionUsage(),
|
|
268
277
|
}
|
|
269
278
|
|
|
270
279
|
try {
|
|
@@ -281,14 +290,21 @@ export async function loadAllSessionStats(logger: Logger): Promise<AggregatedSta
|
|
|
281
290
|
const content = await fs.readFile(filePath, "utf-8")
|
|
282
291
|
const state = JSON.parse(content) as PersistedSessionState
|
|
283
292
|
|
|
284
|
-
|
|
285
|
-
|
|
293
|
+
const usage = normalizeCompressionUsageTotals(state?.stats?.compressionUsage)
|
|
294
|
+
const hasUsage = usage.calls > 0
|
|
295
|
+
|
|
296
|
+
if (state?.prune && (state?.stats?.totalPruneTokens || hasUsage)) {
|
|
297
|
+
result.totalTokens += state.stats?.totalPruneTokens || 0
|
|
286
298
|
result.totalTools += state.prune.tools
|
|
287
299
|
? Object.keys(state.prune.tools).length
|
|
288
300
|
: 0
|
|
289
301
|
result.totalMessages += state.prune.messages?.byMessageId
|
|
290
302
|
? Object.keys(state.prune.messages.byMessageId).length
|
|
291
303
|
: 0
|
|
304
|
+
result.compressionUsage = addCompressionUsageTotals(
|
|
305
|
+
result.compressionUsage,
|
|
306
|
+
usage,
|
|
307
|
+
)
|
|
292
308
|
result.sessionCount++
|
|
293
309
|
}
|
|
294
310
|
} catch {
|
package/lib/state/state.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SessionState, ToolParameterEntry, WithParts } from "./types"
|
|
2
2
|
import type { Logger } from "../logger"
|
|
3
|
+
import { emptyCompressionUsage, normalizeCompressionUsageTotals } from "../compress/usage"
|
|
3
4
|
import { applyPendingCompressionDurations } from "../compress/timing"
|
|
4
5
|
import { loadManualModeSetting, loadSessionState, saveSessionState } from "./persistence"
|
|
5
6
|
import {
|
|
@@ -84,6 +85,7 @@ export function createSessionState(idFormat: IdFormat = "xml"): SessionState {
|
|
|
84
85
|
stats: {
|
|
85
86
|
pruneTokenCounter: 0,
|
|
86
87
|
totalPruneTokens: 0,
|
|
88
|
+
compressionUsage: emptyCompressionUsage(),
|
|
87
89
|
},
|
|
88
90
|
compressionTiming: {
|
|
89
91
|
startsByCallId: new Map<string, number>(),
|
|
@@ -122,6 +124,7 @@ export function resetSessionState(state: SessionState): void {
|
|
|
122
124
|
state.stats = {
|
|
123
125
|
pruneTokenCounter: 0,
|
|
124
126
|
totalPruneTokens: 0,
|
|
127
|
+
compressionUsage: emptyCompressionUsage(),
|
|
125
128
|
}
|
|
126
129
|
state.toolParameters.clear()
|
|
127
130
|
state.subAgentResultCache.clear()
|
|
@@ -186,6 +189,7 @@ export async function ensureSessionInitialized(
|
|
|
186
189
|
state.stats = {
|
|
187
190
|
pruneTokenCounter: persisted.stats?.pruneTokenCounter || 0,
|
|
188
191
|
totalPruneTokens: persisted.stats?.totalPruneTokens || 0,
|
|
192
|
+
compressionUsage: normalizeCompressionUsageTotals(persisted.stats?.compressionUsage),
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
const applied = applyPendingCompressionDurations(state)
|
package/lib/state/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CompressionTimingState } from "../compress/timing"
|
|
2
|
+
import type { CompressionUsageTotals } from "../compress/usage"
|
|
2
3
|
import type { IdFormat } from "../message-ids"
|
|
3
4
|
import { Message, Part } from "@opencode-ai/sdk/v2"
|
|
4
5
|
|
|
@@ -22,6 +23,8 @@ export interface ToolParameterEntry {
|
|
|
22
23
|
export interface SessionStats {
|
|
23
24
|
pruneTokenCounter: number
|
|
24
25
|
totalPruneTokens: number
|
|
26
|
+
/** Token usage of the hidden compression (summary) requests. */
|
|
27
|
+
compressionUsage: CompressionUsageTotals
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
export interface PrunedMessageEntry {
|
package/lib/tui/dialogs.tsx
CHANGED
|
@@ -121,6 +121,24 @@ export function StatsDialog(props: { api: ViewApi; report: StatsReport; onBack:
|
|
|
121
121
|
label="Messages pruned"
|
|
122
122
|
value={`${props.report.sessionMessages}`}
|
|
123
123
|
/>
|
|
124
|
+
<Metric
|
|
125
|
+
theme={theme}
|
|
126
|
+
label="Summary requests"
|
|
127
|
+
value={`${props.report.sessionCompressionUsage.calls}`}
|
|
128
|
+
hint={`${props.report.sessionCompressionUsage.providerCalls} provider · ${props.report.sessionCompressionUsage.estimatedCalls} estimated`}
|
|
129
|
+
/>
|
|
130
|
+
<Metric
|
|
131
|
+
theme={theme}
|
|
132
|
+
label="Request in|out"
|
|
133
|
+
value={`~${formatTokenCount(props.report.sessionCompressionUsage.inputTokens)} | ~${formatTokenCount(props.report.sessionCompressionUsage.outputTokens)}`}
|
|
134
|
+
hint="tokens"
|
|
135
|
+
/>
|
|
136
|
+
<Metric
|
|
137
|
+
theme={theme}
|
|
138
|
+
label="Request cache r|w"
|
|
139
|
+
value={`~${formatTokenCount(props.report.sessionCompressionUsage.cacheReadTokens)} | ~${formatTokenCount(props.report.sessionCompressionUsage.cacheWriteTokens)}`}
|
|
140
|
+
hint={`reasoning ~${formatTokenCount(props.report.sessionCompressionUsage.reasoningTokens)}`}
|
|
141
|
+
/>
|
|
124
142
|
</Card>
|
|
125
143
|
<Card theme={theme} title="All time">
|
|
126
144
|
<Metric
|
|
@@ -144,6 +162,12 @@ export function StatsDialog(props: { api: ViewApi; report: StatsReport; onBack:
|
|
|
144
162
|
label="Sessions with DCP history"
|
|
145
163
|
value={`${props.report.allTime.sessionCount}`}
|
|
146
164
|
/>
|
|
165
|
+
<Metric
|
|
166
|
+
theme={theme}
|
|
167
|
+
label="Summary requests"
|
|
168
|
+
value={`${props.report.allTime.compressionUsage.calls}`}
|
|
169
|
+
hint={`~${formatTokenCount(props.report.allTime.compressionUsage.inputTokens)} in · ~${formatTokenCount(props.report.allTime.compressionUsage.outputTokens)} out`}
|
|
170
|
+
/>
|
|
147
171
|
</Card>
|
|
148
172
|
</DcpFrame>
|
|
149
173
|
)
|
package/lib/v2/index.ts
CHANGED
|
@@ -4,8 +4,13 @@ import { getConfig } from "../config"
|
|
|
4
4
|
import { Logger } from "../logger"
|
|
5
5
|
import { PromptStore } from "../prompts/store"
|
|
6
6
|
import { createRcCompressTool } from "../compress"
|
|
7
|
-
import {
|
|
7
|
+
import { extractSummaryCallId, hasSummaryMarker } from "../compress/summary"
|
|
8
8
|
import { attachCompressionDuration } from "../compress/state"
|
|
9
|
+
import {
|
|
10
|
+
createCompressionUsageTracker,
|
|
11
|
+
installCompressionUsageHook,
|
|
12
|
+
installHttpUsageHook,
|
|
13
|
+
} from "./usage"
|
|
9
14
|
import { createCommandExecuteHandler, createSystemPromptHandler } from "../hooks"
|
|
10
15
|
import {
|
|
11
16
|
createSessionState,
|
|
@@ -56,6 +61,9 @@ export async function setup(ctx: Plugin.Context) {
|
|
|
56
61
|
return
|
|
57
62
|
}
|
|
58
63
|
const logger = new Logger(config.debug)
|
|
64
|
+
const usageTracker = createCompressionUsageTracker()
|
|
65
|
+
await installCompressionUsageHook(ctx, usageTracker, logger)
|
|
66
|
+
await installHttpUsageHook(ctx, usageTracker, logger)
|
|
59
67
|
const prompts = new PromptStore(
|
|
60
68
|
logger,
|
|
61
69
|
ctx.location.directory,
|
|
@@ -251,7 +259,7 @@ export async function setup(ctx: Plugin.Context) {
|
|
|
251
259
|
(part: any) =>
|
|
252
260
|
part?.type === "text" &&
|
|
253
261
|
typeof part.text === "string" &&
|
|
254
|
-
part.text
|
|
262
|
+
hasSummaryMarker(part.text),
|
|
255
263
|
),
|
|
256
264
|
)
|
|
257
265
|
if (own.length === 0) {
|
|
@@ -261,6 +269,32 @@ export async function setup(ctx: Plugin.Context) {
|
|
|
261
269
|
for (const name of Object.keys(event.tools ?? {})) {
|
|
262
270
|
delete (event.tools as Record<string, unknown>)[name]
|
|
263
271
|
}
|
|
272
|
+
|
|
273
|
+
// Local fallback estimate of the request input, used only when the
|
|
274
|
+
// provider usage cannot be captured from the language model wrapper.
|
|
275
|
+
const callId = own
|
|
276
|
+
.flatMap((message) =>
|
|
277
|
+
Array.isArray(message.content) ? (message.content as any[]) : [],
|
|
278
|
+
)
|
|
279
|
+
.filter((part) => part?.type === "text" && typeof part.text === "string")
|
|
280
|
+
.map((part) => extractSummaryCallId(part.text))
|
|
281
|
+
.find((id): id is string => !!id)
|
|
282
|
+
if (callId) {
|
|
283
|
+
const texts: string[] = []
|
|
284
|
+
for (const part of event.system ?? []) {
|
|
285
|
+
if (typeof (part as any)?.text === "string") {
|
|
286
|
+
texts.push((part as any).text)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
for (const message of own) {
|
|
290
|
+
for (const part of (message.content as any[]) ?? []) {
|
|
291
|
+
if (part?.type === "text" && typeof part.text === "string") {
|
|
292
|
+
texts.push(part.text)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
usageTracker.recordEstimatedInput(callId, countTokens(texts.join("\n")))
|
|
297
|
+
}
|
|
264
298
|
})
|
|
265
299
|
|
|
266
300
|
if (config.compress.permission !== "deny") {
|
|
@@ -271,9 +305,22 @@ export async function setup(ctx: Plugin.Context) {
|
|
|
271
305
|
logger,
|
|
272
306
|
config,
|
|
273
307
|
prompts,
|
|
274
|
-
generate: async (input: { sessionID: string; prompt: string }) => {
|
|
275
|
-
|
|
276
|
-
|
|
308
|
+
generate: async (input: { sessionID: string; prompt: string; callId?: string }) => {
|
|
309
|
+
try {
|
|
310
|
+
const result = await ctx.session.generate({
|
|
311
|
+
sessionID: input.sessionID,
|
|
312
|
+
prompt: input.prompt,
|
|
313
|
+
})
|
|
314
|
+
const text = result?.text ?? ""
|
|
315
|
+
const usage = input.callId
|
|
316
|
+
? usageTracker.resolve(input.callId, text)
|
|
317
|
+
: undefined
|
|
318
|
+
return { text, usage }
|
|
319
|
+
} finally {
|
|
320
|
+
if (input.callId) {
|
|
321
|
+
usageTracker.discard(input.callId)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
277
324
|
},
|
|
278
325
|
}
|
|
279
326
|
return createRcCompressTool(context)
|
package/lib/v2/rpc.ts
CHANGED
|
@@ -2,17 +2,29 @@ import { tool } from "@opencode-ai/plugin"
|
|
|
2
2
|
|
|
3
3
|
const z = tool.schema
|
|
4
4
|
const session = z.object({ sessionID: z.string() })
|
|
5
|
+
const compressionUsage = z.object({
|
|
6
|
+
calls: z.number(),
|
|
7
|
+
providerCalls: z.number(),
|
|
8
|
+
estimatedCalls: z.number(),
|
|
9
|
+
inputTokens: z.number(),
|
|
10
|
+
outputTokens: z.number(),
|
|
11
|
+
cacheReadTokens: z.number(),
|
|
12
|
+
cacheWriteTokens: z.number(),
|
|
13
|
+
reasoningTokens: z.number(),
|
|
14
|
+
})
|
|
5
15
|
const stats = z.object({
|
|
6
16
|
sessionTokens: z.number(),
|
|
7
17
|
sessionSummaryTokens: z.number(),
|
|
8
18
|
sessionDurationMs: z.number(),
|
|
9
19
|
sessionTools: z.number(),
|
|
10
20
|
sessionMessages: z.number(),
|
|
21
|
+
sessionCompressionUsage: compressionUsage,
|
|
11
22
|
allTime: z.object({
|
|
12
23
|
totalTokens: z.number(),
|
|
13
24
|
totalTools: z.number(),
|
|
14
25
|
totalMessages: z.number(),
|
|
15
26
|
sessionCount: z.number(),
|
|
27
|
+
compressionUsage,
|
|
16
28
|
}),
|
|
17
29
|
})
|
|
18
30
|
const context = z.object({
|