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.
Files changed (55) hide show
  1. package/package.json +64 -64
  2. package/src/compression/active-summary-cache.ts +50 -50
  3. package/src/compression/adopt.ts +57 -57
  4. package/src/compression/auto.ts +372 -372
  5. package/src/compression/babel.ts +85 -85
  6. package/src/compression/block.ts +25 -25
  7. package/src/compression/cache-history.ts +75 -75
  8. package/src/compression/compaction.ts +189 -189
  9. package/src/compression/decompress-tool.ts +74 -74
  10. package/src/compression/decompress.ts +292 -292
  11. package/src/compression/deleted-sessions.ts +24 -24
  12. package/src/compression/distill.ts +220 -220
  13. package/src/compression/events.ts +275 -275
  14. package/src/compression/markdown.ts +53 -53
  15. package/src/compression/normalize.ts +114 -114
  16. package/src/compression/render.ts +70 -70
  17. package/src/compression/schema.ts +84 -84
  18. package/src/compression/select.ts +123 -123
  19. package/src/compression/session-message.ts +112 -112
  20. package/src/compression/settings-rpc.ts +111 -111
  21. package/src/compression/settings.ts +175 -175
  22. package/src/compression/state.ts +248 -248
  23. package/src/compression/summarize.ts +595 -595
  24. package/src/compression/summary.ts +73 -73
  25. package/src/compression/tool.ts +142 -142
  26. package/src/compression/transcript-store.ts +258 -258
  27. package/src/compression/transcript.ts +63 -63
  28. package/src/compression/validate.ts +81 -81
  29. package/src/i18n/en.ts +93 -93
  30. package/src/i18n/index.ts +28 -28
  31. package/src/i18n/types.ts +89 -89
  32. package/src/i18n/zh.ts +93 -93
  33. package/src/index.ts +108 -108
  34. package/src/shared/blocks.ts +17 -17
  35. package/src/shared/cache.ts +199 -199
  36. package/src/shared/format.ts +23 -23
  37. package/src/shared/media.ts +184 -184
  38. package/src/shared/parse.ts +25 -25
  39. package/src/shared/parts.ts +72 -72
  40. package/src/shared/rpc.ts +145 -145
  41. package/src/shared/scan.ts +32 -32
  42. package/src/shared/settings.ts +471 -471
  43. package/src/shared/stats.ts +142 -142
  44. package/src/shared/tokens.ts +259 -259
  45. package/src/tui/chart/cache-chart.ts +237 -237
  46. package/src/tui/chart/color.ts +58 -58
  47. package/src/tui/index.tsx +111 -111
  48. package/src/tui/panel/cache.tsx +203 -203
  49. package/src/tui/panel/data.ts +105 -105
  50. package/src/tui/panel/index.tsx +176 -176
  51. package/src/tui/panel/summaries.tsx +192 -192
  52. package/src/tui/settings/dialogs.tsx +215 -215
  53. package/src/tui/settings/options.ts +238 -238
  54. package/src/tui/settings.tsx +418 -418
  55. package/src/tui/types.ts +4 -4
