@tuanhung303/opencode-acp 2.2.0 → 2.2.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 (160) hide show
  1. package/README.md +131 -71
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +10 -2
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/commands/budget.d.ts +15 -0
  6. package/dist/lib/commands/budget.d.ts.map +1 -0
  7. package/dist/lib/commands/budget.js +120 -0
  8. package/dist/lib/commands/budget.js.map +1 -0
  9. package/dist/lib/commands/context.d.ts +1 -1
  10. package/dist/lib/commands/context.js +4 -4
  11. package/dist/lib/commands/context.js.map +1 -1
  12. package/dist/lib/commands/help.d.ts.map +1 -1
  13. package/dist/lib/commands/help.js +2 -0
  14. package/dist/lib/commands/help.js.map +1 -1
  15. package/dist/lib/commands/protected.d.ts +17 -0
  16. package/dist/lib/commands/protected.d.ts.map +1 -0
  17. package/dist/lib/commands/protected.js +50 -0
  18. package/dist/lib/commands/protected.js.map +1 -0
  19. package/dist/lib/commands/stats.d.ts +1 -1
  20. package/dist/lib/commands/stats.d.ts.map +1 -1
  21. package/dist/lib/commands/stats.js +24 -4
  22. package/dist/lib/commands/stats.js.map +1 -1
  23. package/dist/lib/commands/sweep.d.ts.map +1 -1
  24. package/dist/lib/commands/sweep.js +5 -2
  25. package/dist/lib/commands/sweep.js.map +1 -1
  26. package/dist/lib/config-schema.d.ts +119 -0
  27. package/dist/lib/config-schema.d.ts.map +1 -0
  28. package/dist/lib/config-schema.js +97 -0
  29. package/dist/lib/config-schema.js.map +1 -0
  30. package/dist/lib/config.d.ts +7 -30
  31. package/dist/lib/config.d.ts.map +1 -1
  32. package/dist/lib/config.js +45 -483
  33. package/dist/lib/config.js.map +1 -1
  34. package/dist/lib/hooks.d.ts +14 -0
  35. package/dist/lib/hooks.d.ts.map +1 -1
  36. package/dist/lib/hooks.js +120 -13
  37. package/dist/lib/hooks.js.map +1 -1
  38. package/dist/lib/logger.d.ts +25 -1
  39. package/dist/lib/logger.d.ts.map +1 -1
  40. package/dist/lib/logger.js +45 -4
  41. package/dist/lib/logger.js.map +1 -1
  42. package/dist/lib/messages/index.d.ts +1 -1
  43. package/dist/lib/messages/index.d.ts.map +1 -1
  44. package/dist/lib/messages/index.js +1 -1
  45. package/dist/lib/messages/index.js.map +1 -1
  46. package/dist/lib/messages/inject.d.ts +6 -1
  47. package/dist/lib/messages/inject.d.ts.map +1 -1
  48. package/dist/lib/messages/inject.js +8 -125
  49. package/dist/lib/messages/inject.js.map +1 -1
  50. package/dist/lib/messages/prune.d.ts +8 -0
  51. package/dist/lib/messages/prune.d.ts.map +1 -1
  52. package/dist/lib/messages/prune.js +89 -2
  53. package/dist/lib/messages/prune.js.map +1 -1
  54. package/dist/lib/messages/utils.d.ts +12 -0
  55. package/dist/lib/messages/utils.d.ts.map +1 -1
  56. package/dist/lib/messages/utils.js +34 -0
  57. package/dist/lib/messages/utils.js.map +1 -1
  58. package/dist/lib/prompts/discard-tool-spec.d.ts +1 -1
  59. package/dist/lib/prompts/discard-tool-spec.d.ts.map +1 -1
  60. package/dist/lib/prompts/discard-tool-spec.js +30 -17
  61. package/dist/lib/prompts/discard-tool-spec.js.map +1 -1
  62. package/dist/lib/prompts/extract-tool-spec.d.ts +1 -1
  63. package/dist/lib/prompts/extract-tool-spec.d.ts.map +1 -1
  64. package/dist/lib/prompts/extract-tool-spec.js +19 -11
  65. package/dist/lib/prompts/extract-tool-spec.js.map +1 -1
  66. package/dist/lib/prompts/index.d.ts.map +1 -1
  67. package/dist/lib/prompts/index.js +2 -7
  68. package/dist/lib/prompts/index.js.map +1 -1
  69. package/dist/lib/prompts/restore-tool-spec.d.ts +2 -0
  70. package/dist/lib/prompts/restore-tool-spec.d.ts.map +1 -0
  71. package/dist/lib/prompts/restore-tool-spec.js +37 -0
  72. package/dist/lib/prompts/restore-tool-spec.js.map +1 -0
  73. package/dist/lib/prompts/system/both.d.ts +1 -1
  74. package/dist/lib/prompts/system/both.d.ts.map +1 -1
  75. package/dist/lib/prompts/system/both.js +20 -16
  76. package/dist/lib/prompts/system/both.js.map +1 -1
  77. package/dist/lib/prompts/system/discard.d.ts +1 -1
  78. package/dist/lib/prompts/system/discard.d.ts.map +1 -1
  79. package/dist/lib/prompts/system/discard.js +19 -16
  80. package/dist/lib/prompts/system/discard.js.map +1 -1
  81. package/dist/lib/prompts/system/extract.d.ts +1 -1
  82. package/dist/lib/prompts/system/extract.d.ts.map +1 -1
  83. package/dist/lib/prompts/system/extract.js +19 -16
  84. package/dist/lib/prompts/system/extract.js.map +1 -1
  85. package/dist/lib/protected-file-patterns.js +1 -1
  86. package/dist/lib/protected-file-patterns.js.map +1 -1
  87. package/dist/lib/safe-execute.d.ts +20 -0
  88. package/dist/lib/safe-execute.d.ts.map +1 -0
  89. package/dist/lib/safe-execute.js +38 -0
  90. package/dist/lib/safe-execute.js.map +1 -0
  91. package/dist/lib/shared-utils.js +1 -1
  92. package/dist/lib/shared-utils.js.map +1 -1
  93. package/dist/lib/state/persistence.d.ts +6 -1
  94. package/dist/lib/state/persistence.d.ts.map +1 -1
  95. package/dist/lib/state/persistence.js +59 -1
  96. package/dist/lib/state/persistence.js.map +1 -1
  97. package/dist/lib/state/state.d.ts.map +1 -1
  98. package/dist/lib/state/state.js +54 -3
  99. package/dist/lib/state/state.js.map +1 -1
  100. package/dist/lib/state/tool-cache.d.ts +2 -0
  101. package/dist/lib/state/tool-cache.d.ts.map +1 -1
  102. package/dist/lib/state/tool-cache.js +34 -12
  103. package/dist/lib/state/tool-cache.js.map +1 -1
  104. package/dist/lib/state/types.d.ts +50 -5
  105. package/dist/lib/state/types.d.ts.map +1 -1
  106. package/dist/lib/strategies/deduplication.d.ts +1 -0
  107. package/dist/lib/strategies/deduplication.d.ts.map +1 -1
  108. package/dist/lib/strategies/deduplication.js +87 -3
  109. package/dist/lib/strategies/deduplication.js.map +1 -1
  110. package/dist/lib/strategies/index.d.ts +1 -5
  111. package/dist/lib/strategies/index.d.ts.map +1 -1
  112. package/dist/lib/strategies/index.js +1 -5
  113. package/dist/lib/strategies/index.js.map +1 -1
  114. package/dist/lib/strategies/purge-errors.d.ts.map +1 -1
  115. package/dist/lib/strategies/purge-errors.js +4 -1
  116. package/dist/lib/strategies/purge-errors.js.map +1 -1
  117. package/dist/lib/strategies/supersede-writes.d.ts.map +1 -1
  118. package/dist/lib/strategies/supersede-writes.js +7 -1
  119. package/dist/lib/strategies/supersede-writes.js.map +1 -1
  120. package/dist/lib/strategies/tools.d.ts +1 -0
  121. package/dist/lib/strategies/tools.d.ts.map +1 -1
  122. package/dist/lib/strategies/tools.js +215 -62
  123. package/dist/lib/strategies/tools.js.map +1 -1
  124. package/dist/lib/ui/notification.d.ts +5 -2
  125. package/dist/lib/ui/notification.d.ts.map +1 -1
  126. package/dist/lib/ui/notification.js +10 -6
  127. package/dist/lib/ui/notification.js.map +1 -1
  128. package/dist/lib/ui/utils.d.ts +3 -3
  129. package/dist/lib/ui/utils.d.ts.map +1 -1
  130. package/dist/lib/ui/utils.js +38 -12
  131. package/dist/lib/ui/utils.js.map +1 -1
  132. package/package.json +7 -12
  133. package/dist/lib/prompts/nudge/both.d.ts +0 -2
  134. package/dist/lib/prompts/nudge/both.d.ts.map +0 -1
  135. package/dist/lib/prompts/nudge/both.js +0 -11
  136. package/dist/lib/prompts/nudge/both.js.map +0 -1
  137. package/dist/lib/prompts/nudge/discard.d.ts +0 -2
  138. package/dist/lib/prompts/nudge/discard.d.ts.map +0 -1
  139. package/dist/lib/prompts/nudge/discard.js +0 -10
  140. package/dist/lib/prompts/nudge/discard.js.map +0 -1
  141. package/dist/lib/prompts/nudge/extract.d.ts +0 -2
  142. package/dist/lib/prompts/nudge/extract.d.ts.map +0 -1
  143. package/dist/lib/prompts/nudge/extract.js +0 -10
  144. package/dist/lib/prompts/nudge/extract.js.map +0 -1
  145. package/dist/lib/strategies/head-tail-truncation.d.ts +0 -15
  146. package/dist/lib/strategies/head-tail-truncation.d.ts.map +0 -1
  147. package/dist/lib/strategies/head-tail-truncation.js +0 -144
  148. package/dist/lib/strategies/head-tail-truncation.js.map +0 -1
  149. package/dist/lib/strategies/placeholder-compression.d.ts +0 -5
  150. package/dist/lib/strategies/placeholder-compression.d.ts.map +0 -1
  151. package/dist/lib/strategies/placeholder-compression.js +0 -148
  152. package/dist/lib/strategies/placeholder-compression.js.map +0 -1
  153. package/dist/lib/strategies/prune-thinking.d.ts +0 -15
  154. package/dist/lib/strategies/prune-thinking.d.ts.map +0 -1
  155. package/dist/lib/strategies/prune-thinking.js +0 -79
  156. package/dist/lib/strategies/prune-thinking.js.map +0 -1
  157. package/dist/lib/strategies/read-consolidation.d.ts +0 -21
  158. package/dist/lib/strategies/read-consolidation.d.ts.map +0 -1
  159. package/dist/lib/strategies/read-consolidation.js +0 -155
  160. package/dist/lib/strategies/read-consolidation.js.map +0 -1
