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/stats.ts
CHANGED
|
@@ -1,142 +1,142 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 上下文统计的跨进程载体。
|
|
3
|
-
*
|
|
4
|
-
* 服务端插件把估算写进 session metadata,TUI 插件读出来按真实 usage 缩放。
|
|
5
|
-
* 本模块只放纯函数:构造、校验、节流判断、metadata 合并、缩放。
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { totalCategories, type TokenCategories } from "./tokens.ts"
|
|
9
|
-
|
|
10
|
-
/** metadata 里存放统计的键名,避免和其他插件撞车。 */
|
|
11
|
-
export const STATS_KEY = "compressed-context"
|
|
12
|
-
|
|
13
|
-
/** metadata 里存放终止原因的键名;TUI 面板据此显示警告。 */
|
|
14
|
-
export const TERMINAL_KEY = "compressed-context-terminal"
|
|
15
|
-
|
|
16
|
-
export const CATEGORY_KEYS = ["system", "tools", "tool", "text", "recent", "summaries", "media"] as const
|
|
17
|
-
|
|
18
|
-
export type CategoryKey = (typeof CATEGORY_KEYS)[number]
|
|
19
|
-
|
|
20
|
-
export interface ContextStats extends TokenCategories {
|
|
21
|
-
readonly estimatedTotal: number
|
|
22
|
-
readonly at: number
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** 两次写入之间至少要有这么多 token 的变化,避免 durable 事件被刷屏。 */
|
|
26
|
-
export const PUBLISH_MIN_DELTA = 2_000
|
|
27
|
-
|
|
28
|
-
/** 输入:分类估算 + 时间戳;输出:带合计的统计对象。 */
|
|
29
|
-
export function createStats(categories: TokenCategories, now: number): ContextStats {
|
|
30
|
-
return { ...categories, estimatedTotal: totalCategories(categories), at: now }
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** 输入:任意 metadata 值;输出:合法数字或 undefined。 */
|
|
34
|
-
function readNumber(value: unknown): number | undefined {
|
|
35
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 输入:metadata 里的原始 JSON。
|
|
40
|
-
* 输出:校验通过的统计对象;缺字段、类型不对或负数返回 undefined。
|
|
41
|
-
* 预期:metadata 可能被别的版本或别的插件改写,解析必须防御。
|
|
42
|
-
*/
|
|
43
|
-
export function parseStats(value: unknown): ContextStats | undefined {
|
|
44
|
-
if (typeof value !== "object" || value === null) return undefined
|
|
45
|
-
const record = value as Record<string, unknown>
|
|
46
|
-
const system = readNumber(record["system"])
|
|
47
|
-
const tools = readNumber(record["tools"])
|
|
48
|
-
const tool = readNumber(record["tool"])
|
|
49
|
-
const text = readNumber(record["text"])
|
|
50
|
-
// 旧版本没有 recent 字段;缺省按 0 处理,避免统计整块失效。
|
|
51
|
-
const recent = record["recent"] === undefined ? 0 : readNumber(record["recent"])
|
|
52
|
-
const summaries = readNumber(record["summaries"])
|
|
53
|
-
const media = readNumber(record["media"])
|
|
54
|
-
const estimatedTotal = readNumber(record["estimatedTotal"])
|
|
55
|
-
const at = readNumber(record["at"])
|
|
56
|
-
if (
|
|
57
|
-
system === undefined ||
|
|
58
|
-
tools === undefined ||
|
|
59
|
-
tool === undefined ||
|
|
60
|
-
text === undefined ||
|
|
61
|
-
recent === undefined ||
|
|
62
|
-
summaries === undefined ||
|
|
63
|
-
media === undefined ||
|
|
64
|
-
estimatedTotal === undefined ||
|
|
65
|
-
at === undefined
|
|
66
|
-
) {
|
|
67
|
-
return undefined
|
|
68
|
-
}
|
|
69
|
-
return { system, tools, tool, text, recent, summaries, media, estimatedTotal, at }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* 输入:上一次发布的统计(可能没有)+ 本次统计。
|
|
74
|
-
* 输出:是否值得写一次 metadata。
|
|
75
|
-
* 预期:合计或任一分类变化达到阈值才写,减少 durable 事件。
|
|
76
|
-
*/
|
|
77
|
-
export function shouldPublish(previous: ContextStats | undefined, next: ContextStats): boolean {
|
|
78
|
-
if (previous === undefined) return true
|
|
79
|
-
if (Math.abs(next.estimatedTotal - previous.estimatedTotal) >= PUBLISH_MIN_DELTA) return true
|
|
80
|
-
return CATEGORY_KEYS.some((key) => Math.abs(next[key] - previous[key]) >= PUBLISH_MIN_DELTA)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 输入:已有 metadata + 本次统计。
|
|
87
|
-
* 输出:保留其他键、替换 STATS_KEY 的新对象。
|
|
88
|
-
* 预期:metadata 是整体替换语义,必须先合并再写。
|
|
89
|
-
*/
|
|
90
|
-
export function mergeMetadata(
|
|
91
|
-
metadata: Readonly<Record<string, JsonValue>> | undefined,
|
|
92
|
-
stats: ContextStats,
|
|
93
|
-
): Record<string, JsonValue> {
|
|
94
|
-
return {
|
|
95
|
-
...(metadata ?? {}),
|
|
96
|
-
[STATS_KEY]: {
|
|
97
|
-
system: stats.system,
|
|
98
|
-
tools: stats.tools,
|
|
99
|
-
tool: stats.tool,
|
|
100
|
-
text: stats.text,
|
|
101
|
-
recent: stats.recent,
|
|
102
|
-
summaries: stats.summaries,
|
|
103
|
-
media: stats.media,
|
|
104
|
-
estimatedTotal: stats.estimatedTotal,
|
|
105
|
-
at: stats.at,
|
|
106
|
-
},
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* 输入:已有 metadata + 终止原因。
|
|
112
|
-
* 输出:保留其他键、写入终止原因的新对象。
|
|
113
|
-
* 预期:TUI 面板读 TERMINAL_KEY 显示警告;写入失败不影响压缩状态。
|
|
114
|
-
*/
|
|
115
|
-
export function mergeTerminalMetadata(
|
|
116
|
-
metadata: Readonly<Record<string, JsonValue>> | undefined,
|
|
117
|
-
message: string,
|
|
118
|
-
): Record<string, JsonValue> {
|
|
119
|
-
return { ...(metadata ?? {}), [TERMINAL_KEY]: message }
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export interface ScaledCategory {
|
|
123
|
-
readonly key: CategoryKey
|
|
124
|
-
readonly value: number
|
|
125
|
-
readonly pct: number
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* 输入:估算统计 + 真实总量(0 表示上游没给)。
|
|
130
|
-
* 输出:按真实总量缩放后的各类数值与百分比。
|
|
131
|
-
* 预期:真实总量缺失时按原值展示;百分比分母是缩放后的合计。
|
|
132
|
-
*/
|
|
133
|
-
export function scaleCategories(stats: ContextStats, realTotal: number): readonly ScaledCategory[] {
|
|
134
|
-
const scale = realTotal > 0 && stats.estimatedTotal > 0 ? realTotal / stats.estimatedTotal : 1
|
|
135
|
-
const scaled = CATEGORY_KEYS.map((key) => ({ key, value: Math.round(stats[key] * scale) }))
|
|
136
|
-
const total = scaled.reduce((sum, item) => sum + item.value, 0)
|
|
137
|
-
return scaled.map((item) => ({
|
|
138
|
-
key: item.key,
|
|
139
|
-
value: item.value,
|
|
140
|
-
pct: total > 0 ? Math.round((item.value / total) * 100) : 0,
|
|
141
|
-
}))
|
|
142
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 上下文统计的跨进程载体。
|
|
3
|
+
*
|
|
4
|
+
* 服务端插件把估算写进 session metadata,TUI 插件读出来按真实 usage 缩放。
|
|
5
|
+
* 本模块只放纯函数:构造、校验、节流判断、metadata 合并、缩放。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { totalCategories, type TokenCategories } from "./tokens.ts"
|
|
9
|
+
|
|
10
|
+
/** metadata 里存放统计的键名,避免和其他插件撞车。 */
|
|
11
|
+
export const STATS_KEY = "compressed-context"
|
|
12
|
+
|
|
13
|
+
/** metadata 里存放终止原因的键名;TUI 面板据此显示警告。 */
|
|
14
|
+
export const TERMINAL_KEY = "compressed-context-terminal"
|
|
15
|
+
|
|
16
|
+
export const CATEGORY_KEYS = ["system", "tools", "tool", "text", "recent", "summaries", "media"] as const
|
|
17
|
+
|
|
18
|
+
export type CategoryKey = (typeof CATEGORY_KEYS)[number]
|
|
19
|
+
|
|
20
|
+
export interface ContextStats extends TokenCategories {
|
|
21
|
+
readonly estimatedTotal: number
|
|
22
|
+
readonly at: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** 两次写入之间至少要有这么多 token 的变化,避免 durable 事件被刷屏。 */
|
|
26
|
+
export const PUBLISH_MIN_DELTA = 2_000
|
|
27
|
+
|
|
28
|
+
/** 输入:分类估算 + 时间戳;输出:带合计的统计对象。 */
|
|
29
|
+
export function createStats(categories: TokenCategories, now: number): ContextStats {
|
|
30
|
+
return { ...categories, estimatedTotal: totalCategories(categories), at: now }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** 输入:任意 metadata 值;输出:合法数字或 undefined。 */
|
|
34
|
+
function readNumber(value: unknown): number | undefined {
|
|
35
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 输入:metadata 里的原始 JSON。
|
|
40
|
+
* 输出:校验通过的统计对象;缺字段、类型不对或负数返回 undefined。
|
|
41
|
+
* 预期:metadata 可能被别的版本或别的插件改写,解析必须防御。
|
|
42
|
+
*/
|
|
43
|
+
export function parseStats(value: unknown): ContextStats | undefined {
|
|
44
|
+
if (typeof value !== "object" || value === null) return undefined
|
|
45
|
+
const record = value as Record<string, unknown>
|
|
46
|
+
const system = readNumber(record["system"])
|
|
47
|
+
const tools = readNumber(record["tools"])
|
|
48
|
+
const tool = readNumber(record["tool"])
|
|
49
|
+
const text = readNumber(record["text"])
|
|
50
|
+
// 旧版本没有 recent 字段;缺省按 0 处理,避免统计整块失效。
|
|
51
|
+
const recent = record["recent"] === undefined ? 0 : readNumber(record["recent"])
|
|
52
|
+
const summaries = readNumber(record["summaries"])
|
|
53
|
+
const media = readNumber(record["media"])
|
|
54
|
+
const estimatedTotal = readNumber(record["estimatedTotal"])
|
|
55
|
+
const at = readNumber(record["at"])
|
|
56
|
+
if (
|
|
57
|
+
system === undefined ||
|
|
58
|
+
tools === undefined ||
|
|
59
|
+
tool === undefined ||
|
|
60
|
+
text === undefined ||
|
|
61
|
+
recent === undefined ||
|
|
62
|
+
summaries === undefined ||
|
|
63
|
+
media === undefined ||
|
|
64
|
+
estimatedTotal === undefined ||
|
|
65
|
+
at === undefined
|
|
66
|
+
) {
|
|
67
|
+
return undefined
|
|
68
|
+
}
|
|
69
|
+
return { system, tools, tool, text, recent, summaries, media, estimatedTotal, at }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 输入:上一次发布的统计(可能没有)+ 本次统计。
|
|
74
|
+
* 输出:是否值得写一次 metadata。
|
|
75
|
+
* 预期:合计或任一分类变化达到阈值才写,减少 durable 事件。
|
|
76
|
+
*/
|
|
77
|
+
export function shouldPublish(previous: ContextStats | undefined, next: ContextStats): boolean {
|
|
78
|
+
if (previous === undefined) return true
|
|
79
|
+
if (Math.abs(next.estimatedTotal - previous.estimatedTotal) >= PUBLISH_MIN_DELTA) return true
|
|
80
|
+
return CATEGORY_KEYS.some((key) => Math.abs(next[key] - previous[key]) >= PUBLISH_MIN_DELTA)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 输入:已有 metadata + 本次统计。
|
|
87
|
+
* 输出:保留其他键、替换 STATS_KEY 的新对象。
|
|
88
|
+
* 预期:metadata 是整体替换语义,必须先合并再写。
|
|
89
|
+
*/
|
|
90
|
+
export function mergeMetadata(
|
|
91
|
+
metadata: Readonly<Record<string, JsonValue>> | undefined,
|
|
92
|
+
stats: ContextStats,
|
|
93
|
+
): Record<string, JsonValue> {
|
|
94
|
+
return {
|
|
95
|
+
...(metadata ?? {}),
|
|
96
|
+
[STATS_KEY]: {
|
|
97
|
+
system: stats.system,
|
|
98
|
+
tools: stats.tools,
|
|
99
|
+
tool: stats.tool,
|
|
100
|
+
text: stats.text,
|
|
101
|
+
recent: stats.recent,
|
|
102
|
+
summaries: stats.summaries,
|
|
103
|
+
media: stats.media,
|
|
104
|
+
estimatedTotal: stats.estimatedTotal,
|
|
105
|
+
at: stats.at,
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 输入:已有 metadata + 终止原因。
|
|
112
|
+
* 输出:保留其他键、写入终止原因的新对象。
|
|
113
|
+
* 预期:TUI 面板读 TERMINAL_KEY 显示警告;写入失败不影响压缩状态。
|
|
114
|
+
*/
|
|
115
|
+
export function mergeTerminalMetadata(
|
|
116
|
+
metadata: Readonly<Record<string, JsonValue>> | undefined,
|
|
117
|
+
message: string,
|
|
118
|
+
): Record<string, JsonValue> {
|
|
119
|
+
return { ...(metadata ?? {}), [TERMINAL_KEY]: message }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface ScaledCategory {
|
|
123
|
+
readonly key: CategoryKey
|
|
124
|
+
readonly value: number
|
|
125
|
+
readonly pct: number
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 输入:估算统计 + 真实总量(0 表示上游没给)。
|
|
130
|
+
* 输出:按真实总量缩放后的各类数值与百分比。
|
|
131
|
+
* 预期:真实总量缺失时按原值展示;百分比分母是缩放后的合计。
|
|
132
|
+
*/
|
|
133
|
+
export function scaleCategories(stats: ContextStats, realTotal: number): readonly ScaledCategory[] {
|
|
134
|
+
const scale = realTotal > 0 && stats.estimatedTotal > 0 ? realTotal / stats.estimatedTotal : 1
|
|
135
|
+
const scaled = CATEGORY_KEYS.map((key) => ({ key, value: Math.round(stats[key] * scale) }))
|
|
136
|
+
const total = scaled.reduce((sum, item) => sum + item.value, 0)
|
|
137
|
+
return scaled.map((item) => ({
|
|
138
|
+
key: item.key,
|
|
139
|
+
value: item.value,
|
|
140
|
+
pct: total > 0 ? Math.round((item.value / total) * 100) : 0,
|
|
141
|
+
}))
|
|
142
|
+
}
|