compressed-context 0.1.0 → 0.2.0

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 (57) hide show
  1. package/README.md +59 -51
  2. package/package.json +64 -64
  3. package/src/compression/active-summary-cache.ts +50 -50
  4. package/src/compression/adopt.ts +57 -57
  5. package/src/compression/auto.ts +386 -372
  6. package/src/compression/babel.ts +85 -85
  7. package/src/compression/block.ts +25 -25
  8. package/src/compression/cache-history.ts +75 -75
  9. package/src/compression/compaction.ts +189 -189
  10. package/src/compression/decompress-tool.ts +74 -74
  11. package/src/compression/decompress.ts +292 -292
  12. package/src/compression/deleted-sessions.ts +24 -24
  13. package/src/compression/distill.ts +220 -220
  14. package/src/compression/events.ts +275 -275
  15. package/src/compression/markdown.ts +53 -53
  16. package/src/compression/normalize.ts +114 -114
  17. package/src/compression/render.ts +70 -70
  18. package/src/compression/schema.ts +131 -84
  19. package/src/compression/select.ts +123 -123
  20. package/src/compression/session-message.ts +112 -112
  21. package/src/compression/settings-rpc.ts +123 -111
  22. package/src/compression/settings.ts +175 -175
  23. package/src/compression/shape.ts +75 -0
  24. package/src/compression/state.ts +248 -248
  25. package/src/compression/summarize.ts +534 -595
  26. package/src/compression/summary.ts +73 -73
  27. package/src/compression/tool.ts +184 -142
  28. package/src/compression/transcript-store.ts +258 -258
  29. package/src/compression/transcript.ts +63 -63
  30. package/src/compression/validate.ts +81 -81
  31. package/src/i18n/en.ts +93 -93
  32. package/src/i18n/index.ts +28 -28
  33. package/src/i18n/types.ts +89 -89
  34. package/src/i18n/zh.ts +93 -93
  35. package/src/index.ts +116 -108
  36. package/src/shared/blocks.ts +17 -17
  37. package/src/shared/cache.ts +199 -199
  38. package/src/shared/format.ts +23 -23
  39. package/src/shared/media.ts +184 -184
  40. package/src/shared/parse.ts +25 -25
  41. package/src/shared/parts.ts +72 -72
  42. package/src/shared/rpc.ts +145 -145
  43. package/src/shared/scan.ts +32 -32
  44. package/src/shared/settings.ts +466 -471
  45. package/src/shared/stats.ts +142 -142
  46. package/src/shared/tokens.ts +259 -259
  47. package/src/tui/chart/cache-chart.ts +237 -237
  48. package/src/tui/chart/color.ts +58 -58
  49. package/src/tui/index.tsx +109 -111
  50. package/src/tui/panel/cache.tsx +203 -203
  51. package/src/tui/panel/data.ts +105 -105
  52. package/src/tui/panel/index.tsx +176 -176
  53. package/src/tui/panel/summaries.tsx +203 -192
  54. package/src/tui/settings/dialogs.tsx +215 -215
  55. package/src/tui/settings/options.ts +238 -238
  56. package/src/tui/settings.tsx +418 -418
  57. package/src/tui/types.ts +4 -4