@@ -2,7 +2,17 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from "fs
2
2
  import { join, dirname } from "path";
3
3
  import { homedir } from "os";
4
4
  import { parse } from "jsonc-parser";
5
+ import { validateConfig, getUnknownKeys } from "./config-schema";
6
+ /**
7
+ * Tools that should never be pruned.
8
+ *
9
+ * - `context_info`: Synthetic tool used to inject prunable-tools context
10
+ * for DeepSeek/Kimi models. Must be protected to prevent self-pruning
11
+ * which would break the context management feedback loop.
12
+ * See lib/messages/utils.ts createSyntheticToolPart()
13
+ */
5
14
  const DEFAULT_PROTECTED_TOOLS = [
15
+ "context_info", // Synthetic tool for context injection
6
16
  "task",
7
17
  "todowrite",
8
18
  "todoread",
@@ -14,417 +24,26 @@ const DEFAULT_PROTECTED_TOOLS = [
14
24
  "plan_enter",
15
25
  "plan_exit",
16
26
  ];
17
- // Valid config keys for validation against user config
18
- export const VALID_CONFIG_KEYS = new Set([
19
- // Top-level keys
20
- "$schema",
21
- "enabled",
22
- "debug",
23
- "showUpdateToasts", // Deprecated but kept for backwards compatibility
24
- "pruneNotification",
25
- "turnProtection",
26
- "turnProtection.enabled",
27
- "turnProtection.turns",
28
- "protectedFilePatterns",
29
- "commands",
30
- "commands.enabled",
31
- "commands.protectedTools",
32
- "tools",
33
- "tools.settings",
34
- "tools.settings.nudgeEnabled",
35
- "tools.settings.nudgeFrequency",
36
- "tools.settings.protectedTools",
37
- "tools.discard",
38
- "tools.discard.enabled",
39
- "tools.extract",
40
- "tools.extract.enabled",
41
- "tools.extract.showDistillation",
42
- "strategies",
43
- // strategies.deduplication
44
- "strategies.deduplication",
45
- "strategies.deduplication.enabled",
46
- "strategies.deduplication.protectedTools",
47
- // strategies.supersedeWrites
48
- "strategies.supersedeWrites",
49
- "strategies.supersedeWrites.enabled",
50
- // strategies.purgeErrors
51
- "strategies.purgeErrors",
52
- "strategies.purgeErrors.enabled",
53
- "strategies.purgeErrors.turns",
54
- "strategies.purgeErrors.protectedTools",
55
- // strategies.pruneThinking
56
- "strategies.pruneThinking",
57
- "strategies.pruneThinking.enabled",
58
- "strategies.pruneThinking.delayTurns",
59
- // strategies.placeholderCompression
60
- "strategies.placeholderCompression",
61
- "strategies.placeholderCompression.enabled",
62
- "strategies.placeholderCompression.delayTurns",
63
- "strategies.placeholderCompression.minOutputTokens",
64
- "strategies.placeholderCompression.protectedTools",
65
- // strategies.headTailTruncation
66
- "strategies.headTailTruncation",
67
- "strategies.headTailTruncation.enabled",
68
- "strategies.headTailTruncation.delayTurns",
69
- "strategies.headTailTruncation.headRatio",
70
- "strategies.headTailTruncation.tailRatio",
71
- "strategies.headTailTruncation.protectedTools",
72
- // strategies.readConsolidation
73
- "strategies.readConsolidation",
74
- "strategies.readConsolidation.enabled",
75
- "strategies.readConsolidation.tools",
76
- ]);
77
- // Extract all key paths from a config object for validation
78
- function getConfigKeyPaths(obj, prefix = "") {
79
- const keys = [];
80
- for (const key of Object.keys(obj)) {
81
- const fullKey = prefix ? `${prefix}.${key}` : key;
82
- keys.push(fullKey);
83
- if (obj[key] && typeof obj[key] === "object" && !Array.isArray(obj[key])) {
84
- keys.push(...getConfigKeyPaths(obj[key], fullKey));
85
- }
86
- }
87
- return keys;
88
- }
89
- // Returns invalid keys found in user config
90
- export function getInvalidConfigKeys(userConfig) {
91
- const userKeys = getConfigKeyPaths(userConfig);
92
- return userKeys.filter((key) => !VALID_CONFIG_KEYS.has(key));
93
- }
94
- function validateConfigTypes(config) {
95
- const errors = [];
96
- // Top-level validators
97
- if (config.enabled !== undefined && typeof config.enabled !== "boolean") {
98
- errors.push({ key: "enabled", expected: "boolean", actual: typeof config.enabled });
99
- }
100
- if (config.debug !== undefined && typeof config.debug !== "boolean") {
101
- errors.push({ key: "debug", expected: "boolean", actual: typeof config.debug });
102
- }
103
- if (config.pruneNotification !== undefined) {
104
- const validValues = ["off", "minimal", "detailed"];
105
- if (!validValues.includes(config.pruneNotification)) {
106
- errors.push({
107
- key: "pruneNotification",
108
- expected: '"off" | "minimal" | "detailed"',
109
- actual: JSON.stringify(config.pruneNotification),
110
- });
111
- }
112
- }
113
- if (config.protectedFilePatterns !== undefined) {
114
- if (!Array.isArray(config.protectedFilePatterns)) {
115
- errors.push({
116
- key: "protectedFilePatterns",
117
- expected: "string[]",
118
- actual: typeof config.protectedFilePatterns,
119
- });
120
- }
121
- else if (!config.protectedFilePatterns.every((v) => typeof v === "string")) {
122
- errors.push({
123
- key: "protectedFilePatterns",
124
- expected: "string[]",
125
- actual: "non-string entries",
126
- });
127
- }
128
- }
129
- // Top-level turnProtection validator
130
- if (config.turnProtection) {
131
- if (config.turnProtection.enabled !== undefined &&
132
- typeof config.turnProtection.enabled !== "boolean") {
133
- errors.push({
134
- key: "turnProtection.enabled",
135
- expected: "boolean",
136
- actual: typeof config.turnProtection.enabled,
137
- });
138
- }
139
- if (config.turnProtection.turns !== undefined &&
140
- typeof config.turnProtection.turns !== "number") {
141
- errors.push({
142
- key: "turnProtection.turns",
143
- expected: "number",
144
- actual: typeof config.turnProtection.turns,
145
- });
146
- }
147
- }
148
- // Commands validator
149
- const commands = config.commands;
150
- if (commands !== undefined) {
151
- if (typeof commands === "object") {
152
- if (commands.enabled !== undefined && typeof commands.enabled !== "boolean") {
153
- errors.push({
154
- key: "commands.enabled",
155
- expected: "boolean",
156
- actual: typeof commands.enabled,
157
- });
158
- }
159
- if (commands.protectedTools !== undefined && !Array.isArray(commands.protectedTools)) {
160
- errors.push({
161
- key: "commands.protectedTools",
162
- expected: "string[]",
163
- actual: typeof commands.protectedTools,
164
- });
165
- }
166
- }
167
- else {
168
- errors.push({
169
- key: "commands",
170
- expected: "{ enabled: boolean, protectedTools: string[] }",
171
- actual: typeof commands,
172
- });
173
- }
174
- }
175
- // Tools validators
176
- const tools = config.tools;
177
- if (tools) {
178
- if (tools.settings) {
179
- if (tools.settings.nudgeEnabled !== undefined &&
180
- typeof tools.settings.nudgeEnabled !== "boolean") {
181
- errors.push({
182
- key: "tools.settings.nudgeEnabled",
183
- expected: "boolean",
184
- actual: typeof tools.settings.nudgeEnabled,
185
- });
186
- }
187
- if (tools.settings.nudgeFrequency !== undefined &&
188
- typeof tools.settings.nudgeFrequency !== "number") {
189
- errors.push({
190
- key: "tools.settings.nudgeFrequency",
191
- expected: "number",
192
- actual: typeof tools.settings.nudgeFrequency,
193
- });
194
- }
195
- if (tools.settings.protectedTools !== undefined &&
196
- !Array.isArray(tools.settings.protectedTools)) {
197
- errors.push({
198
- key: "tools.settings.protectedTools",
199
- expected: "string[]",
200
- actual: typeof tools.settings.protectedTools,
201
- });
202
- }
203
- }
204
- if (tools.discard) {
205
- if (tools.discard.enabled !== undefined && typeof tools.discard.enabled !== "boolean") {
206
- errors.push({
207
- key: "tools.discard.enabled",
208
- expected: "boolean",
209
- actual: typeof tools.discard.enabled,
210
- });
211
- }
212
- }
213
- if (tools.extract) {
214
- if (tools.extract.enabled !== undefined && typeof tools.extract.enabled !== "boolean") {
215
- errors.push({
216
- key: "tools.extract.enabled",
217
- expected: "boolean",
218
- actual: typeof tools.extract.enabled,
219
- });
220
- }
221
- if (tools.extract.showDistillation !== undefined &&
222
- typeof tools.extract.showDistillation !== "boolean") {
223
- errors.push({
224
- key: "tools.extract.showDistillation",
225
- expected: "boolean",
226
- actual: typeof tools.extract.showDistillation,
227
- });
228
- }
229
- }
230
- }
231
- // Strategies validators
232
- const strategies = config.strategies;
233
- if (strategies) {
234
- // deduplication
235
- if (strategies.deduplication?.enabled !== undefined &&
236
- typeof strategies.deduplication.enabled !== "boolean") {
237
- errors.push({
238
- key: "strategies.deduplication.enabled",
239
- expected: "boolean",
240
- actual: typeof strategies.deduplication.enabled,
241
- });
242
- }
243
- if (strategies.deduplication?.protectedTools !== undefined &&
244
- !Array.isArray(strategies.deduplication.protectedTools)) {
245
- errors.push({
246
- key: "strategies.deduplication.protectedTools",
247
- expected: "string[]",
248
- actual: typeof strategies.deduplication.protectedTools,
249
- });
250
- }
251
- // supersedeWrites
252
- if (strategies.supersedeWrites) {
253
- if (strategies.supersedeWrites.enabled !== undefined &&
254
- typeof strategies.supersedeWrites.enabled !== "boolean") {
255
- errors.push({
256
- key: "strategies.supersedeWrites.enabled",
257
- expected: "boolean",
258
- actual: typeof strategies.supersedeWrites.enabled,
259
- });
260
- }
261
- }
262
- // purgeErrors
263
- if (strategies.purgeErrors) {
264
- if (strategies.purgeErrors.enabled !== undefined &&
265
- typeof strategies.purgeErrors.enabled !== "boolean") {
266
- errors.push({
267
- key: "strategies.purgeErrors.enabled",
268
- expected: "boolean",
269
- actual: typeof strategies.purgeErrors.enabled,
270
- });
271
- }
272
- if (strategies.purgeErrors.turns !== undefined &&
273
- typeof strategies.purgeErrors.turns !== "number") {
274
- errors.push({
275
- key: "strategies.purgeErrors.turns",
276
- expected: "number",
277
- actual: typeof strategies.purgeErrors.turns,
278
- });
279
- }
280
- if (strategies.purgeErrors.protectedTools !== undefined &&
281
- !Array.isArray(strategies.purgeErrors.protectedTools)) {
282
- errors.push({
283
- key: "strategies.purgeErrors.protectedTools",
284
- expected: "string[]",
285
- actual: typeof strategies.purgeErrors.protectedTools,
286
- });
287
- }
288
- }
289
- // pruneThinking
290
- if (strategies.pruneThinking) {
291
- if (strategies.pruneThinking.enabled !== undefined &&
292
- typeof strategies.pruneThinking.enabled !== "boolean") {
293
- errors.push({
294
- key: "strategies.pruneThinking.enabled",
295
- expected: "boolean",
296
- actual: typeof strategies.pruneThinking.enabled,
297
- });
298
- }
299
- if (strategies.pruneThinking.delayTurns !== undefined &&
300
- typeof strategies.pruneThinking.delayTurns !== "number") {
301
- errors.push({
302
- key: "strategies.pruneThinking.delayTurns",
303
- expected: "number",
304
- actual: typeof strategies.pruneThinking.delayTurns,
305
- });
306
- }
307
- }
308
- // placeholderCompression
309
- if (strategies.placeholderCompression) {
310
- if (strategies.placeholderCompression.enabled !== undefined &&
311
- typeof strategies.placeholderCompression.enabled !== "boolean") {
312
- errors.push({
313
- key: "strategies.placeholderCompression.enabled",
314
- expected: "boolean",
315
- actual: typeof strategies.placeholderCompression.enabled,
316
- });
317
- }
318
- if (strategies.placeholderCompression.delayTurns !== undefined &&
319
- typeof strategies.placeholderCompression.delayTurns !== "number") {
320
- errors.push({
321
- key: "strategies.placeholderCompression.delayTurns",
322
- expected: "number",
323
- actual: typeof strategies.placeholderCompression.delayTurns,
324
- });
325
- }
326
- if (strategies.placeholderCompression.minOutputTokens !== undefined &&
327
- typeof strategies.placeholderCompression.minOutputTokens !== "number") {
328
- errors.push({
329
- key: "strategies.placeholderCompression.minOutputTokens",
330
- expected: "number",
331
- actual: typeof strategies.placeholderCompression.minOutputTokens,
332
- });
333
- }
334
- if (strategies.placeholderCompression.protectedTools !== undefined &&
335
- !Array.isArray(strategies.placeholderCompression.protectedTools)) {
336
- errors.push({
337
- key: "strategies.placeholderCompression.protectedTools",
338
- expected: "string[]",
339
- actual: typeof strategies.placeholderCompression.protectedTools,
340
- });
341
- }
342
- }
343
- // headTailTruncation
344
- if (strategies.headTailTruncation) {
345
- if (strategies.headTailTruncation.enabled !== undefined &&
346
- typeof strategies.headTailTruncation.enabled !== "boolean") {
347
- errors.push({
348
- key: "strategies.headTailTruncation.enabled",
349
- expected: "boolean",
350
- actual: typeof strategies.headTailTruncation.enabled,
351
- });
352
- }
353
- if (strategies.headTailTruncation.delayTurns !== undefined &&
354
- typeof strategies.headTailTruncation.delayTurns !== "number") {
355
- errors.push({
356
- key: "strategies.headTailTruncation.delayTurns",
357
- expected: "number",
358
- actual: typeof strategies.headTailTruncation.delayTurns,
359
- });
360
- }
361
- if (strategies.headTailTruncation.headRatio !== undefined &&
362
- typeof strategies.headTailTruncation.headRatio !== "number") {
363
- errors.push({
364
- key: "strategies.headTailTruncation.headRatio",
365
- expected: "number",
366
- actual: typeof strategies.headTailTruncation.headRatio,
367
- });
368
- }
369
- if (strategies.headTailTruncation.tailRatio !== undefined &&
370
- typeof strategies.headTailTruncation.tailRatio !== "number") {
371
- errors.push({
372
- key: "strategies.headTailTruncation.tailRatio",
373
- expected: "number",
374
- actual: typeof strategies.headTailTruncation.tailRatio,
375
- });
376
- }
377
- if (strategies.headTailTruncation.protectedTools !== undefined &&
378
- !Array.isArray(strategies.headTailTruncation.protectedTools)) {
379
- errors.push({
380
- key: "strategies.headTailTruncation.protectedTools",
381
- expected: "string[]",
382
- actual: typeof strategies.headTailTruncation.protectedTools,
383
- });
384
- }
385
- }
386
- // readConsolidation
387
- if (strategies.readConsolidation) {
388
- if (strategies.readConsolidation.enabled !== undefined &&
389
- typeof strategies.readConsolidation.enabled !== "boolean") {
390
- errors.push({
391
- key: "strategies.readConsolidation.enabled",
392
- expected: "boolean",
393
- actual: typeof strategies.readConsolidation.enabled,
394
- });
395
- }
396
- if (strategies.readConsolidation.tools !== undefined &&
397
- !Array.isArray(strategies.readConsolidation.tools)) {
398
- errors.push({
399
- key: "strategies.readConsolidation.tools",
400
- expected: "string[]",
401
- actual: typeof strategies.readConsolidation.tools,
402
- });
403
- }
404
- }
405
- }
406
- return errors;
407
- }
408
- // Show validation warnings for a config file
27
+ // Show validation warnings for a config file using Zod schema
409
28
  function showConfigValidationWarnings(ctx, configPath, configData, isProject) {
410
- const invalidKeys = getInvalidConfigKeys(configData);
411
- const typeErrors = validateConfigTypes(configData);
412
- if (invalidKeys.length === 0 && typeErrors.length === 0) {
29
+ const validationErrors = validateConfig(configData);
30
+ const unknownKeys = getUnknownKeys(configData);
31
+ if (validationErrors.length === 0 && unknownKeys.length === 0) {
413
32
  return;
414
33
  }
415
34
  const configType = isProject ? "project config" : "config";
416
35
  const messages = [];
417
- if (invalidKeys.length > 0) {
418
- const keyList = invalidKeys.slice(0, 3).join(", ");
419
- const suffix = invalidKeys.length > 3 ? ` (+${invalidKeys.length - 3} more)` : "";
36
+ if (unknownKeys.length > 0) {
37
+ const keyList = unknownKeys.slice(0, 3).join(", ");
38
+ const suffix = unknownKeys.length > 3 ? ` (+${unknownKeys.length - 3} more)` : "";
420
39
  messages.push(`Unknown keys: ${keyList}${suffix}`);
421
40
  }
422
- if (typeErrors.length > 0) {
423
- for (const err of typeErrors.slice(0, 2)) {
424
- messages.push(`${err.key}: expected ${err.expected}, got ${err.actual}`);
41
+ if (validationErrors.length > 0) {
42
+ for (const err of validationErrors.slice(0, 2)) {
43
+ messages.push(err);
425
44
  }
426
- if (typeErrors.length > 2) {
427
- messages.push(`(+${typeErrors.length - 2} more type errors)`);
45
+ if (validationErrors.length > 2) {
46
+ messages.push(`(+${validationErrors.length - 2} more validation errors)`);
428
47
  }
429
48
  }
430
49
  setTimeout(() => {
@@ -441,10 +60,11 @@ function showConfigValidationWarnings(ctx, configPath, configData, isProject) {
441
60
  catch { }
442
61
  }, 7000);
443
62
  }
444
- export const DEFAULT_CONFIG = {
63
+ const defaultConfig = {
445
64
  enabled: true,
446
65
  debug: false,
447
66
  pruneNotification: "detailed",
67
+ autoPruneAfterTool: true,
448
68
  commands: {
449
69
  enabled: true,
450
70
  protectedTools: [...DEFAULT_PROTECTED_TOOLS],
@@ -453,11 +73,22 @@ export const DEFAULT_CONFIG = {
453
73
  enabled: false,
454
74
  turns: 4,
455
75
  },
456
- protectedFilePatterns: [],
76
+ protectedFilePatterns: [
77
+ // Environment and secrets
78
+ "**/.env",
79
+ "**/.env.*",
80
+ "**/credentials.json",
81
+ "**/secrets.json",
82
+ "**/*.pem",
83
+ "**/*.key",
84
+ // Config files users likely want to keep visible
85
+ "**/package.json",
86
+ "**/tsconfig.json",
87
+ "**/pyproject.toml",
88
+ "**/Cargo.toml",
89
+ ],
457
90
  tools: {
458
91
  settings: {
459
- nudgeEnabled: true,
460
- nudgeFrequency: 5,
461
92
  protectedTools: [...DEFAULT_PROTECTED_TOOLS],
462
93
  },
463
94
  discard: {
@@ -474,34 +105,13 @@ export const DEFAULT_CONFIG = {
474
105
  protectedTools: [],
475
106
  },
476
107
  supersedeWrites: {
477
- enabled: true,
478
- },
479
- purgeErrors: {
480
- enabled: true,
481
- turns: 2,
482
- protectedTools: [],
483
- },
484
- pruneThinking: {
485
- enabled: true,
486
- delayTurns: 1,
487
- },
488
- placeholderCompression: {
489
108
  enabled: false,
490
- delayTurns: 2,
491
- minOutputTokens: 100,
492
- protectedTools: [],
493
109
  },
494
- headTailTruncation: {
110
+ purgeErrors: {
495
111
  enabled: true,
496
- delayTurns: 2,
497
- headRatio: 0.2,
498
- tailRatio: 0.3,
112
+ turns: 4,
499
113
  protectedTools: [],
500
114
  },
501
- readConsolidation: {
502
- enabled: true,
503
- tools: ["read", "glob", "grep", "webfetch", "bash"],
504
- },
505
115
  },
506
116
  };
507
117
  const GLOBAL_CONFIG_DIR = join(homedir(), ".config", "opencode");
@@ -565,7 +175,7 @@ function createDefaultConfig() {
565
175
  mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
566
176
  }
567
177
  const configContent = `{
568
- "$schema": "https://raw.githubusercontent.com/tuanhung303/opencode-agent-context-pruning/master/acp.schema.json"
178
+ "$schema": "https://raw.githubusercontent.com/opencode-acp/opencode-acp/master/acp.schema.json"
569
179
  }
570
180
  `;
571
181
  writeFileSync(GLOBAL_CONFIG_PATH_JSONC, configContent, "utf-8");
@@ -616,39 +226,6 @@ function mergeStrategies(base, override) {
616
226
  ]),
617
227
  ],
618
228
  },
619
- pruneThinking: {
620
- enabled: override.pruneThinking?.enabled ?? base.pruneThinking.enabled,
621
- delayTurns: override.pruneThinking?.delayTurns ?? base.pruneThinking.delayTurns,
622
- },
623
- placeholderCompression: {
624
- enabled: override.placeholderCompression?.enabled ?? base.placeholderCompression.enabled,
625
- delayTurns: override.placeholderCompression?.delayTurns ??
626
- base.placeholderCompression.delayTurns,
627
- minOutputTokens: override.placeholderCompression?.minOutputTokens ??
628
- base.placeholderCompression.minOutputTokens,
629
- protectedTools: [
630
- ...new Set([
631
- ...base.placeholderCompression.protectedTools,
632
- ...(override.placeholderCompression?.protectedTools ?? []),
633
- ]),
634
- ],
635
- },
636
- headTailTruncation: {
637
- enabled: override.headTailTruncation?.enabled ?? base.headTailTruncation.enabled,
638
- delayTurns: override.headTailTruncation?.delayTurns ?? base.headTailTruncation.delayTurns,
639
- headRatio: override.headTailTruncation?.headRatio ?? base.headTailTruncation.headRatio,
640
- tailRatio: override.headTailTruncation?.tailRatio ?? base.headTailTruncation.tailRatio,
641
- protectedTools: [
642
- ...new Set([
643
- ...base.headTailTruncation.protectedTools,
644
- ...(override.headTailTruncation?.protectedTools ?? []),
645
- ]),
646
- ],
647
- },
648
- readConsolidation: {
649
- enabled: override.readConsolidation?.enabled ?? base.readConsolidation.enabled,
650
- tools: override.readConsolidation?.tools ?? base.readConsolidation.tools,
651
- },
652
229
  };
653
230
  }
654
231
  function mergeTools(base, override) {
@@ -656,8 +233,6 @@ function mergeTools(base, override) {
656
233
  return base;
657
234
  return {
658
235
  settings: {
659
- nudgeEnabled: override.settings?.nudgeEnabled ?? base.settings.nudgeEnabled,
660
- nudgeFrequency: override.settings?.nudgeFrequency ?? base.settings.nudgeFrequency,
661
236
  protectedTools: [
662
237
  ...new Set([
663
238
  ...base.settings.protectedTools,
@@ -693,7 +268,6 @@ function deepCloneConfig(config) {
693
268
  protectedFilePatterns: [...config.protectedFilePatterns],
694
269
  tools: {
695
270
  settings: {
696
- ...config.tools.settings,
697
271
  protectedTools: [...config.tools.settings.protectedTools],
698
272
  },
699
273
  discard: { ...config.tools.discard },
@@ -711,26 +285,11 @@ function deepCloneConfig(config) {
711
285
  ...config.strategies.purgeErrors,
712
286
  protectedTools: [...config.strategies.purgeErrors.protectedTools],
713
287
  },
714
- pruneThinking: {
715
- ...config.strategies.pruneThinking,
716
- },
717
- placeholderCompression: {
718
- ...config.strategies.placeholderCompression,
719
- protectedTools: [...config.strategies.placeholderCompression.protectedTools],
720
- },
721
- headTailTruncation: {
722
- ...config.strategies.headTailTruncation,
723
- protectedTools: [...config.strategies.headTailTruncation.protectedTools],
724
- },
725
- readConsolidation: {
726
- ...config.strategies.readConsolidation,
727
- tools: [...config.strategies.readConsolidation.tools],
728
- },
729
288
  },
730
289
  };
731
290
  }
732
291
  export function getConfig(ctx) {
733
- let config = deepCloneConfig(DEFAULT_CONFIG);
292
+ let config = deepCloneConfig(defaultConfig);
734
293
  const configPaths = getConfigPaths(ctx);
735
294
  // Load and merge global config
736
295
  if (configPaths.global) {
@@ -757,6 +316,7 @@ export function getConfig(ctx) {
757
316
  enabled: result.data.enabled ?? config.enabled,
758
317
  debug: result.data.debug ?? config.debug,
759
318
  pruneNotification: result.data.pruneNotification ?? config.pruneNotification,
319
+ autoPruneAfterTool: result.data.autoPruneAfterTool ?? config.autoPruneAfterTool,
760
320
  commands: mergeCommands(config.commands, result.data.commands),
761
321
  turnProtection: {
762
322
  enabled: result.data.turnProtection?.enabled ?? config.turnProtection.enabled,
@@ -802,6 +362,7 @@ export function getConfig(ctx) {
802
362
  enabled: result.data.enabled ?? config.enabled,
803
363
  debug: result.data.debug ?? config.debug,
804
364
  pruneNotification: result.data.pruneNotification ?? config.pruneNotification,
365
+ autoPruneAfterTool: result.data.autoPruneAfterTool ?? config.autoPruneAfterTool,
805
366
  commands: mergeCommands(config.commands, result.data.commands),
806
367
  turnProtection: {
807
368
  enabled: result.data.turnProtection?.enabled ?? config.turnProtection.enabled,
@@ -843,6 +404,7 @@ export function getConfig(ctx) {
843
404
  enabled: result.data.enabled ?? config.enabled,
844
405
  debug: result.data.debug ?? config.debug,
845
406
  pruneNotification: result.data.pruneNotification ?? config.pruneNotification,
407
+ autoPruneAfterTool: result.data.autoPruneAfterTool ?? config.autoPruneAfterTool,
846
408
  commands: mergeCommands(config.commands, result.data.commands),
847
409
  turnProtection: {
848
410
  enabled: result.data.turnProtection?.enabled ?? config.turnProtection.enabled,