compressed-context 0.1.0 → 0.1.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/package.json +64 -64
- package/src/compression/active-summary-cache.ts +50 -50
- package/src/compression/adopt.ts +57 -57
- package/src/compression/auto.ts +372 -372
- package/src/compression/babel.ts +85 -85
- package/src/compression/block.ts +25 -25
- package/src/compression/cache-history.ts +75 -75
- package/src/compression/compaction.ts +189 -189
- package/src/compression/decompress-tool.ts +74 -74
- package/src/compression/decompress.ts +292 -292
- package/src/compression/deleted-sessions.ts +24 -24
- package/src/compression/distill.ts +220 -220
- package/src/compression/events.ts +275 -275
- package/src/compression/markdown.ts +53 -53
- package/src/compression/normalize.ts +114 -114
- package/src/compression/render.ts +70 -70
- package/src/compression/schema.ts +84 -84
- package/src/compression/select.ts +123 -123
- package/src/compression/session-message.ts +112 -112
- package/src/compression/settings-rpc.ts +111 -111
- package/src/compression/settings.ts +175 -175
- package/src/compression/state.ts +248 -248
- package/src/compression/summarize.ts +595 -595
- package/src/compression/summary.ts +73 -73
- package/src/compression/tool.ts +142 -142
- package/src/compression/transcript-store.ts +258 -258
- package/src/compression/transcript.ts +63 -63
- package/src/compression/validate.ts +81 -81
- package/src/i18n/en.ts +93 -93
- package/src/i18n/index.ts +28 -28
- package/src/i18n/types.ts +89 -89
- package/src/i18n/zh.ts +93 -93
- package/src/index.ts +108 -108
- package/src/shared/blocks.ts +17 -17
- package/src/shared/cache.ts +199 -199
- package/src/shared/format.ts +23 -23
- package/src/shared/media.ts +184 -184
- package/src/shared/parse.ts +25 -25
- package/src/shared/parts.ts +72 -72
- package/src/shared/rpc.ts +145 -145
- package/src/shared/scan.ts +32 -32
- package/src/shared/settings.ts +471 -471
- package/src/shared/stats.ts +142 -142
- package/src/shared/tokens.ts +259 -259
- package/src/tui/chart/cache-chart.ts +237 -237
- package/src/tui/chart/color.ts +58 -58
- package/src/tui/index.tsx +111 -111
- package/src/tui/panel/cache.tsx +203 -203
- package/src/tui/panel/data.ts +105 -105
- package/src/tui/panel/index.tsx +176 -176
- package/src/tui/panel/summaries.tsx +192 -192
- package/src/tui/settings/dialogs.tsx +215 -215
- package/src/tui/settings/options.ts +238 -238
- package/src/tui/settings.tsx +418 -418
- package/src/tui/types.ts +4 -4
package/src/shared/rpc.ts
CHANGED
|
@@ -1,145 +1,145 @@
|
|
|
1
|
-
import { Rpc } from "@opencode/plugin/rpc"
|
|
2
|
-
|
|
3
|
-
import type { SummaryBlock } from "./blocks.ts"
|
|
4
|
-
import type { CacheSample } from "./cache.ts"
|
|
5
|
-
import type { CompressionSettings } from "./settings.ts"
|
|
6
|
-
|
|
7
|
-
const SettingsGetInputJson = {
|
|
8
|
-
type: "object",
|
|
9
|
-
properties: { sessionID: { type: "string" } },
|
|
10
|
-
additionalProperties: false,
|
|
11
|
-
} as const
|
|
12
|
-
|
|
13
|
-
const SettingsProperties = {
|
|
14
|
-
contextLimit: {
|
|
15
|
-
anyOf: [{ type: "string", enum: ["auto"] }, { type: "number" }],
|
|
16
|
-
},
|
|
17
|
-
triggerPercent: { type: "number" },
|
|
18
|
-
nudgeGrowthTokens: { type: "number" },
|
|
19
|
-
summaryBlockPercent: { type: "number" },
|
|
20
|
-
summaryTotalPercent: { type: "number" },
|
|
21
|
-
summaryLanguage: { type: "string", enum: ["auto", "zh", "en"] },
|
|
22
|
-
summaryFormat: { type: "string", enum: ["json", "markdown", "babel"] },
|
|
23
|
-
autoCompress: { type: "boolean" },
|
|
24
|
-
enableCompressTool: { type: "boolean" },
|
|
25
|
-
enableDecompressTool: { type: "boolean" },
|
|
26
|
-
showTimeline: { type: "boolean" },
|
|
27
|
-
showBlockTokens: { type: "boolean" },
|
|
28
|
-
decompressInlineMax: { type: "number" },
|
|
29
|
-
decompressTempCap: { type: "number" },
|
|
30
|
-
decompressShowTokens: { type: "boolean" },
|
|
31
|
-
} as const
|
|
32
|
-
|
|
33
|
-
const SettingsInputJson = {
|
|
34
|
-
type: "object",
|
|
35
|
-
properties: SettingsProperties,
|
|
36
|
-
required: ["contextLimit", "triggerPercent"],
|
|
37
|
-
additionalProperties: false,
|
|
38
|
-
} as const
|
|
39
|
-
|
|
40
|
-
/** 读取结果比写入多一个 resolvedWindow:auto 时从模型原生窗口解析出来的值。 */
|
|
41
|
-
const SettingsOutputJson = {
|
|
42
|
-
type: "object",
|
|
43
|
-
properties: {
|
|
44
|
-
...SettingsProperties,
|
|
45
|
-
resolvedWindow: { type: "number" },
|
|
46
|
-
},
|
|
47
|
-
required: ["contextLimit", "triggerPercent"],
|
|
48
|
-
additionalProperties: false,
|
|
49
|
-
} as const
|
|
50
|
-
|
|
51
|
-
const OkJson = {
|
|
52
|
-
type: "object",
|
|
53
|
-
properties: { ok: { type: "boolean" } },
|
|
54
|
-
required: ["ok"],
|
|
55
|
-
additionalProperties: false,
|
|
56
|
-
} as const
|
|
57
|
-
|
|
58
|
-
/** 需要明确指定会话的只读查询。 */
|
|
59
|
-
const SessionInputJson = {
|
|
60
|
-
type: "object",
|
|
61
|
-
properties: { sessionID: { type: "string" } },
|
|
62
|
-
required: ["sessionID"],
|
|
63
|
-
additionalProperties: false,
|
|
64
|
-
} as const
|
|
65
|
-
|
|
66
|
-
/** 摘要块:服务端返回 active 链,body 是渲染好的 markdown。 */
|
|
67
|
-
const BlockJson = {
|
|
68
|
-
type: "object",
|
|
69
|
-
properties: {
|
|
70
|
-
id: { type: "string" },
|
|
71
|
-
tier: { type: "number", enum: [1, 2, 3] },
|
|
72
|
-
format: { type: "string", enum: ["json", "markdown", "babel"] },
|
|
73
|
-
title: { type: "string" },
|
|
74
|
-
at: { type: "number" },
|
|
75
|
-
tokensBefore: { type: "number" },
|
|
76
|
-
tokensAfter: { type: "number" },
|
|
77
|
-
sourceBlockIds: { type: "array", items: { type: "string" } },
|
|
78
|
-
body: { type: "string" },
|
|
79
|
-
},
|
|
80
|
-
required: ["id", "tier", "format", "title", "at", "tokensBefore", "tokensAfter", "body"],
|
|
81
|
-
additionalProperties: false,
|
|
82
|
-
} as const
|
|
83
|
-
|
|
84
|
-
const BlocksOutputJson = {
|
|
85
|
-
type: "object",
|
|
86
|
-
properties: { blocks: { type: "array", items: BlockJson } },
|
|
87
|
-
required: ["blocks"],
|
|
88
|
-
additionalProperties: false,
|
|
89
|
-
} as const
|
|
90
|
-
|
|
91
|
-
/** 一条缓存命中样本。 */
|
|
92
|
-
const CacheSampleJson = {
|
|
93
|
-
type: "object",
|
|
94
|
-
properties: {
|
|
95
|
-
messageID: { type: "string" },
|
|
96
|
-
at: { type: "number" },
|
|
97
|
-
input: { type: "number" },
|
|
98
|
-
output: { type: "number" },
|
|
99
|
-
cached: { type: "number" },
|
|
100
|
-
hitRate: { type: "number" },
|
|
101
|
-
},
|
|
102
|
-
required: ["messageID", "at", "input", "output", "cached", "hitRate"],
|
|
103
|
-
additionalProperties: false,
|
|
104
|
-
} as const
|
|
105
|
-
|
|
106
|
-
const CacheHistoryOutputJson = {
|
|
107
|
-
type: "object",
|
|
108
|
-
properties: { samples: { type: "array", items: CacheSampleJson } },
|
|
109
|
-
required: ["samples"],
|
|
110
|
-
additionalProperties: false,
|
|
111
|
-
} as const
|
|
112
|
-
|
|
113
|
-
/** TUI 与服务端共用的设置 / 面板 RPC。 */
|
|
114
|
-
export const ContextRpc: Rpc.PortableDefinition = Rpc.define({
|
|
115
|
-
id: "compressed-context",
|
|
116
|
-
methods: {
|
|
117
|
-
settingsGet: { input: SettingsGetInputJson, output: SettingsOutputJson },
|
|
118
|
-
settingsSet: { input: SettingsInputJson, output: OkJson },
|
|
119
|
-
blocksGet: { input: SessionInputJson, output: BlocksOutputJson },
|
|
120
|
-
cacheHistoryGet: { input: SessionInputJson, output: CacheHistoryOutputJson },
|
|
121
|
-
},
|
|
122
|
-
events: {},
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
/** 服务端设置读取结果。 */
|
|
126
|
-
export interface ContextSettingsResult extends CompressionSettings {
|
|
127
|
-
readonly resolvedWindow?: number
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** TUI 侧对 ContextRpc 的类型化视图。 */
|
|
131
|
-
export interface ContextRpcClient {
|
|
132
|
-
settingsGet(input: { readonly sessionID?: string }): Promise<ContextSettingsResult>
|
|
133
|
-
settingsSet(input: CompressionSettings): Promise<{ readonly ok: boolean }>
|
|
134
|
-
blocksGet(input: { readonly sessionID: string }): Promise<{ readonly blocks: readonly SummaryBlock[] }>
|
|
135
|
-
cacheHistoryGet(input: {
|
|
136
|
-
readonly sessionID: string
|
|
137
|
-
}): Promise<{ readonly samples: readonly CacheSample[] }>
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/** 输入:带 rpc 方法的 TUI client;输出:类型化 RPC 客户端。 */
|
|
141
|
-
export function contextRpc(client: {
|
|
142
|
-
rpc(definition: Rpc.PortableDefinition): unknown
|
|
143
|
-
}): ContextRpcClient {
|
|
144
|
-
return client.rpc(ContextRpc) as ContextRpcClient
|
|
145
|
-
}
|
|
1
|
+
import { Rpc } from "@opencode/plugin/rpc"
|
|
2
|
+
|
|
3
|
+
import type { SummaryBlock } from "./blocks.ts"
|
|
4
|
+
import type { CacheSample } from "./cache.ts"
|
|
5
|
+
import type { CompressionSettings } from "./settings.ts"
|
|
6
|
+
|
|
7
|
+
const SettingsGetInputJson = {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: { sessionID: { type: "string" } },
|
|
10
|
+
additionalProperties: false,
|
|
11
|
+
} as const
|
|
12
|
+
|
|
13
|
+
const SettingsProperties = {
|
|
14
|
+
contextLimit: {
|
|
15
|
+
anyOf: [{ type: "string", enum: ["auto"] }, { type: "number" }],
|
|
16
|
+
},
|
|
17
|
+
triggerPercent: { type: "number" },
|
|
18
|
+
nudgeGrowthTokens: { type: "number" },
|
|
19
|
+
summaryBlockPercent: { type: "number" },
|
|
20
|
+
summaryTotalPercent: { type: "number" },
|
|
21
|
+
summaryLanguage: { type: "string", enum: ["auto", "zh", "en"] },
|
|
22
|
+
summaryFormat: { type: "string", enum: ["json", "markdown", "babel"] },
|
|
23
|
+
autoCompress: { type: "boolean" },
|
|
24
|
+
enableCompressTool: { type: "boolean" },
|
|
25
|
+
enableDecompressTool: { type: "boolean" },
|
|
26
|
+
showTimeline: { type: "boolean" },
|
|
27
|
+
showBlockTokens: { type: "boolean" },
|
|
28
|
+
decompressInlineMax: { type: "number" },
|
|
29
|
+
decompressTempCap: { type: "number" },
|
|
30
|
+
decompressShowTokens: { type: "boolean" },
|
|
31
|
+
} as const
|
|
32
|
+
|
|
33
|
+
const SettingsInputJson = {
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: SettingsProperties,
|
|
36
|
+
required: ["contextLimit", "triggerPercent"],
|
|
37
|
+
additionalProperties: false,
|
|
38
|
+
} as const
|
|
39
|
+
|
|
40
|
+
/** 读取结果比写入多一个 resolvedWindow:auto 时从模型原生窗口解析出来的值。 */
|
|
41
|
+
const SettingsOutputJson = {
|
|
42
|
+
type: "object",
|
|
43
|
+
properties: {
|
|
44
|
+
...SettingsProperties,
|
|
45
|
+
resolvedWindow: { type: "number" },
|
|
46
|
+
},
|
|
47
|
+
required: ["contextLimit", "triggerPercent"],
|
|
48
|
+
additionalProperties: false,
|
|
49
|
+
} as const
|
|
50
|
+
|
|
51
|
+
const OkJson = {
|
|
52
|
+
type: "object",
|
|
53
|
+
properties: { ok: { type: "boolean" } },
|
|
54
|
+
required: ["ok"],
|
|
55
|
+
additionalProperties: false,
|
|
56
|
+
} as const
|
|
57
|
+
|
|
58
|
+
/** 需要明确指定会话的只读查询。 */
|
|
59
|
+
const SessionInputJson = {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: { sessionID: { type: "string" } },
|
|
62
|
+
required: ["sessionID"],
|
|
63
|
+
additionalProperties: false,
|
|
64
|
+
} as const
|
|
65
|
+
|
|
66
|
+
/** 摘要块:服务端返回 active 链,body 是渲染好的 markdown。 */
|
|
67
|
+
const BlockJson = {
|
|
68
|
+
type: "object",
|
|
69
|
+
properties: {
|
|
70
|
+
id: { type: "string" },
|
|
71
|
+
tier: { type: "number", enum: [1, 2, 3] },
|
|
72
|
+
format: { type: "string", enum: ["json", "markdown", "babel"] },
|
|
73
|
+
title: { type: "string" },
|
|
74
|
+
at: { type: "number" },
|
|
75
|
+
tokensBefore: { type: "number" },
|
|
76
|
+
tokensAfter: { type: "number" },
|
|
77
|
+
sourceBlockIds: { type: "array", items: { type: "string" } },
|
|
78
|
+
body: { type: "string" },
|
|
79
|
+
},
|
|
80
|
+
required: ["id", "tier", "format", "title", "at", "tokensBefore", "tokensAfter", "body"],
|
|
81
|
+
additionalProperties: false,
|
|
82
|
+
} as const
|
|
83
|
+
|
|
84
|
+
const BlocksOutputJson = {
|
|
85
|
+
type: "object",
|
|
86
|
+
properties: { blocks: { type: "array", items: BlockJson } },
|
|
87
|
+
required: ["blocks"],
|
|
88
|
+
additionalProperties: false,
|
|
89
|
+
} as const
|
|
90
|
+
|
|
91
|
+
/** 一条缓存命中样本。 */
|
|
92
|
+
const CacheSampleJson = {
|
|
93
|
+
type: "object",
|
|
94
|
+
properties: {
|
|
95
|
+
messageID: { type: "string" },
|
|
96
|
+
at: { type: "number" },
|
|
97
|
+
input: { type: "number" },
|
|
98
|
+
output: { type: "number" },
|
|
99
|
+
cached: { type: "number" },
|
|
100
|
+
hitRate: { type: "number" },
|
|
101
|
+
},
|
|
102
|
+
required: ["messageID", "at", "input", "output", "cached", "hitRate"],
|
|
103
|
+
additionalProperties: false,
|
|
104
|
+
} as const
|
|
105
|
+
|
|
106
|
+
const CacheHistoryOutputJson = {
|
|
107
|
+
type: "object",
|
|
108
|
+
properties: { samples: { type: "array", items: CacheSampleJson } },
|
|
109
|
+
required: ["samples"],
|
|
110
|
+
additionalProperties: false,
|
|
111
|
+
} as const
|
|
112
|
+
|
|
113
|
+
/** TUI 与服务端共用的设置 / 面板 RPC。 */
|
|
114
|
+
export const ContextRpc: Rpc.PortableDefinition = Rpc.define({
|
|
115
|
+
id: "compressed-context",
|
|
116
|
+
methods: {
|
|
117
|
+
settingsGet: { input: SettingsGetInputJson, output: SettingsOutputJson },
|
|
118
|
+
settingsSet: { input: SettingsInputJson, output: OkJson },
|
|
119
|
+
blocksGet: { input: SessionInputJson, output: BlocksOutputJson },
|
|
120
|
+
cacheHistoryGet: { input: SessionInputJson, output: CacheHistoryOutputJson },
|
|
121
|
+
},
|
|
122
|
+
events: {},
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
/** 服务端设置读取结果。 */
|
|
126
|
+
export interface ContextSettingsResult extends CompressionSettings {
|
|
127
|
+
readonly resolvedWindow?: number
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** TUI 侧对 ContextRpc 的类型化视图。 */
|
|
131
|
+
export interface ContextRpcClient {
|
|
132
|
+
settingsGet(input: { readonly sessionID?: string }): Promise<ContextSettingsResult>
|
|
133
|
+
settingsSet(input: CompressionSettings): Promise<{ readonly ok: boolean }>
|
|
134
|
+
blocksGet(input: { readonly sessionID: string }): Promise<{ readonly blocks: readonly SummaryBlock[] }>
|
|
135
|
+
cacheHistoryGet(input: {
|
|
136
|
+
readonly sessionID: string
|
|
137
|
+
}): Promise<{ readonly samples: readonly CacheSample[] }>
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** 输入:带 rpc 方法的 TUI client;输出:类型化 RPC 客户端。 */
|
|
141
|
+
export function contextRpc(client: {
|
|
142
|
+
rpc(definition: Rpc.PortableDefinition): unknown
|
|
143
|
+
}): ContextRpcClient {
|
|
144
|
+
return client.rpc(ContextRpc) as ContextRpcClient
|
|
145
|
+
}
|
package/src/shared/scan.ts
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 输入:具备 scan 能力的 storage + 前缀 + 每条目的处理函数。
|
|
3
|
-
* 输出:无。
|
|
4
|
-
* 预期:按 next 游标分页遍历全部匹配条目,逐条串行 await,避免一次性把 key 全读进内存。
|
|
5
|
-
*/
|
|
6
|
-
export interface ScanStorageLike {
|
|
7
|
-
readonly scan?: (options: { prefix: string; limit: number; after?: string }) => Promise<{
|
|
8
|
-
entries: readonly { key: string; value: unknown }[]
|
|
9
|
-
next?: string
|
|
10
|
-
}>
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** 输入:storage + 前缀 + 访问函数 + 每页条数;输出:无;预期:storage 无 scan 时直接返回。 */
|
|
14
|
-
export async function forEachScannedEntry(
|
|
15
|
-
storage: ScanStorageLike,
|
|
16
|
-
prefix: string,
|
|
17
|
-
visit: (entry: { key: string; value: unknown }) => Promise<void>,
|
|
18
|
-
limit = 100,
|
|
19
|
-
): Promise<void> {
|
|
20
|
-
if (storage.scan === undefined) return
|
|
21
|
-
let after: string | undefined
|
|
22
|
-
for (;;) {
|
|
23
|
-
const page = await storage.scan({
|
|
24
|
-
prefix,
|
|
25
|
-
limit,
|
|
26
|
-
...(after === undefined ? {} : { after }),
|
|
27
|
-
})
|
|
28
|
-
for (const entry of page.entries) await visit(entry)
|
|
29
|
-
if (page.next === undefined) return
|
|
30
|
-
after = page.next
|
|
31
|
-
}
|
|
32
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 输入:具备 scan 能力的 storage + 前缀 + 每条目的处理函数。
|
|
3
|
+
* 输出:无。
|
|
4
|
+
* 预期:按 next 游标分页遍历全部匹配条目,逐条串行 await,避免一次性把 key 全读进内存。
|
|
5
|
+
*/
|
|
6
|
+
export interface ScanStorageLike {
|
|
7
|
+
readonly scan?: (options: { prefix: string; limit: number; after?: string }) => Promise<{
|
|
8
|
+
entries: readonly { key: string; value: unknown }[]
|
|
9
|
+
next?: string
|
|
10
|
+
}>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** 输入:storage + 前缀 + 访问函数 + 每页条数;输出:无;预期:storage 无 scan 时直接返回。 */
|
|
14
|
+
export async function forEachScannedEntry(
|
|
15
|
+
storage: ScanStorageLike,
|
|
16
|
+
prefix: string,
|
|
17
|
+
visit: (entry: { key: string; value: unknown }) => Promise<void>,
|
|
18
|
+
limit = 100,
|
|
19
|
+
): Promise<void> {
|
|
20
|
+
if (storage.scan === undefined) return
|
|
21
|
+
let after: string | undefined
|
|
22
|
+
for (;;) {
|
|
23
|
+
const page = await storage.scan({
|
|
24
|
+
prefix,
|
|
25
|
+
limit,
|
|
26
|
+
...(after === undefined ? {} : { after }),
|
|
27
|
+
})
|
|
28
|
+
for (const entry of page.entries) await visit(entry)
|
|
29
|
+
if (page.next === undefined) return
|
|
30
|
+
after = page.next
|
|
31
|
+
}
|
|
32
|
+
}
|