package/package.json CHANGED
@@ -1,64 +1,64 @@
1
- {
2
- "name": "compressed-context",
3
- "version": "0.1.0",
4
- "description": "OpenCode 上下文管理插件:按需压缩、自包含摘要",
5
- "type": "module",
6
- "license": "MIT",
7
- "author": "1cyberlangke1",
8
- "homepage": "https://github.com/1cyberlangke1/compressed-context#readme",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/1cyberlangke1/compressed-context.git"
12
- },
13
- "bugs": {
14
- "url": "https://github.com/1cyberlangke1/compressed-context/issues"
15
- },
16
- "keywords": [
17
- "opencode",
18
- "plugin",
19
- "context",
20
- "compression",
21
- "summarization",
22
- "llm"
23
- ],
24
- "exports": {
25
- ".": "./src/index.ts",
26
- "./tui": "./src/tui/index.tsx"
27
- },
28
- "files": [
29
- "src"
30
- ],
31
- "peerDependencies": {
32
- "@opencode/plugin": "^2.0.32",
33
- "@opentui/core": ">=0.5.17",
34
- "@opentui/solid": ">=0.5.17",
35
- "solid-js": ">=1.9.0"
36
- },
37
- "scripts": {
38
- "typecheck": "tsc --noEmit",
39
- "lint": "eslint .",
40
- "check": "pnpm typecheck && pnpm lint && pnpm test && pnpm test:tui",
41
- "test": "vitest run",
42
- "test:tui": "cd tui-test && bun test",
43
- "test:e2e:nudge": "bun scripts/e2e-nudge.mjs",
44
- "test:e2e:tool-loop": "bun scripts/e2e-tool-loop.mjs",
45
- "test:watch": "vitest"
46
- },
47
- "devDependencies": {
48
- "@eslint/js": "^10.0.1",
49
- "@opencode/plugin": "^2.0.32",
50
- "@opencode/theme": "2.0.32",
51
- "@opentui/core": "0.5.17",
52
- "@opentui/solid": "0.5.17",
53
- "@types/node": "^26.6.5",
54
- "eslint": "^10.13.0",
55
- "solid-js": "1.9.15",
56
- "typescript": "^6.0.3",
57
- "typescript-eslint": "^8.71.2",
58
- "vitest": "^5.0.5"
59
- },
60
- "dependencies": {
61
- "effect": "4.0.1",
62
- "jsonrepair": "^3.15.0"
63
- }
64
- }
1
+ {
2
+ "name": "compressed-context",
3
+ "version": "0.1.1",
4
+ "description": "OpenCode 上下文管理插件:按需压缩、自包含摘要",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "1cyberlangke1",
8
+ "homepage": "https://github.com/1cyberlangke1/compressed-context#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/1cyberlangke1/compressed-context.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/1cyberlangke1/compressed-context/issues"
15
+ },
16
+ "keywords": [
17
+ "opencode",
18
+ "plugin",
19
+ "context",
20
+ "compression",
21
+ "summarization",
22
+ "llm"
23
+ ],
24
+ "exports": {
25
+ ".": "./src/index.ts",
26
+ "./tui": "./src/tui/index.tsx"
27
+ },
28
+ "files": [
29
+ "src"
30
+ ],
31
+ "peerDependencies": {
32
+ "@opencode/plugin": "^2.0.32",
33
+ "@opentui/core": ">=0.5.17",
34
+ "@opentui/solid": ">=0.5.17",
35
+ "solid-js": ">=1.9.0"
36
+ },
37
+ "scripts": {
38
+ "typecheck": "tsc --noEmit",
39
+ "lint": "eslint .",
40
+ "check": "pnpm typecheck && pnpm lint && pnpm test && pnpm test:tui",
41
+ "test": "vitest run",
42
+ "test:tui": "cd tui-test && bun test",
43
+ "test:e2e:nudge": "bun scripts/e2e-nudge.mjs",
44
+ "test:e2e:tool-loop": "bun scripts/e2e-tool-loop.mjs",
45
+ "test:watch": "vitest"
46
+ },
47
+ "devDependencies": {
48
+ "@eslint/js": "^10.0.1",
49
+ "@opencode/plugin": "^2.0.32",
50
+ "@opencode/theme": "2.0.32",
51
+ "@opentui/core": "0.5.17",
52
+ "@opentui/solid": "0.5.17",
53
+ "@types/node": "^26.6.5",
54
+ "eslint": "^10.13.0",
55
+ "solid-js": "1.9.15",
56
+ "typescript": "^6.0.3",
57
+ "typescript-eslint": "^8.71.2",
58
+ "vitest": "^5.0.5"
59
+ },
60
+ "dependencies": {
61
+ "effect": "4.0.1",
62
+ "jsonrepair": "^3.15.0"
63
+ }
64
+ }
@@ -1,50 +1,50 @@
1
- import { renderAll, type RenderOptions } from "./render.ts"
2
- import type { State } from "./state.ts"
3
-
4
- interface Entry {
5
- readonly revision: string
6
- readonly text: string
7
- }
8
-
9
- const LIMIT = 32
10
- const cache = new Map<string, Entry>()
11
-
12
- /** 输入:状态 + 渲染选项;输出:只由块身份决定的 revision。 */
13
- function revisionOf(state: State, options: RenderOptions): string {
14
- return JSON.stringify({
15
- blocks: state.blocks.map((block) => [
16
- block.id,
17
- block.tier,
18
- block.order,
19
- block.at,
20
- block.sourceBlockIds ?? [],
21
- ]),
22
- pending: state.pending?.id ?? null,
23
- options,
24
- })
25
- }
26
-
27
- /** 输入:sessionID + 状态 + 渲染选项;输出:活跃摘要文本。 */
28
- export function renderActiveSummary(
29
- sessionID: string,
30
- state: State,
31
- options: RenderOptions,
32
- ): string {
33
- const revision = revisionOf(state, options)
34
- const hit = cache.get(sessionID)
35
- if (hit?.revision === revision) return hit.text
36
- const text = renderAll(state.blocks, state.pending, options)
37
- cache.delete(sessionID)
38
- cache.set(sessionID, { revision, text })
39
- while (cache.size > LIMIT) {
40
- const oldest = cache.keys().next().value
41
- if (oldest === undefined) break
42
- cache.delete(oldest)
43
- }
44
- return text
45
- }
46
-
47
- /** 输入:sessionID;输出:无;预期:会话删除时丢掉活跃摘要缓存。 */
48
- export function clearActiveSummary(sessionID: string): void {
49
- cache.delete(sessionID)
50
- }
1
+ import { renderAll, type RenderOptions } from "./render.ts"
2
+ import type { State } from "./state.ts"
3
+
4
+ interface Entry {
5
+ readonly revision: string
6
+ readonly text: string
7
+ }
8
+
9
+ const LIMIT = 32
10
+ const cache = new Map<string, Entry>()
11
+
12
+ /** 输入:状态 + 渲染选项;输出:只由块身份决定的 revision。 */
13
+ function revisionOf(state: State, options: RenderOptions): string {
14
+ return JSON.stringify({
15
+ blocks: state.blocks.map((block) => [
16
+ block.id,
17
+ block.tier,
18
+ block.order,
19
+ block.at,
20
+ block.sourceBlockIds ?? [],
21
+ ]),
22
+ pending: state.pending?.id ?? null,
23
+ options,
24
+ })
25
+ }
26
+
27
+ /** 输入:sessionID + 状态 + 渲染选项;输出:活跃摘要文本。 */
28
+ export function renderActiveSummary(
29
+ sessionID: string,
30
+ state: State,
31
+ options: RenderOptions,
32
+ ): string {
33
+ const revision = revisionOf(state, options)
34
+ const hit = cache.get(sessionID)
35
+ if (hit?.revision === revision) return hit.text
36
+ const text = renderAll(state.blocks, state.pending, options)
37
+ cache.delete(sessionID)
38
+ cache.set(sessionID, { revision, text })
39
+ while (cache.size > LIMIT) {
40
+ const oldest = cache.keys().next().value
41
+ if (oldest === undefined) break
42
+ cache.delete(oldest)
43
+ }
44
+ return text
45
+ }
46
+
47
+ /** 输入:sessionID;输出:无;预期:会话删除时丢掉活跃摘要缓存。 */
48
+ export function clearActiveSummary(sessionID: string): void {
49
+ cache.delete(sessionID)
50
+ }
@@ -1,57 +1,57 @@
1
- import type { Plugin } from "@opencode/plugin"
2
-
3
- import { createT1Block } from "./block.ts"
4
- import { toMarkdownSummary } from "./markdown.ts"
5
- import { isCheckpoint, messageText, type MessageLike } from "./select.ts"
6
- import { loadState, saveState } from "./state.ts"
7
-
8
- /** 原生 checkpoint 里抽出的摘要与 recent 上下文。 */
9
- export interface NativeCheckpoint {
10
- readonly summary: string
11
- readonly recent?: string
12
- }
13
-
14
- /**
15
- * 输入:OpenCode checkpoint 文本。
16
- * 输出:`<summary>` / `<recent-context>` 内容;不是 checkpoint 时 undefined。
17
- * 预期:只做只读解析,不改原文。
18
- */
19
- export function extractCheckpoint(text: string): NativeCheckpoint | undefined {
20
- if (!text.trimStart().startsWith("<conversation-checkpoint>")) return undefined
21
- const summary = /<summary>\n?([\s\S]*?)\n?<\/summary>/.exec(text)?.[1]?.trim()
22
- if (summary === undefined || summary.length === 0) return undefined
23
- const recent = /<recent-context>\n?([\s\S]*?)\n?<\/recent-context>/.exec(text)?.[1]?.trim()
24
- return recent === undefined || recent.length === 0 ? { summary } : { summary, recent }
25
- }
26
-
27
- /**
28
- * 输入:插件上下文 + sessionID + 当前请求消息。
29
- * 输出:是否收养了原生摘要。
30
- * 预期:仅当插件还没有任何块时,把 OpenCode 自己的 checkpoint 摘要当作 b1:T1;
31
- * 它没有原始 transcript,fromId/toId 指向 checkpoint 消息本身。
32
- */
33
- export async function adoptNativeCheckpoint(
34
- ctx: Plugin.Context,
35
- sessionID: string,
36
- messages: readonly MessageLike[],
37
- ): Promise<boolean> {
38
- const state = await loadState(ctx.storage, sessionID)
39
- if (state.blocks.length > 0 || state.pending !== undefined || state.terminal !== undefined) return false
40
-
41
- const checkpoint = messages.findLast((message) => isCheckpoint(message))
42
- if (checkpoint === undefined) return false
43
- const parsed = extractCheckpoint(messageText(checkpoint))
44
- if (parsed === undefined) return false
45
-
46
- const summary = toMarkdownSummary(parsed.summary)
47
- const block = createT1Block({
48
- blocks: [],
49
- summary,
50
- // 原生摘要没有原始 transcript;空范围让 decompress full 返回 unavailable,而不是伪造原文。
51
- fromId: "",
52
- toId: "",
53
- sourceChars: parsed.summary.length,
54
- })
55
- await saveState(ctx.storage, sessionID, { blocks: [block], attempts: 0 })
56
- return true
57
- }
1
+ import type { Plugin } from "@opencode/plugin"
2
+
3
+ import { createT1Block } from "./block.ts"
4
+ import { toMarkdownSummary } from "./markdown.ts"
5
+ import { isCheckpoint, messageText, type MessageLike } from "./select.ts"
6
+ import { loadState, saveState } from "./state.ts"
7
+
8
+ /** 原生 checkpoint 里抽出的摘要与 recent 上下文。 */
9
+ export interface NativeCheckpoint {
10
+ readonly summary: string
11
+ readonly recent?: string
12
+ }
13
+
14
+ /**
15
+ * 输入:OpenCode checkpoint 文本。
16
+ * 输出:`<summary>` / `<recent-context>` 内容;不是 checkpoint 时 undefined。
17
+ * 预期:只做只读解析,不改原文。
18
+ */
19
+ export function extractCheckpoint(text: string): NativeCheckpoint | undefined {
20
+ if (!text.trimStart().startsWith("<conversation-checkpoint>")) return undefined
21
+ const summary = /<summary>\n?([\s\S]*?)\n?<\/summary>/.exec(text)?.[1]?.trim()
22
+ if (summary === undefined || summary.length === 0) return undefined
23
+ const recent = /<recent-context>\n?([\s\S]*?)\n?<\/recent-context>/.exec(text)?.[1]?.trim()
24
+ return recent === undefined || recent.length === 0 ? { summary } : { summary, recent }
25
+ }
26
+
27
+ /**
28
+ * 输入:插件上下文 + sessionID + 当前请求消息。
29
+ * 输出:是否收养了原生摘要。
30
+ * 预期:仅当插件还没有任何块时,把 OpenCode 自己的 checkpoint 摘要当作 b1:T1;
31
+ * 它没有原始 transcript,fromId/toId 指向 checkpoint 消息本身。
32
+ */
33
+ export async function adoptNativeCheckpoint(
34
+ ctx: Plugin.Context,
35
+ sessionID: string,
36
+ messages: readonly MessageLike[],
37
+ ): Promise<boolean> {
38
+ const state = await loadState(ctx.storage, sessionID)
39
+ if (state.blocks.length > 0 || state.pending !== undefined || state.terminal !== undefined) return false
40
+
41
+ const checkpoint = messages.findLast((message) => isCheckpoint(message))
42
+ if (checkpoint === undefined) return false
43
+ const parsed = extractCheckpoint(messageText(checkpoint))
44
+ if (parsed === undefined) return false
45
+
46
+ const summary = toMarkdownSummary(parsed.summary)
47
+ const block = createT1Block({
48
+ blocks: [],
49
+ summary,
50
+ // 原生摘要没有原始 transcript;空范围让 decompress full 返回 unavailable,而不是伪造原文。
51
+ fromId: "",
52
+ toId: "",
53
+ sourceChars: parsed.summary.length,
54
+ })
55
+ await saveState(ctx.storage, sessionID, { blocks: [block], attempts: 0 })
56
+ return true
57
+ }