@@ -1,189 +1,189 @@
1
- import type { Plugin } from "@opencode/plugin"
2
-
3
- import { resolveSummaryLimits } from "../shared/settings.ts"
4
- import { mergeTerminalMetadata } from "../shared/stats.ts"
5
- import { renderActiveSummary } from "./active-summary-cache.ts"
6
- import { markCompactionInFlight, resolveLimit } from "./auto.ts"
7
- import { createT1Block } from "./block.ts"
8
- import { distillOnce, isTerminal, planT1Phase } from "./distill.ts"
9
- import { TERMINAL_MESSAGE } from "./events.ts"
10
- import { activeBlocks } from "./render.ts"
11
- import { messageRange, olderMessages, textChars, type MessageLike } from "./select.ts"
12
- import { loadServerSettings } from "./settings.ts"
13
- import { loadState, saveState, type Block, type CompactionPhase, type State } from "./state.ts"
14
- import { buildT1Instruction, sessionGenerator, summarizeInstruction } from "./summarize.ts"
15
- import type { ModelRefLike } from "./summarize.ts"
16
- import { buildTranscript } from "./transcript.ts"
17
- import { saveTranscript } from "./transcript-store.ts"
18
- import { MIN_SOURCE_CHARS, validateSummary } from "./validate.ts"
19
-
20
- /** compaction 钩子需要的最小事件形状。 */
21
- export interface CompactionEvent {
22
- readonly sessionID: string
23
- readonly messages: readonly MessageLike[]
24
- readonly model: ModelRefLike
25
- result?: { summary: string }
26
- }
27
-
28
- /**
29
- * 输入:插件上下文 + compaction 事件。
30
- * 输出:无;预期:T1 生成摘要或 T2/T3 合并块,把结果写回 event.result。
31
- * 终止时记录 terminal;异常写入 lastError 后重新抛出,交给失败事件。
32
- */
33
- export async function handleCompaction(ctx: Plugin.Context, event: CompactionEvent): Promise<void> {
34
- markCompactionInFlight(event.sessionID, true)
35
- try {
36
- const state = await loadState(ctx.storage, event.sessionID)
37
- if (state.terminal !== undefined) throw new Error(state.terminal)
38
- const settings = await loadServerSettings(ctx.storage)
39
- // auto 窗口在运行时解析后钳制摘要上限;数字窗口已在保存时校验,这里统一再钳一次。
40
- const windowTokens = await resolveLimit(ctx, event.model, settings.contextLimit)
41
- const limits = resolveSummaryLimits(settings, windowTokens)
42
- const renderOptions = {
43
- showTimeline: settings.showTimeline,
44
- showBlockTokens: settings.showBlockTokens,
45
- }
46
-
47
- /** 保存这一层的结果;pending 可选,phase 由下一层决定。 */
48
- const save = async (
49
- blocks: readonly Block[],
50
- pending: Block | undefined,
51
- phase: CompactionPhase | undefined,
52
- error: string | undefined,
53
- ): Promise<void> => {
54
- const base: State = {
55
- blocks,
56
- attempts: state.attempts,
57
- ...(state.lastError === undefined ? {} : { lastError: state.lastError }),
58
- ...(phase === undefined ? {} : { phase }),
59
- }
60
- const next: State = pending === undefined ? base : { ...base, pending }
61
- await saveState(
62
- ctx.storage,
63
- event.sessionID,
64
- error === undefined ? next : { ...next, lastError: error },
65
- )
66
- }
67
-
68
- // 第二阶段:T2/T3。older 里没有真实消息,或新消息不足以单独做 T1 时合并块。
69
- // 短消息仍由 OpenCode 原样保留在 recent 里,不会丢内容。
70
- // 有足够新内容时走下面的 T1:先总结新内容,phase 留到下一次压缩再合并。
71
- const older = olderMessages(event.messages)
72
- const olderChars = textChars(older)
73
- if (state.phase !== undefined && (older.length === 0 || olderChars < MIN_SOURCE_CHARS)) {
74
- const result = await distillOnce({
75
- ctx,
76
- sessionID: event.sessionID,
77
- state,
78
- pending: state.pending,
79
- budgetTokens: limits.budgetTokens,
80
- summaryBlockChars: limits.blockChars,
81
- summaryTotalChars: limits.totalChars,
82
- summaryLanguage: settings.summaryLanguage,
83
- summaryFormat: settings.summaryFormat,
84
- phase: state.phase,
85
- showTimeline: settings.showTimeline,
86
- showBlockTokens: settings.showBlockTokens,
87
- })
88
- await save(result.blocks, result.pending, result.nextPhase, result.error)
89
- event.result = { summary: result.summary }
90
- // 摘要链仍超预算且没有合法候选:无法再压,记录终止原因并停止自动压缩。
91
- if (
92
- isTerminal(
93
- result,
94
- activeBlocks(result.blocks, result.pending),
95
- limits.budgetTokens,
96
- limits.totalChars,
97
- )
98
- ) {
99
- await saveState(ctx.storage, event.sessionID, {
100
- blocks: result.blocks,
101
- attempts: 0,
102
- lastError: TERMINAL_MESSAGE,
103
- terminal: TERMINAL_MESSAGE,
104
- })
105
- // 让 TUI 面板能显示终止原因;写失败不影响压缩状态。
106
- try {
107
- const session = await ctx.session.get({ sessionID: event.sessionID })
108
- await ctx.session.update({
109
- sessionID: event.sessionID,
110
- metadata: mergeTerminalMetadata(session.metadata, TERMINAL_MESSAGE),
111
- })
112
- } catch {
113
- // best effort
114
- }
115
- }
116
- return
117
- }
118
-
119
- // 第一阶段:T1。已有 pending(compress 工具)就直接用;
120
- // 否则用 session 前缀 + 尾部指令生成,保持前缀缓存。
121
- let pending = state.pending
122
- if (pending === undefined) {
123
- const eligible = older
124
- const transcript = buildTranscript(eligible)
125
- const sourceChars = transcript.length
126
- if (eligible.length === 0 || sourceChars < MIN_SOURCE_CHARS) {
127
- throw new Error("nothing to compress")
128
- }
129
- let failure = "unknown"
130
- const summary = await summarizeInstruction({
131
- prompt: buildT1Instruction({
132
- maxChars: limits.blockChars,
133
- totalChars: limits.totalChars,
134
- language: settings.summaryLanguage,
135
- format: settings.summaryFormat,
136
- }),
137
- sourceChars,
138
- maxChars: limits.blockChars,
139
- generate: sessionGenerator(ctx.session, event.sessionID),
140
- format: settings.summaryFormat,
141
- onFailure: (reason) => {
142
- failure = reason
143
- },
144
- })
145
- if (summary === undefined) throw new Error(`compression failed: ${failure}`)
146
- const validation = validateSummary(summary, sourceChars, undefined, limits.blockChars)
147
- if (!validation.ok) throw new Error(validation.message)
148
-
149
- const range = messageRange(eligible)
150
- if (range === undefined) throw new Error("messages are missing ids")
151
-
152
- pending = createT1Block({
153
- blocks: state.blocks,
154
- summary,
155
- fromId: range.fromId,
156
- toId: range.toId,
157
- sourceChars,
158
- })
159
-
160
- // 原文与摘要同生命周期;写盘失败时摘要仍然可用,解压降级为占位符。
161
- try {
162
- await saveTranscript(ctx.storage, event.sessionID, pending, eligible)
163
- } catch {
164
- // best effort
165
- }
166
- }
167
-
168
- // T1 之后再检查预算;没超就不排 T2。
169
- const active = activeBlocks(state.blocks, pending)
170
- const summary = renderActiveSummary(
171
- event.sessionID,
172
- { ...state, pending },
173
- renderOptions,
174
- )
175
- const nextPhase = planT1Phase(summary, active, limits.budgetTokens, limits.totalChars)
176
- await save(state.blocks, pending, nextPhase, undefined)
177
- event.result = { summary }
178
- } catch (error) {
179
- // 把钩子里的真实异常留给失败事件和 TUI;不要只留下“compaction failed”。
180
- const current = await loadState(ctx.storage, event.sessionID)
181
- await saveState(ctx.storage, event.sessionID, {
182
- ...current,
183
- lastError: error instanceof Error ? (error.stack ?? error.message) : String(error),
184
- })
185
- throw error
186
- } finally {
187
- markCompactionInFlight(event.sessionID, false)
188
- }
189
- }
1
+ import type { Plugin } from "@opencode/plugin"
2
+
3
+ import { resolveSummaryLimits } from "../shared/settings.ts"
4
+ import { mergeTerminalMetadata } from "../shared/stats.ts"
5
+ import { renderActiveSummary } from "./active-summary-cache.ts"
6
+ import { markCompactionInFlight, resolveLimit } from "./auto.ts"
7
+ import { createT1Block } from "./block.ts"
8
+ import { distillOnce, isTerminal, planT1Phase } from "./distill.ts"
9
+ import { TERMINAL_MESSAGE } from "./events.ts"
10
+ import { activeBlocks } from "./render.ts"
11
+ import { messageRange, olderMessages, textChars, type MessageLike } from "./select.ts"
12
+ import { loadServerSettings } from "./settings.ts"
13
+ import { loadState, saveState, type Block, type CompactionPhase, type State } from "./state.ts"
14
+ import { buildT1Instruction, sessionGenerator, summarizeInstruction } from "./summarize.ts"
15
+ import type { ModelRefLike } from "./summarize.ts"
16
+ import { buildTranscript } from "./transcript.ts"
17
+ import { saveTranscript } from "./transcript-store.ts"
18
+ import { MIN_SOURCE_CHARS, validateSummary } from "./validate.ts"
19
+
20
+ /** compaction 钩子需要的最小事件形状。 */
21
+ export interface CompactionEvent {
22
+ readonly sessionID: string
23
+ readonly messages: readonly MessageLike[]
24
+ readonly model: ModelRefLike
25
+ result?: { summary: string }
26
+ }
27
+
28
+ /**
29
+ * 输入:插件上下文 + compaction 事件。
30
+ * 输出:无;预期:T1 生成摘要或 T2/T3 合并块,把结果写回 event.result。
31
+ * 终止时记录 terminal;异常写入 lastError 后重新抛出,交给失败事件。
32
+ */
33
+ export async function handleCompaction(ctx: Plugin.Context, event: CompactionEvent): Promise<void> {
34
+ markCompactionInFlight(event.sessionID, true)
35
+ try {
36
+ const state = await loadState(ctx.storage, event.sessionID)
37
+ if (state.terminal !== undefined) throw new Error(state.terminal)
38
+ const settings = await loadServerSettings(ctx.storage)
39
+ // auto 窗口在运行时解析后钳制摘要上限;数字窗口已在保存时校验,这里统一再钳一次。
40
+ const windowTokens = await resolveLimit(ctx, event.model, settings.contextLimit)
41
+ const limits = resolveSummaryLimits(settings, windowTokens)
42
+ const renderOptions = {
43
+ showTimeline: settings.showTimeline,
44
+ showBlockTokens: settings.showBlockTokens,
45
+ }
46
+
47
+ /** 保存这一层的结果;pending 可选,phase 由下一层决定。 */
48
+ const save = async (
49
+ blocks: readonly Block[],
50
+ pending: Block | undefined,
51
+ phase: CompactionPhase | undefined,
52
+ error: string | undefined,
53
+ ): Promise<void> => {
54
+ const base: State = {
55
+ blocks,
56
+ attempts: state.attempts,
57
+ ...(state.lastError === undefined ? {} : { lastError: state.lastError }),
58
+ ...(phase === undefined ? {} : { phase }),
59
+ }
60
+ const next: State = pending === undefined ? base : { ...base, pending }
61
+ await saveState(
62
+ ctx.storage,
63
+ event.sessionID,
64
+ error === undefined ? next : { ...next, lastError: error },
65
+ )
66
+ }
67
+
68
+ // 第二阶段:T2/T3。older 里没有真实消息,或新消息不足以单独做 T1 时合并块。
69
+ // 短消息仍由 OpenCode 原样保留在 recent 里,不会丢内容。
70
+ // 有足够新内容时走下面的 T1:先总结新内容,phase 留到下一次压缩再合并。
71
+ const older = olderMessages(event.messages)
72
+ const olderChars = textChars(older)
73
+ if (state.phase !== undefined && (older.length === 0 || olderChars < MIN_SOURCE_CHARS)) {
74
+ const result = await distillOnce({
75
+ ctx,
76
+ sessionID: event.sessionID,
77
+ state,
78
+ pending: state.pending,
79
+ budgetTokens: limits.budgetTokens,
80
+ summaryBlockChars: limits.blockChars,
81
+ summaryTotalChars: limits.totalChars,
82
+ summaryLanguage: settings.summaryLanguage,
83
+ summaryFormat: settings.summaryFormat,
84
+ phase: state.phase,
85
+ showTimeline: settings.showTimeline,
86
+ showBlockTokens: settings.showBlockTokens,
87
+ })
88
+ await save(result.blocks, result.pending, result.nextPhase, result.error)
89
+ event.result = { summary: result.summary }
90
+ // 摘要链仍超预算且没有合法候选:无法再压,记录终止原因并停止自动压缩。
91
+ if (
92
+ isTerminal(
93
+ result,
94
+ activeBlocks(result.blocks, result.pending),
95
+ limits.budgetTokens,
96
+ limits.totalChars,
97
+ )
98
+ ) {
99
+ await saveState(ctx.storage, event.sessionID, {
100
+ blocks: result.blocks,
101
+ attempts: 0,
102
+ lastError: TERMINAL_MESSAGE,
103
+ terminal: TERMINAL_MESSAGE,
104
+ })
105
+ // 让 TUI 面板能显示终止原因;写失败不影响压缩状态。
106
+ try {
107
+ const session = await ctx.session.get({ sessionID: event.sessionID })
108
+ await ctx.session.update({
109
+ sessionID: event.sessionID,
110
+ metadata: mergeTerminalMetadata(session.metadata, TERMINAL_MESSAGE),
111
+ })
112
+ } catch {
113
+ // best effort
114
+ }
115
+ }
116
+ return
117
+ }
118
+
119
+ // 第一阶段:T1。已有 pending(compress 工具)就直接用;
120
+ // 否则用 session 前缀 + 尾部指令生成,保持前缀缓存。
121
+ let pending = state.pending
122
+ if (pending === undefined) {
123
+ const eligible = older
124
+ const transcript = buildTranscript(eligible)
125
+ const sourceChars = transcript.length
126
+ if (eligible.length === 0 || sourceChars < MIN_SOURCE_CHARS) {
127
+ throw new Error("nothing to compress")
128
+ }
129
+ let failure = "unknown"
130
+ const summary = await summarizeInstruction({
131
+ prompt: buildT1Instruction({
132
+ maxChars: limits.blockChars,
133
+ totalChars: limits.totalChars,
134
+ language: settings.summaryLanguage,
135
+ format: settings.summaryFormat,
136
+ }),
137
+ sourceChars,
138
+ maxChars: limits.blockChars,
139
+ generate: sessionGenerator(ctx.session, event.sessionID),
140
+ format: settings.summaryFormat,
141
+ onFailure: (reason) => {
142
+ failure = reason
143
+ },
144
+ })
145
+ if (summary === undefined) throw new Error(`compression failed: ${failure}`)
146
+ const validation = validateSummary(summary, sourceChars, undefined, limits.blockChars)
147
+ if (!validation.ok) throw new Error(validation.message)
148
+
149
+ const range = messageRange(eligible)
150
+ if (range === undefined) throw new Error("messages are missing ids")
151
+
152
+ pending = createT1Block({
153
+ blocks: state.blocks,
154
+ summary,
155
+ fromId: range.fromId,
156
+ toId: range.toId,
157
+ sourceChars,
158
+ })
159
+
160
+ // 原文与摘要同生命周期;写盘失败时摘要仍然可用,解压降级为占位符。
161
+ try {
162
+ await saveTranscript(ctx.storage, event.sessionID, pending, eligible)
163
+ } catch {
164
+ // best effort
165
+ }
166
+ }
167
+
168
+ // T1 之后再检查预算;没超就不排 T2。
169
+ const active = activeBlocks(state.blocks, pending)
170
+ const summary = renderActiveSummary(
171
+ event.sessionID,
172
+ { ...state, pending },
173
+ renderOptions,
174
+ )
175
+ const nextPhase = planT1Phase(summary, active, limits.budgetTokens, limits.totalChars)
176
+ await save(state.blocks, pending, nextPhase, undefined)
177
+ event.result = { summary }
178
+ } catch (error) {
179
+ // 把钩子里的真实异常留给失败事件和 TUI;不要只留下“compaction failed”。
180
+ const current = await loadState(ctx.storage, event.sessionID)
181
+ await saveState(ctx.storage, event.sessionID, {
182
+ ...current,
183
+ lastError: error instanceof Error ? (error.stack ?? error.message) : String(error),
184
+ })
185
+ throw error
186
+ } finally {
187
+ markCompactionInFlight(event.sessionID, false)
188
+ }
189
+ }
@@ -1,74 +1,74 @@
1
- import type { Plugin } from "@opencode/plugin"
2
- import { Schema } from "effect"
3
-
4
- import { effectiveDecompressInlineMax, effectiveDecompressTempCap } from "../shared/settings.ts"
5
- import { loadServerSettings } from "./settings.ts"
6
- import type { Disposable } from "./settings-rpc.ts"
7
-
8
- const DESCRIPTION =
9
- "Restores content from a compressed checkpoint block. Use when you need exact details that were " +
10
- "compressed away (exact errors, code lines, file contents, tool outputs). Pass the block id you see " +
11
- "in the checkpoint (e.g. b3). By default a merged block expands one level (its child summaries); " +
12
- "pass full:true to recurse all the way to the original messages. Large content is written to a " +
13
- "temporary file whose path is returned — open it with the read tool. This tool never changes the " +
14
- "checkpoint; content returns only as this tool's result."
15
-
16
- const DecompressInput = Schema.Struct({
17
- block: Schema.String,
18
- full: Schema.Boolean,
19
- })
20
-
21
- const DecompressOutput = Schema.Struct({
22
- inline: Schema.optional(Schema.String),
23
- path: Schema.optional(Schema.String),
24
- tokens: Schema.optional(Schema.Number),
25
- })
26
-
27
- /**
28
- * 输入:服务端插件上下文。
29
- * 输出:注册 decompress 工具,返回注销句柄。
30
- * 预期:只读操作——不修改块状态;内容作为 tool result 返回。
31
- */
32
- export async function registerDecompressTool(ctx: Plugin.Context): Promise<Disposable> {
33
- const registration = await ctx.tool.transform((tools) => {
34
- tools.add({
35
- name: "decompress",
36
- options: { codemode: false },
37
- description: DESCRIPTION,
38
- input: DecompressInput,
39
- output: DecompressOutput,
40
- execute: async (input, toolCtx) => {
41
- const { resolveDecompress } = await import("./decompress.ts")
42
- const settings = await loadServerSettings(ctx.storage)
43
- if (!settings.enableDecompressTool) {
44
- return {
45
- output: {},
46
- content: "ERROR: decompress tool is disabled in settings",
47
- metadata: { error: true },
48
- }
49
- }
50
- const result = await resolveDecompress(input, ctx, toolCtx.sessionID, {
51
- inlineMax: effectiveDecompressInlineMax(settings),
52
- tempCap: effectiveDecompressTempCap(settings),
53
- showTokens: settings.decompressShowTokens,
54
- })
55
- if (result.status === "error") {
56
- return {
57
- output: {},
58
- content: `ERROR: ${result.message}`,
59
- metadata: { error: true },
60
- }
61
- }
62
- const output: { inline?: string; path?: string; tokens?: number } = {
63
- ...(result.path === undefined ? {} : { path: result.path }),
64
- ...(result.tokens === undefined ? {} : { tokens: result.tokens }),
65
- }
66
- return {
67
- output,
68
- content: result.inline,
69
- }
70
- },
71
- })
72
- })
73
- return registration
74
- }
1
+ import type { Plugin } from "@opencode/plugin"
2
+ import { Schema } from "effect"
3
+
4
+ import { effectiveDecompressInlineMax, effectiveDecompressTempCap } from "../shared/settings.ts"
5
+ import { loadServerSettings } from "./settings.ts"
6
+ import type { Disposable } from "./settings-rpc.ts"
7
+
8
+ const DESCRIPTION =
9
+ "Restores content from a compressed checkpoint block. Use when you need exact details that were " +
10
+ "compressed away (exact errors, code lines, file contents, tool outputs). Pass the block id you see " +
11
+ "in the checkpoint (e.g. b3). By default a merged block expands one level (its child summaries); " +
12
+ "pass full:true to recurse all the way to the original messages. Large content is written to a " +
13
+ "temporary file whose path is returned — open it with the read tool. This tool never changes the " +
14
+ "checkpoint; content returns only as this tool's result."
15
+
16
+ const DecompressInput = Schema.Struct({
17
+ block: Schema.String,
18
+ full: Schema.Boolean,
19
+ })
20
+
21
+ const DecompressOutput = Schema.Struct({
22
+ inline: Schema.optional(Schema.String),
23
+ path: Schema.optional(Schema.String),
24
+ tokens: Schema.optional(Schema.Number),
25
+ })
26
+
27
+ /**
28
+ * 输入:服务端插件上下文。
29
+ * 输出:注册 decompress 工具,返回注销句柄。
30
+ * 预期:只读操作——不修改块状态;内容作为 tool result 返回。
31
+ */
32
+ export async function registerDecompressTool(ctx: Plugin.Context): Promise<Disposable> {
33
+ const registration = await ctx.tool.transform((tools) => {
34
+ tools.add({
35
+ name: "decompress",
36
+ options: { codemode: false },
37
+ description: DESCRIPTION,
38
+ input: DecompressInput,
39
+ output: DecompressOutput,
40
+ execute: async (input, toolCtx) => {
41
+ const { resolveDecompress } = await import("./decompress.ts")
42
+ const settings = await loadServerSettings(ctx.storage)
43
+ if (!settings.enableDecompressTool) {
44
+ return {
45
+ output: {},
46
+ content: "ERROR: decompress tool is disabled in settings",
47
+ metadata: { error: true },
48
+ }
49
+ }
50
+ const result = await resolveDecompress(input, ctx, toolCtx.sessionID, {
51
+ inlineMax: effectiveDecompressInlineMax(settings),
52
+ tempCap: effectiveDecompressTempCap(settings),
53
+ showTokens: settings.decompressShowTokens,
54
+ })
55
+ if (result.status === "error") {
56
+ return {
57
+ output: {},
58
+ content: `ERROR: ${result.message}`,
59
+ metadata: { error: true },
60
+ }
61
+ }
62
+ const output: { inline?: string; path?: string; tokens?: number } = {
63
+ ...(result.path === undefined ? {} : { path: result.path }),
64
+ ...(result.tokens === undefined ? {} : { tokens: result.tokens }),
65
+ }
66
+ return {
67
+ output,
68
+ content: result.inline,
69
+ }
70
+ },
71
+ })
72
+ })
73
+ return registration
74
+ }