@royalcat/opencode-dcp-rc 4.0.0 → 4.0.2

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.
Files changed (55) hide show
  1. package/README.md +21 -1
  2. package/dist/index.js +1402 -27
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/commands/stats.d.ts +3 -1
  5. package/dist/lib/commands/stats.d.ts.map +1 -1
  6. package/dist/lib/compress/rc.d.ts.map +1 -1
  7. package/dist/lib/compress/summary.d.ts +11 -1
  8. package/dist/lib/compress/summary.d.ts.map +1 -1
  9. package/dist/lib/compress/types.d.ts +10 -1
  10. package/dist/lib/compress/types.d.ts.map +1 -1
  11. package/dist/lib/compress/usage.d.ts +45 -0
  12. package/dist/lib/compress/usage.d.ts.map +1 -0
  13. package/dist/lib/config.d.ts +2 -0
  14. package/dist/lib/config.d.ts.map +1 -1
  15. package/dist/lib/prompts/context-limit-nudge.d.ts +1 -1
  16. package/dist/lib/prompts/context-limit-nudge.d.ts.map +1 -1
  17. package/dist/lib/prompts/extensions/system.d.ts +2 -2
  18. package/dist/lib/prompts/extensions/system.d.ts.map +1 -1
  19. package/dist/lib/prompts/iteration-nudge.d.ts +1 -1
  20. package/dist/lib/prompts/iteration-nudge.d.ts.map +1 -1
  21. package/dist/lib/prompts/turn-nudge.d.ts +1 -1
  22. package/dist/lib/prompts/turn-nudge.d.ts.map +1 -1
  23. package/dist/lib/state/persistence.d.ts +2 -0
  24. package/dist/lib/state/persistence.d.ts.map +1 -1
  25. package/dist/lib/state/state.d.ts.map +1 -1
  26. package/dist/lib/state/types.d.ts +3 -0
  27. package/dist/lib/state/types.d.ts.map +1 -1
  28. package/dist/lib/tui/dialogs.d.ts.map +1 -1
  29. package/dist/lib/v2/index.d.ts.map +1 -1
  30. package/dist/lib/v2/rpc.d.ts +20 -0
  31. package/dist/lib/v2/rpc.d.ts.map +1 -1
  32. package/dist/lib/v2/scrub.d.ts +94 -0
  33. package/dist/lib/v2/scrub.d.ts.map +1 -0
  34. package/dist/lib/v2/usage.d.ts +45 -0
  35. package/dist/lib/v2/usage.d.ts.map +1 -0
  36. package/lib/commands/stats.ts +21 -0
  37. package/lib/compress/rc.ts +43 -7
  38. package/lib/compress/summary.ts +28 -1
  39. package/lib/compress/types.ts +14 -1
  40. package/lib/compress/usage.ts +168 -0
  41. package/lib/config.ts +30 -0
  42. package/lib/prompts/context-limit-nudge.ts +2 -0
  43. package/lib/prompts/extensions/system.ts +4 -0
  44. package/lib/prompts/iteration-nudge.ts +2 -0
  45. package/lib/prompts/system.ts +1 -1
  46. package/lib/prompts/turn-nudge.ts +2 -0
  47. package/lib/state/persistence.ts +18 -2
  48. package/lib/state/state.ts +4 -0
  49. package/lib/state/types.ts +3 -0
  50. package/lib/tui/dialogs.tsx +24 -0
  51. package/lib/v2/index.ts +58 -5
  52. package/lib/v2/rpc.ts +12 -0
  53. package/lib/v2/scrub.ts +954 -0
  54. package/lib/v2/usage.ts +450 -0
  55. package/package.json +1 -1
package/lib/v2/index.ts CHANGED
@@ -4,8 +4,14 @@ 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 { RC_SUMMARY_MARKER } from "../compress/summary"
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"
14
+ import { installResponseScrubHook, type OutputScrubConfig } from "./scrub"
9
15
  import { createCommandExecuteHandler, createSystemPromptHandler } from "../hooks"
10
16
  import {
11
17
  createSessionState,
@@ -56,6 +62,14 @@ export async function setup(ctx: Plugin.Context) {
56
62
  return
57
63
  }
58
64
  const logger = new Logger(config.debug)
65
+ const usageTracker = createCompressionUsageTracker()
66
+ const scrub: OutputScrubConfig = {
67
+ modelOutput: config.compress.scrubModelOutput,
68
+ messageIds: config.compress.scrubMessageIds,
69
+ }
70
+ await installCompressionUsageHook(ctx, usageTracker, logger, scrub)
71
+ await installHttpUsageHook(ctx, usageTracker, logger)
72
+ await installResponseScrubHook(ctx, scrub, logger)
59
73
  const prompts = new PromptStore(
60
74
  logger,
61
75
  ctx.location.directory,
@@ -251,7 +265,7 @@ export async function setup(ctx: Plugin.Context) {
251
265
  (part: any) =>
252
266
  part?.type === "text" &&
253
267
  typeof part.text === "string" &&
254
- part.text.includes(RC_SUMMARY_MARKER),
268
+ hasSummaryMarker(part.text),
255
269
  ),
256
270
  )
257
271
  if (own.length === 0) {
@@ -261,6 +275,32 @@ export async function setup(ctx: Plugin.Context) {
261
275
  for (const name of Object.keys(event.tools ?? {})) {
262
276
  delete (event.tools as Record<string, unknown>)[name]
263
277
  }
278
+
279
+ // Local fallback estimate of the request input, used only when the
280
+ // provider usage cannot be captured from the language model wrapper.
281
+ const callId = own
282
+ .flatMap((message) =>
283
+ Array.isArray(message.content) ? (message.content as any[]) : [],
284
+ )
285
+ .filter((part) => part?.type === "text" && typeof part.text === "string")
286
+ .map((part) => extractSummaryCallId(part.text))
287
+ .find((id): id is string => !!id)
288
+ if (callId) {
289
+ const texts: string[] = []
290
+ for (const part of event.system ?? []) {
291
+ if (typeof (part as any)?.text === "string") {
292
+ texts.push((part as any).text)
293
+ }
294
+ }
295
+ for (const message of own) {
296
+ for (const part of (message.content as any[]) ?? []) {
297
+ if (part?.type === "text" && typeof part.text === "string") {
298
+ texts.push(part.text)
299
+ }
300
+ }
301
+ }
302
+ usageTracker.recordEstimatedInput(callId, countTokens(texts.join("\n")))
303
+ }
264
304
  })
265
305
 
266
306
  if (config.compress.permission !== "deny") {
@@ -271,9 +311,22 @@ export async function setup(ctx: Plugin.Context) {
271
311
  logger,
272
312
  config,
273
313
  prompts,
274
- generate: async (input: { sessionID: string; prompt: string }) => {
275
- const result = await ctx.session.generate(input)
276
- return result?.text ?? ""
314
+ generate: async (input: { sessionID: string; prompt: string; callId?: string }) => {
315
+ try {
316
+ const result = await ctx.session.generate({
317
+ sessionID: input.sessionID,
318
+ prompt: input.prompt,
319
+ })
320
+ const text = result?.text ?? ""
321
+ const usage = input.callId
322
+ ? usageTracker.resolve(input.callId, text)
323
+ : undefined
324
+ return { text, usage }
325
+ } finally {
326
+ if (input.callId) {
327
+ usageTracker.discard(input.callId)
328
+ }
329
+ }
277
330
  },
278
331
  }
279
332
  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({