@tuanhung303/opencode-acp 2.1.1 → 2.2.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 (162) hide show
  1. package/README.md +131 -71
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +13 -5
  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 +2 -2
  13. package/dist/lib/commands/help.d.ts.map +1 -1
  14. package/dist/lib/commands/help.js +8 -6
  15. package/dist/lib/commands/help.js.map +1 -1
  16. package/dist/lib/commands/protected.d.ts +17 -0
  17. package/dist/lib/commands/protected.d.ts.map +1 -0
  18. package/dist/lib/commands/protected.js +50 -0
  19. package/dist/lib/commands/protected.js.map +1 -0
  20. package/dist/lib/commands/stats.d.ts +1 -1
  21. package/dist/lib/commands/stats.d.ts.map +1 -1
  22. package/dist/lib/commands/stats.js +24 -4
  23. package/dist/lib/commands/stats.js.map +1 -1
  24. package/dist/lib/commands/sweep.d.ts +3 -3
  25. package/dist/lib/commands/sweep.d.ts.map +1 -1
  26. package/dist/lib/commands/sweep.js +10 -7
  27. package/dist/lib/commands/sweep.js.map +1 -1
  28. package/dist/lib/config-schema.d.ts +119 -0
  29. package/dist/lib/config-schema.d.ts.map +1 -0
  30. package/dist/lib/config-schema.js +97 -0
  31. package/dist/lib/config-schema.js.map +1 -0
  32. package/dist/lib/config.d.ts +7 -30
  33. package/dist/lib/config.d.ts.map +1 -1
  34. package/dist/lib/config.js +59 -497
  35. package/dist/lib/config.js.map +1 -1
  36. package/dist/lib/hooks.d.ts +14 -0
  37. package/dist/lib/hooks.d.ts.map +1 -1
  38. package/dist/lib/hooks.js +126 -19
  39. package/dist/lib/hooks.js.map +1 -1
  40. package/dist/lib/logger.d.ts +25 -1
  41. package/dist/lib/logger.d.ts.map +1 -1
  42. package/dist/lib/logger.js +46 -5
  43. package/dist/lib/logger.js.map +1 -1
  44. package/dist/lib/messages/index.d.ts +1 -1
  45. package/dist/lib/messages/index.d.ts.map +1 -1
  46. package/dist/lib/messages/index.js +1 -1
  47. package/dist/lib/messages/index.js.map +1 -1
  48. package/dist/lib/messages/inject.d.ts +6 -1
  49. package/dist/lib/messages/inject.d.ts.map +1 -1
  50. package/dist/lib/messages/inject.js +8 -125
  51. package/dist/lib/messages/inject.js.map +1 -1
  52. package/dist/lib/messages/prune.d.ts +8 -0
  53. package/dist/lib/messages/prune.d.ts.map +1 -1
  54. package/dist/lib/messages/prune.js +89 -2
  55. package/dist/lib/messages/prune.js.map +1 -1
  56. package/dist/lib/messages/utils.d.ts +12 -0
  57. package/dist/lib/messages/utils.d.ts.map +1 -1
  58. package/dist/lib/messages/utils.js +34 -0
  59. package/dist/lib/messages/utils.js.map +1 -1
  60. package/dist/lib/prompts/discard-tool-spec.d.ts +1 -1
  61. package/dist/lib/prompts/discard-tool-spec.d.ts.map +1 -1
  62. package/dist/lib/prompts/discard-tool-spec.js +30 -17
  63. package/dist/lib/prompts/discard-tool-spec.js.map +1 -1
  64. package/dist/lib/prompts/extract-tool-spec.d.ts +1 -1
  65. package/dist/lib/prompts/extract-tool-spec.d.ts.map +1 -1
  66. package/dist/lib/prompts/extract-tool-spec.js +19 -11
  67. package/dist/lib/prompts/extract-tool-spec.js.map +1 -1
  68. package/dist/lib/prompts/index.d.ts.map +1 -1
  69. package/dist/lib/prompts/index.js +2 -7
  70. package/dist/lib/prompts/index.js.map +1 -1
  71. package/dist/lib/prompts/restore-tool-spec.d.ts +2 -0
  72. package/dist/lib/prompts/restore-tool-spec.d.ts.map +1 -0
  73. package/dist/lib/prompts/restore-tool-spec.js +37 -0
  74. package/dist/lib/prompts/restore-tool-spec.js.map +1 -0
  75. package/dist/lib/prompts/system/both.d.ts +1 -1
  76. package/dist/lib/prompts/system/both.d.ts.map +1 -1
  77. package/dist/lib/prompts/system/both.js +20 -16
  78. package/dist/lib/prompts/system/both.js.map +1 -1
  79. package/dist/lib/prompts/system/discard.d.ts +1 -1
  80. package/dist/lib/prompts/system/discard.d.ts.map +1 -1
  81. package/dist/lib/prompts/system/discard.js +19 -16
  82. package/dist/lib/prompts/system/discard.js.map +1 -1
  83. package/dist/lib/prompts/system/extract.d.ts +1 -1
  84. package/dist/lib/prompts/system/extract.d.ts.map +1 -1
  85. package/dist/lib/prompts/system/extract.js +19 -16
  86. package/dist/lib/prompts/system/extract.js.map +1 -1
  87. package/dist/lib/protected-file-patterns.js +1 -1
  88. package/dist/lib/protected-file-patterns.js.map +1 -1
  89. package/dist/lib/safe-execute.d.ts +20 -0
  90. package/dist/lib/safe-execute.d.ts.map +1 -0
  91. package/dist/lib/safe-execute.js +38 -0
  92. package/dist/lib/safe-execute.js.map +1 -0
  93. package/dist/lib/shared-utils.js +1 -1
  94. package/dist/lib/shared-utils.js.map +1 -1
  95. package/dist/lib/state/persistence.d.ts +8 -3
  96. package/dist/lib/state/persistence.d.ts.map +1 -1
  97. package/dist/lib/state/persistence.js +62 -4
  98. package/dist/lib/state/persistence.js.map +1 -1
  99. package/dist/lib/state/state.d.ts.map +1 -1
  100. package/dist/lib/state/state.js +54 -3
  101. package/dist/lib/state/state.js.map +1 -1
  102. package/dist/lib/state/tool-cache.d.ts +2 -0
  103. package/dist/lib/state/tool-cache.d.ts.map +1 -1
  104. package/dist/lib/state/tool-cache.js +34 -12
  105. package/dist/lib/state/tool-cache.js.map +1 -1
  106. package/dist/lib/state/types.d.ts +50 -5
  107. package/dist/lib/state/types.d.ts.map +1 -1
  108. package/dist/lib/strategies/deduplication.d.ts +1 -0
  109. package/dist/lib/strategies/deduplication.d.ts.map +1 -1
  110. package/dist/lib/strategies/deduplication.js +87 -3
  111. package/dist/lib/strategies/deduplication.js.map +1 -1
  112. package/dist/lib/strategies/index.d.ts +1 -5
  113. package/dist/lib/strategies/index.d.ts.map +1 -1
  114. package/dist/lib/strategies/index.js +1 -5
  115. package/dist/lib/strategies/index.js.map +1 -1
  116. package/dist/lib/strategies/purge-errors.d.ts.map +1 -1
  117. package/dist/lib/strategies/purge-errors.js +4 -1
  118. package/dist/lib/strategies/purge-errors.js.map +1 -1
  119. package/dist/lib/strategies/supersede-writes.d.ts.map +1 -1
  120. package/dist/lib/strategies/supersede-writes.js +7 -1
  121. package/dist/lib/strategies/supersede-writes.js.map +1 -1
  122. package/dist/lib/strategies/tools.d.ts +1 -0
  123. package/dist/lib/strategies/tools.d.ts.map +1 -1
  124. package/dist/lib/strategies/tools.js +215 -62
  125. package/dist/lib/strategies/tools.js.map +1 -1
  126. package/dist/lib/ui/notification.d.ts +5 -2
  127. package/dist/lib/ui/notification.d.ts.map +1 -1
  128. package/dist/lib/ui/notification.js +10 -6
  129. package/dist/lib/ui/notification.js.map +1 -1
  130. package/dist/lib/ui/utils.d.ts +3 -3
  131. package/dist/lib/ui/utils.d.ts.map +1 -1
  132. package/dist/lib/ui/utils.js +38 -12
  133. package/dist/lib/ui/utils.js.map +1 -1
  134. package/package.json +7 -12
  135. package/dist/lib/prompts/nudge/both.d.ts +0 -2
  136. package/dist/lib/prompts/nudge/both.d.ts.map +0 -1
  137. package/dist/lib/prompts/nudge/both.js +0 -11
  138. package/dist/lib/prompts/nudge/both.js.map +0 -1
  139. package/dist/lib/prompts/nudge/discard.d.ts +0 -2
  140. package/dist/lib/prompts/nudge/discard.d.ts.map +0 -1
  141. package/dist/lib/prompts/nudge/discard.js +0 -10
  142. package/dist/lib/prompts/nudge/discard.js.map +0 -1
  143. package/dist/lib/prompts/nudge/extract.d.ts +0 -2
  144. package/dist/lib/prompts/nudge/extract.d.ts.map +0 -1
  145. package/dist/lib/prompts/nudge/extract.js +0 -10
  146. package/dist/lib/prompts/nudge/extract.js.map +0 -1
  147. package/dist/lib/strategies/head-tail-truncation.d.ts +0 -15
  148. package/dist/lib/strategies/head-tail-truncation.d.ts.map +0 -1
  149. package/dist/lib/strategies/head-tail-truncation.js +0 -144
  150. package/dist/lib/strategies/head-tail-truncation.js.map +0 -1
  151. package/dist/lib/strategies/placeholder-compression.d.ts +0 -5
  152. package/dist/lib/strategies/placeholder-compression.d.ts.map +0 -1
  153. package/dist/lib/strategies/placeholder-compression.js +0 -148
  154. package/dist/lib/strategies/placeholder-compression.js.map +0 -1
  155. package/dist/lib/strategies/prune-thinking.d.ts +0 -15
  156. package/dist/lib/strategies/prune-thinking.d.ts.map +0 -1
  157. package/dist/lib/strategies/prune-thinking.js +0 -79
  158. package/dist/lib/strategies/prune-thinking.js.map +0 -1
  159. package/dist/lib/strategies/read-consolidation.d.ts +0 -21
  160. package/dist/lib/strategies/read-consolidation.d.ts.map +0 -1
  161. package/dist/lib/strategies/read-consolidation.js +0 -155
  162. 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,424 +24,33 @@ 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(() => {
431
50
  try {
432
51
  ctx.client.tui.showToast({
433
52
  body: {
434
- title: `DCP: Invalid ${configType}`,
53
+ title: `ACP: Invalid ${configType}`,
435
54
  message: `${configPath}\n${messages.join("\n")}`,
436
55
  variant: "warning",
437
56
  duration: 7000,
@@ -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,39 +105,18 @@ 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");
508
- const GLOBAL_CONFIG_PATH_JSONC = join(GLOBAL_CONFIG_DIR, "dcp.jsonc");
509
- const GLOBAL_CONFIG_PATH_JSON = join(GLOBAL_CONFIG_DIR, "dcp.json");
118
+ const GLOBAL_CONFIG_PATH_JSONC = join(GLOBAL_CONFIG_DIR, "acp.jsonc");
119
+ const GLOBAL_CONFIG_PATH_JSON = join(GLOBAL_CONFIG_DIR, "acp.json");
510
120
  function findOpencodeDir(startDir) {
511
121
  let current = startDir;
512
122
  while (current !== "/") {
@@ -522,7 +132,7 @@ function findOpencodeDir(startDir) {
522
132
  return null;
523
133
  }
524
134
  function getConfigPaths(ctx) {
525
- // Global: ~/.config/opencode/dcp.jsonc|json
135
+ // Global: ~/.config/opencode/acp.jsonc|json
526
136
  let globalPath = null;
527
137
  if (existsSync(GLOBAL_CONFIG_PATH_JSONC)) {
528
138
  globalPath = GLOBAL_CONFIG_PATH_JSONC;
@@ -530,12 +140,12 @@ function getConfigPaths(ctx) {
530
140
  else if (existsSync(GLOBAL_CONFIG_PATH_JSON)) {
531
141
  globalPath = GLOBAL_CONFIG_PATH_JSON;
532
142
  }
533
- // Custom config directory: $OPENCODE_CONFIG_DIR/dcp.jsonc|json
143
+ // Custom config directory: $OPENCODE_CONFIG_DIR/acp.jsonc|json
534
144
  let configDirPath = null;
535
145
  const opencodeConfigDir = process.env.OPENCODE_CONFIG_DIR;
536
146
  if (opencodeConfigDir) {
537
- const configJsonc = join(opencodeConfigDir, "dcp.jsonc");
538
- const configJson = join(opencodeConfigDir, "dcp.json");
147
+ const configJsonc = join(opencodeConfigDir, "acp.jsonc");
148
+ const configJson = join(opencodeConfigDir, "acp.json");
539
149
  if (existsSync(configJsonc)) {
540
150
  configDirPath = configJsonc;
541
151
  }
@@ -543,13 +153,13 @@ function getConfigPaths(ctx) {
543
153
  configDirPath = configJson;
544
154
  }
545
155
  }
546
- // Project: <project>/.opencode/dcp.jsonc|json
156
+ // Project: <project>/.opencode/acp.jsonc|json
547
157
  let projectPath = null;
548
158
  if (ctx?.directory) {
549
159
  const opencodeDir = findOpencodeDir(ctx.directory);
550
160
  if (opencodeDir) {
551
- const projectJsonc = join(opencodeDir, "dcp.jsonc");
552
- const projectJson = join(opencodeDir, "dcp.json");
161
+ const projectJsonc = join(opencodeDir, "acp.jsonc");
162
+ const projectJson = join(opencodeDir, "acp.json");
553
163
  if (existsSync(projectJsonc)) {
554
164
  projectPath = projectJsonc;
555
165
  }
@@ -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/Opencode-DCP/opencode-dynamic-context-pruning/master/dcp.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) {
@@ -740,7 +299,7 @@ export function getConfig(ctx) {
740
299
  try {
741
300
  ctx.client.tui.showToast({
742
301
  body: {
743
- title: "DCP: Invalid config",
302
+ title: "ACP: Invalid config",
744
303
  message: `${configPaths.global}\n${result.parseError}\nUsing default values`,
745
304
  variant: "warning",
746
305
  duration: 7000,
@@ -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,
@@ -777,7 +337,7 @@ export function getConfig(ctx) {
777
337
  // No config exists, create default
778
338
  createDefaultConfig();
779
339
  }
780
- // Load and merge $OPENCODE_CONFIG_DIR/dcp.jsonc|json (overrides global)
340
+ // Load and merge $OPENCODE_CONFIG_DIR/acp.jsonc|json (overrides global)
781
341
  if (configPaths.configDir) {
782
342
  const result = loadConfigFile(configPaths.configDir);
783
343
  if (result.parseError) {
@@ -785,7 +345,7 @@ export function getConfig(ctx) {
785
345
  try {
786
346
  ctx.client.tui.showToast({
787
347
  body: {
788
- title: "DCP: Invalid configDir config",
348
+ title: "ACP: Invalid configDir config",
789
349
  message: `${configPaths.configDir}\n${result.parseError}\nUsing global/default values`,
790
350
  variant: "warning",
791
351
  duration: 7000,
@@ -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,
@@ -826,7 +387,7 @@ export function getConfig(ctx) {
826
387
  try {
827
388
  ctx.client.tui.showToast({
828
389
  body: {
829
- title: "DCP: Invalid project config",
390
+ title: "ACP: Invalid project config",
830
391
  message: `${configPaths.project}\n${result.parseError}\nUsing global/default values`,
831
392
  variant: "warning",
832
393
  duration: 7000,
@@ -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,