@tarquinen/opencode-dcp 0.3.16 → 0.3.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -43
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +1 -15
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +22 -95
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/deduplicator.d.ts +0 -30
- package/dist/lib/deduplicator.d.ts.map +1 -1
- package/dist/lib/deduplicator.js +1 -62
- package/dist/lib/deduplicator.js.map +1 -1
- package/dist/lib/janitor.d.ts +2 -61
- package/dist/lib/janitor.d.ts.map +1 -1
- package/dist/lib/janitor.js +48 -193
- package/dist/lib/janitor.js.map +1 -1
- package/dist/lib/logger.d.ts +0 -24
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +3 -58
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/model-selector.d.ts +0 -31
- package/dist/lib/model-selector.d.ts.map +1 -1
- package/dist/lib/model-selector.js +0 -55
- package/dist/lib/model-selector.js.map +1 -1
- package/dist/lib/prompt.d.ts.map +1 -1
- package/dist/lib/prompt.js +3 -29
- package/dist/lib/prompt.js.map +1 -1
- package/dist/lib/tokenizer.d.ts +0 -23
- package/dist/lib/tokenizer.d.ts.map +1 -1
- package/dist/lib/tokenizer.js +0 -25
- package/dist/lib/tokenizer.js.map +1 -1
- package/dist/lib/version-checker.d.ts +0 -15
- package/dist/lib/version-checker.d.ts.map +1 -1
- package/dist/lib/version-checker.js +1 -19
- package/dist/lib/version-checker.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,16 +6,27 @@ Automatically reduces token usage in OpenCode by removing obsolete tool outputs
|
|
|
6
6
|
|
|
7
7
|

|
|
8
8
|
|
|
9
|
+
## Pruning Strategies
|
|
10
|
+
|
|
11
|
+
DCP implements two complementary strategies:
|
|
12
|
+
|
|
13
|
+
**Deduplication** — Fast, zero-cost pruning that identifies repeated tool calls (e.g., reading the same file multiple times) and keeps only the most recent output. Runs instantly with no LLM calls.
|
|
14
|
+
|
|
15
|
+
**AI Analysis** — Uses a language model to semantically analyze conversation context and identify tool outputs that are no longer relevant to the current task. More thorough but incurs LLM cost.
|
|
16
|
+
|
|
9
17
|
## Installation
|
|
10
18
|
|
|
11
|
-
Add to your OpenCode config
|
|
19
|
+
Add to your OpenCode config:
|
|
12
20
|
|
|
13
|
-
```
|
|
21
|
+
```jsonc
|
|
22
|
+
// opencode.jsonc
|
|
14
23
|
{
|
|
15
|
-
"
|
|
24
|
+
"plugins": ["@tarquinen/opencode-dcp@0.3.17"]
|
|
16
25
|
}
|
|
17
26
|
```
|
|
18
27
|
|
|
28
|
+
When a new version is available, DCP will show a toast notification. Update by changing the version number in your config.
|
|
29
|
+
|
|
19
30
|
Restart OpenCode. The plugin will automatically start optimizing your sessions.
|
|
20
31
|
|
|
21
32
|
> **Note:** Project `plugin` arrays override global completely—include all desired plugins in project config if using both.
|
|
@@ -36,6 +47,7 @@ DCP uses its own config file (`~/.config/opencode/dcp.jsonc` or `.opencode/dcp.j
|
|
|
36
47
|
| `debug` | `false` | Log to `~/.config/opencode/logs/dcp/` |
|
|
37
48
|
| `model` | (session) | Model for analysis (e.g., `"anthropic/claude-haiku-4-5"`) |
|
|
38
49
|
| `showModelErrorToasts` | `true` | Show notifications on model fallback |
|
|
50
|
+
| `strictModelSelection` | `false` | Only run AI analysis with session or configured model (disables fallback models) |
|
|
39
51
|
| `pruning_summary` | `"detailed"` | `"off"`, `"minimal"`, or `"detailed"` |
|
|
40
52
|
| `protectedTools` | `["task", "todowrite", "todoread", "context_pruning"]` | Tools that are never pruned |
|
|
41
53
|
| `strategies.onIdle` | `["deduplication", "ai-analysis"]` | Strategies for automatic pruning |
|
|
@@ -54,13 +66,11 @@ DCP uses its own config file (`~/.config/opencode/dcp.jsonc` or `.opencode/dcp.j
|
|
|
54
66
|
}
|
|
55
67
|
```
|
|
56
68
|
|
|
57
|
-
|
|
69
|
+
### Config Precedence
|
|
58
70
|
|
|
59
|
-
|
|
71
|
+
Settings are merged in order: **Defaults** → **Global** (`~/.config/opencode/dcp.jsonc`) → **Project** (`.opencode/dcp.jsonc`). Each level overrides the previous, so project settings take priority over global, which takes priority over defaults.
|
|
60
72
|
|
|
61
|
-
|
|
62
|
-
{ "plugin": ["@tarquinen/opencode-dcp@0.3.16"] }
|
|
63
|
-
```
|
|
73
|
+
Restart OpenCode after making config changes.
|
|
64
74
|
|
|
65
75
|
## License
|
|
66
76
|
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAgBjD,QAAA,MAAM,MAAM,EAAE,MAmOK,CAAA;AAEnB,eAAe,MAAM,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -3,37 +3,29 @@ import { getConfig } from "./lib/config";
|
|
|
3
3
|
import { Logger } from "./lib/logger";
|
|
4
4
|
import { Janitor } from "./lib/janitor";
|
|
5
5
|
import { checkForUpdates } from "./lib/version-checker";
|
|
6
|
-
/**
|
|
7
|
-
* Checks if a session is a subagent (child session)
|
|
8
|
-
* Subagent sessions should skip pruning operations
|
|
9
|
-
*/
|
|
10
6
|
async function isSubagentSession(client, sessionID) {
|
|
11
7
|
try {
|
|
12
8
|
const result = await client.session.get({ path: { id: sessionID } });
|
|
13
9
|
return !!result.data?.parentID;
|
|
14
10
|
}
|
|
15
11
|
catch (error) {
|
|
16
|
-
// On error, assume it's not a subagent and continue (fail open)
|
|
17
12
|
return false;
|
|
18
13
|
}
|
|
19
14
|
}
|
|
20
15
|
const plugin = (async (ctx) => {
|
|
21
16
|
const { config, migrations } = getConfig(ctx);
|
|
22
|
-
// Exit early if plugin is disabled
|
|
23
17
|
if (!config.enabled) {
|
|
24
18
|
return {};
|
|
25
19
|
}
|
|
26
|
-
// Suppress AI SDK warnings about responseFormat (harmless for our use case)
|
|
27
20
|
if (typeof globalThis !== 'undefined') {
|
|
28
21
|
globalThis.AI_SDK_LOG_WARNINGS = false;
|
|
29
22
|
}
|
|
30
|
-
// Logger uses ~/.config/opencode/logs/dcp/ for consistent log location
|
|
31
23
|
const logger = new Logger(config.debug);
|
|
32
24
|
const prunedIdsState = new Map();
|
|
33
25
|
const statsState = new Map();
|
|
34
|
-
const toolParametersCache = new Map();
|
|
35
|
-
const modelCache = new Map();
|
|
36
|
-
const janitor = new Janitor(ctx.client, prunedIdsState, statsState, logger, toolParametersCache, config.protectedTools, modelCache, config.model, config.showModelErrorToasts, config.pruning_summary, ctx.directory);
|
|
26
|
+
const toolParametersCache = new Map();
|
|
27
|
+
const modelCache = new Map();
|
|
28
|
+
const janitor = new Janitor(ctx.client, prunedIdsState, statsState, logger, toolParametersCache, config.protectedTools, modelCache, config.model, config.showModelErrorToasts, config.strictModelSelection, config.pruning_summary, ctx.directory);
|
|
37
29
|
const cacheToolParameters = (messages) => {
|
|
38
30
|
for (const message of messages) {
|
|
39
31
|
if (message.role !== 'assistant' || !Array.isArray(message.tool_calls)) {
|
|
@@ -53,40 +45,32 @@ const plugin = (async (ctx) => {
|
|
|
53
45
|
});
|
|
54
46
|
}
|
|
55
47
|
catch (error) {
|
|
56
|
-
// Ignore JSON parse errors for individual tool calls
|
|
57
48
|
}
|
|
58
49
|
}
|
|
59
50
|
}
|
|
60
51
|
};
|
|
61
|
-
// Global fetch wrapper
|
|
62
|
-
// This works because all providers ultimately call globalThis.fetch
|
|
52
|
+
// Global fetch wrapper - caches tool parameters and performs pruning
|
|
63
53
|
const originalGlobalFetch = globalThis.fetch;
|
|
64
54
|
globalThis.fetch = async (input, init) => {
|
|
65
55
|
if (init?.body && typeof init.body === 'string') {
|
|
66
56
|
try {
|
|
67
57
|
const body = JSON.parse(init.body);
|
|
68
58
|
if (body.messages && Array.isArray(body.messages)) {
|
|
69
|
-
// Cache tool parameters for janitor metadata
|
|
70
59
|
cacheToolParameters(body.messages);
|
|
71
|
-
// Check for tool messages that might need pruning
|
|
72
60
|
const toolMessages = body.messages.filter((m) => m.role === 'tool');
|
|
73
|
-
// Collect all pruned IDs across all sessions (excluding subagents)
|
|
74
|
-
// This is safe because tool_call_ids are globally unique
|
|
75
61
|
const allSessions = await ctx.client.session.list();
|
|
76
62
|
const allPrunedIds = new Set();
|
|
77
63
|
if (allSessions.data) {
|
|
78
64
|
for (const session of allSessions.data) {
|
|
79
65
|
if (session.parentID)
|
|
80
|
-
continue;
|
|
66
|
+
continue;
|
|
81
67
|
const prunedIds = prunedIdsState.get(session.id) ?? [];
|
|
82
68
|
prunedIds.forEach((id) => allPrunedIds.add(id));
|
|
83
69
|
}
|
|
84
70
|
}
|
|
85
|
-
// Only process tool message replacement if there are tool messages and pruned IDs
|
|
86
71
|
if (toolMessages.length > 0 && allPrunedIds.size > 0) {
|
|
87
72
|
let replacedCount = 0;
|
|
88
73
|
body.messages = body.messages.map((m) => {
|
|
89
|
-
// Normalize ID to lowercase for case-insensitive matching
|
|
90
74
|
if (m.role === 'tool' && allPrunedIds.has(m.tool_call_id?.toLowerCase())) {
|
|
91
75
|
replacedCount++;
|
|
92
76
|
return {
|
|
@@ -101,7 +85,6 @@ const plugin = (async (ctx) => {
|
|
|
101
85
|
replaced: replacedCount,
|
|
102
86
|
total: toolMessages.length
|
|
103
87
|
});
|
|
104
|
-
// Save wrapped context to file if debug is enabled
|
|
105
88
|
if (logger.enabled) {
|
|
106
89
|
await logger.saveWrappedContext("global", body.messages, {
|
|
107
90
|
url: typeof input === 'string' ? input : 'URL object',
|
|
@@ -109,14 +92,12 @@ const plugin = (async (ctx) => {
|
|
|
109
92
|
totalMessages: body.messages.length
|
|
110
93
|
});
|
|
111
94
|
}
|
|
112
|
-
// Update the request body with modified messages
|
|
113
95
|
init.body = JSON.stringify(body);
|
|
114
96
|
}
|
|
115
97
|
}
|
|
116
98
|
}
|
|
117
99
|
}
|
|
118
100
|
catch (e) {
|
|
119
|
-
// Ignore parse errors and fall through to original fetch
|
|
120
101
|
}
|
|
121
102
|
}
|
|
122
103
|
return originalGlobalFetch(input, init);
|
|
@@ -125,9 +106,7 @@ const plugin = (async (ctx) => {
|
|
|
125
106
|
strategies: config.strategies,
|
|
126
107
|
model: config.model || "auto"
|
|
127
108
|
});
|
|
128
|
-
// Check for updates on launch (fire and forget)
|
|
129
109
|
checkForUpdates(ctx.client, logger).catch(() => { });
|
|
130
|
-
// Show migration toast if config was migrated (delayed to not overlap with version toast)
|
|
131
110
|
if (migrations.length > 0) {
|
|
132
111
|
setTimeout(async () => {
|
|
133
112
|
try {
|
|
@@ -141,38 +120,25 @@ const plugin = (async (ctx) => {
|
|
|
141
120
|
});
|
|
142
121
|
}
|
|
143
122
|
catch {
|
|
144
|
-
// Silently fail - toast is non-critical
|
|
145
123
|
}
|
|
146
|
-
}, 7000);
|
|
124
|
+
}, 7000);
|
|
147
125
|
}
|
|
148
126
|
return {
|
|
149
|
-
/**
|
|
150
|
-
* Event Hook: Triggers janitor analysis when session becomes idle
|
|
151
|
-
*/
|
|
152
127
|
event: async ({ event }) => {
|
|
153
128
|
if (event.type === "session.status" && event.properties.status.type === "idle") {
|
|
154
|
-
// Skip pruning for subagent sessions
|
|
155
129
|
if (await isSubagentSession(ctx.client, event.properties.sessionID))
|
|
156
130
|
return;
|
|
157
|
-
// Skip if no idle strategies configured
|
|
158
131
|
if (config.strategies.onIdle.length === 0)
|
|
159
132
|
return;
|
|
160
|
-
// Fire and forget the janitor - don't block the event handler
|
|
161
133
|
janitor.runOnIdle(event.properties.sessionID, config.strategies.onIdle).catch(err => {
|
|
162
134
|
logger.error("janitor", "Failed", { error: err.message });
|
|
163
135
|
});
|
|
164
136
|
}
|
|
165
137
|
},
|
|
166
|
-
/**
|
|
167
|
-
* Chat Params Hook: Caches model info for janitor
|
|
168
|
-
*/
|
|
169
138
|
"chat.params": async (input, _output) => {
|
|
170
139
|
const sessionId = input.sessionID;
|
|
171
|
-
// Cache model information for this session so janitor can access it
|
|
172
|
-
// The provider.id is actually nested at provider.info.id (not in SDK types)
|
|
173
140
|
let providerID = input.provider?.info?.id || input.provider?.id;
|
|
174
141
|
const modelID = input.model?.id;
|
|
175
|
-
// If provider.id is not available, try to get it from the message
|
|
176
142
|
if (!providerID && input.message?.model?.providerID) {
|
|
177
143
|
providerID = input.message.model.providerID;
|
|
178
144
|
}
|
|
@@ -183,9 +149,6 @@ const plugin = (async (ctx) => {
|
|
|
183
149
|
});
|
|
184
150
|
}
|
|
185
151
|
},
|
|
186
|
-
/**
|
|
187
|
-
* Tool Hook: Exposes context_pruning tool to AI (if configured)
|
|
188
|
-
*/
|
|
189
152
|
tool: config.strategies.onTool.length > 0 ? {
|
|
190
153
|
context_pruning: tool({
|
|
191
154
|
description: `Performs semantic pruning on session tool outputs that are no longer relevant to the current task. Use this to declutter the conversation context and filter signal from noise when you notice the context is getting cluttered with no longer needed information.
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface PluginConfig {
|
|
|
6
6
|
protectedTools: string[];
|
|
7
7
|
model?: string;
|
|
8
8
|
showModelErrorToasts?: boolean;
|
|
9
|
+
strictModelSelection?: boolean;
|
|
9
10
|
pruning_summary: "off" | "minimal" | "detailed";
|
|
10
11
|
strategies: {
|
|
11
12
|
onIdle: PruningStrategy[];
|
|
@@ -16,20 +17,5 @@ export interface ConfigResult {
|
|
|
16
17
|
config: PluginConfig;
|
|
17
18
|
migrations: string[];
|
|
18
19
|
}
|
|
19
|
-
/**
|
|
20
|
-
* Loads configuration with support for both global and project-level configs
|
|
21
|
-
*
|
|
22
|
-
* Config resolution order:
|
|
23
|
-
* 1. Start with default config
|
|
24
|
-
* 2. Merge with global config (~/.config/opencode/dcp.jsonc)
|
|
25
|
-
* 3. Merge with project config (.opencode/dcp.jsonc) if found
|
|
26
|
-
*
|
|
27
|
-
* If config has invalid/deprecated keys, backs up and resets to defaults.
|
|
28
|
-
*
|
|
29
|
-
* Project config overrides global config, which overrides defaults.
|
|
30
|
-
*
|
|
31
|
-
* @param ctx - Plugin input context (optional). If provided, will search for project-level config.
|
|
32
|
-
* @returns ConfigResult with merged configuration and any migration messages
|
|
33
|
-
*/
|
|
34
20
|
export declare function getConfig(ctx?: PluginInput): ConfigResult;
|
|
35
21
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../lib/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../lib/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEtD,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,aAAa,CAAA;AAE7D,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,eAAe,EAAE,KAAK,GAAG,SAAS,GAAG,UAAU,CAAA;IAC/C,UAAU,EAAE;QACR,MAAM,EAAE,eAAe,EAAE,CAAA;QACzB,MAAM,EAAE,eAAe,EAAE,CAAA;KAC5B,CAAA;CACJ;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,YAAY,CAAA;IACpB,UAAU,EAAE,MAAM,EAAE,CAAA;CACvB;AAkJD,wBAAgB,SAAS,CAAC,GAAG,CAAC,EAAE,WAAW,GAAG,YAAY,CAkEzD"}
|
package/dist/lib/config.js
CHANGED
|
@@ -1,39 +1,33 @@
|
|
|
1
|
-
// lib/config.ts
|
|
2
1
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync, copyFileSync } from 'fs';
|
|
3
2
|
import { join, dirname } from 'path';
|
|
4
3
|
import { homedir } from 'os';
|
|
5
4
|
import { parse } from 'jsonc-parser';
|
|
6
5
|
import { Logger } from './logger';
|
|
7
6
|
const defaultConfig = {
|
|
8
|
-
enabled: true,
|
|
9
|
-
debug: false,
|
|
10
|
-
protectedTools: ['task', 'todowrite', 'todoread', 'context_pruning'],
|
|
11
|
-
showModelErrorToasts: true,
|
|
12
|
-
|
|
7
|
+
enabled: true,
|
|
8
|
+
debug: false,
|
|
9
|
+
protectedTools: ['task', 'todowrite', 'todoread', 'context_pruning'],
|
|
10
|
+
showModelErrorToasts: true,
|
|
11
|
+
strictModelSelection: false,
|
|
12
|
+
pruning_summary: 'detailed',
|
|
13
13
|
strategies: {
|
|
14
|
-
// Default: Full analysis on idle (like previous "smart" mode)
|
|
15
14
|
onIdle: ['deduplication', 'ai-analysis'],
|
|
16
|
-
// Default: Only deduplication when AI calls the tool (faster, no extra LLM cost)
|
|
17
15
|
onTool: ['deduplication']
|
|
18
16
|
}
|
|
19
17
|
};
|
|
20
|
-
// Valid top-level keys in the current config schema
|
|
21
18
|
const VALID_CONFIG_KEYS = new Set([
|
|
22
19
|
'enabled',
|
|
23
20
|
'debug',
|
|
24
21
|
'protectedTools',
|
|
25
22
|
'model',
|
|
26
23
|
'showModelErrorToasts',
|
|
24
|
+
'strictModelSelection',
|
|
27
25
|
'pruning_summary',
|
|
28
26
|
'strategies'
|
|
29
27
|
]);
|
|
30
28
|
const GLOBAL_CONFIG_DIR = join(homedir(), '.config', 'opencode');
|
|
31
29
|
const GLOBAL_CONFIG_PATH_JSONC = join(GLOBAL_CONFIG_DIR, 'dcp.jsonc');
|
|
32
30
|
const GLOBAL_CONFIG_PATH_JSON = join(GLOBAL_CONFIG_DIR, 'dcp.json');
|
|
33
|
-
/**
|
|
34
|
-
* Searches for .opencode directory starting from current directory and going up
|
|
35
|
-
* Returns the path to .opencode directory if found, null otherwise
|
|
36
|
-
*/
|
|
37
31
|
function findOpencodeDir(startDir) {
|
|
38
32
|
let current = startDir;
|
|
39
33
|
while (current !== '/') {
|
|
@@ -43,17 +37,12 @@ function findOpencodeDir(startDir) {
|
|
|
43
37
|
}
|
|
44
38
|
const parent = dirname(current);
|
|
45
39
|
if (parent === current)
|
|
46
|
-
break;
|
|
40
|
+
break;
|
|
47
41
|
current = parent;
|
|
48
42
|
}
|
|
49
43
|
return null;
|
|
50
44
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Determines which config file to use (prefers .jsonc, falls back to .json)
|
|
53
|
-
* Checks both project-level and global configs
|
|
54
|
-
*/
|
|
55
45
|
function getConfigPaths(ctx) {
|
|
56
|
-
// Global config paths
|
|
57
46
|
let globalPath = null;
|
|
58
47
|
if (existsSync(GLOBAL_CONFIG_PATH_JSONC)) {
|
|
59
48
|
globalPath = GLOBAL_CONFIG_PATH_JSONC;
|
|
@@ -61,7 +50,6 @@ function getConfigPaths(ctx) {
|
|
|
61
50
|
else if (existsSync(GLOBAL_CONFIG_PATH_JSON)) {
|
|
62
51
|
globalPath = GLOBAL_CONFIG_PATH_JSON;
|
|
63
52
|
}
|
|
64
|
-
// Project config paths (if context provided)
|
|
65
53
|
let projectPath = null;
|
|
66
54
|
if (ctx?.directory) {
|
|
67
55
|
const opencodeDir = findOpencodeDir(ctx.directory);
|
|
@@ -78,63 +66,36 @@ function getConfigPaths(ctx) {
|
|
|
78
66
|
}
|
|
79
67
|
return { global: globalPath, project: projectPath };
|
|
80
68
|
}
|
|
81
|
-
/**
|
|
82
|
-
* Creates the default configuration file with helpful comments
|
|
83
|
-
*/
|
|
84
69
|
function createDefaultConfig() {
|
|
85
|
-
// Ensure the directory exists
|
|
86
70
|
if (!existsSync(GLOBAL_CONFIG_DIR)) {
|
|
87
71
|
mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
88
72
|
}
|
|
89
73
|
const configContent = `{
|
|
90
|
-
// Enable or disable the
|
|
74
|
+
// Enable or disable the plugin
|
|
91
75
|
"enabled": true,
|
|
92
|
-
|
|
93
76
|
// Enable debug logging to ~/.config/opencode/logs/dcp/
|
|
94
|
-
// Outputs include:
|
|
95
|
-
// - daily/YYYY-MM-DD.log (plugin activity, decisions, errors)
|
|
96
|
-
// - ai-context/*.json (messages sent to AI after pruning)
|
|
97
77
|
"debug": false,
|
|
98
|
-
|
|
99
|
-
// Optional: Specify a model to use for analysis instead of the session model
|
|
100
|
-
// Format: "provider/model" (same as agent model config in opencode.jsonc)
|
|
101
|
-
// NOTE: Anthropic OAuth sonnet 4+ tier models are currently not supported
|
|
78
|
+
// Override model for analysis (format: "provider/model", e.g. "anthropic/claude-haiku-4-5")
|
|
102
79
|
// "model": "anthropic/claude-haiku-4-5",
|
|
103
|
-
|
|
104
|
-
// Show toast notifications when model selection fails and falls back
|
|
105
|
-
// Set to false to disable these informational toasts
|
|
80
|
+
// Show toast notifications when model selection fails
|
|
106
81
|
"showModelErrorToasts": true,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
//
|
|
110
|
-
// Empty array = disabled
|
|
82
|
+
// Only run AI analysis with session model or configured model (disables fallback models)
|
|
83
|
+
"strictModelSelection": false,
|
|
84
|
+
// Pruning strategies: "deduplication", "ai-analysis" (empty array = disabled)
|
|
111
85
|
"strategies": {
|
|
112
|
-
// Strategies to run when session goes idle
|
|
86
|
+
// Strategies to run when session goes idle
|
|
113
87
|
"onIdle": ["deduplication", "ai-analysis"],
|
|
114
|
-
|
|
115
|
-
// Strategies to run when AI calls the context_pruning tool
|
|
116
|
-
// Empty array = tool not exposed to AI
|
|
88
|
+
// Strategies to run when AI calls context_pruning tool
|
|
117
89
|
"onTool": ["deduplication"]
|
|
118
90
|
},
|
|
119
|
-
|
|
120
|
-
// Pruning summary display mode:
|
|
121
|
-
// "off": No UI summary (silent pruning)
|
|
122
|
-
// "minimal": Show tokens saved and count (e.g., "Saved ~2.5K tokens (6 tools pruned)")
|
|
123
|
-
// "detailed": Show full breakdown by tool type and pruning method (default)
|
|
91
|
+
// Summary display: "off", "minimal", or "detailed"
|
|
124
92
|
"pruning_summary": "detailed",
|
|
125
|
-
|
|
126
|
-
// List of tools that should never be pruned from context
|
|
127
|
-
// "task": Each subagent invocation is intentional
|
|
128
|
-
// "todowrite"/"todoread": Stateful tools where each call matters
|
|
129
|
-
// "context_pruning": The pruning tool itself
|
|
93
|
+
// Tools that should never be pruned
|
|
130
94
|
"protectedTools": ["task", "todowrite", "todoread", "context_pruning"]
|
|
131
95
|
}
|
|
132
96
|
`;
|
|
133
97
|
writeFileSync(GLOBAL_CONFIG_PATH_JSONC, configContent, 'utf-8');
|
|
134
98
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Loads a single config file and parses it
|
|
137
|
-
*/
|
|
138
99
|
function loadConfigFile(configPath) {
|
|
139
100
|
try {
|
|
140
101
|
const fileContent = readFileSync(configPath, 'utf-8');
|
|
@@ -144,9 +105,6 @@ function loadConfigFile(configPath) {
|
|
|
144
105
|
return null;
|
|
145
106
|
}
|
|
146
107
|
}
|
|
147
|
-
/**
|
|
148
|
-
* Check if config has any unknown or deprecated keys
|
|
149
|
-
*/
|
|
150
108
|
function getInvalidKeys(config) {
|
|
151
109
|
const invalidKeys = [];
|
|
152
110
|
for (const key of Object.keys(config)) {
|
|
@@ -156,17 +114,11 @@ function getInvalidKeys(config) {
|
|
|
156
114
|
}
|
|
157
115
|
return invalidKeys;
|
|
158
116
|
}
|
|
159
|
-
/**
|
|
160
|
-
* Backs up existing config and creates fresh default config
|
|
161
|
-
* Returns the backup path if successful, null if failed
|
|
162
|
-
*/
|
|
163
117
|
function backupAndResetConfig(configPath, logger) {
|
|
164
118
|
try {
|
|
165
119
|
const backupPath = configPath + '.bak';
|
|
166
|
-
// Create backup
|
|
167
120
|
copyFileSync(configPath, backupPath);
|
|
168
121
|
logger.info('config', 'Created config backup', { backup: backupPath });
|
|
169
|
-
// Write fresh default config
|
|
170
122
|
createDefaultConfig();
|
|
171
123
|
logger.info('config', 'Created fresh default config', { path: GLOBAL_CONFIG_PATH_JSONC });
|
|
172
124
|
return backupPath;
|
|
@@ -176,9 +128,6 @@ function backupAndResetConfig(configPath, logger) {
|
|
|
176
128
|
return null;
|
|
177
129
|
}
|
|
178
130
|
}
|
|
179
|
-
/**
|
|
180
|
-
* Merge strategies config, handling partial overrides
|
|
181
|
-
*/
|
|
182
131
|
function mergeStrategies(base, override) {
|
|
183
132
|
if (!override)
|
|
184
133
|
return base;
|
|
@@ -187,49 +136,30 @@ function mergeStrategies(base, override) {
|
|
|
187
136
|
onTool: override.onTool ?? base.onTool
|
|
188
137
|
};
|
|
189
138
|
}
|
|
190
|
-
/**
|
|
191
|
-
* Loads configuration with support for both global and project-level configs
|
|
192
|
-
*
|
|
193
|
-
* Config resolution order:
|
|
194
|
-
* 1. Start with default config
|
|
195
|
-
* 2. Merge with global config (~/.config/opencode/dcp.jsonc)
|
|
196
|
-
* 3. Merge with project config (.opencode/dcp.jsonc) if found
|
|
197
|
-
*
|
|
198
|
-
* If config has invalid/deprecated keys, backs up and resets to defaults.
|
|
199
|
-
*
|
|
200
|
-
* Project config overrides global config, which overrides defaults.
|
|
201
|
-
*
|
|
202
|
-
* @param ctx - Plugin input context (optional). If provided, will search for project-level config.
|
|
203
|
-
* @returns ConfigResult with merged configuration and any migration messages
|
|
204
|
-
*/
|
|
205
139
|
export function getConfig(ctx) {
|
|
206
140
|
let config = { ...defaultConfig, protectedTools: [...defaultConfig.protectedTools] };
|
|
207
141
|
const configPaths = getConfigPaths(ctx);
|
|
208
|
-
const logger = new Logger(true);
|
|
142
|
+
const logger = new Logger(true);
|
|
209
143
|
const migrations = [];
|
|
210
|
-
// 1. Load global config
|
|
211
144
|
if (configPaths.global) {
|
|
212
145
|
const globalConfig = loadConfigFile(configPaths.global);
|
|
213
146
|
if (globalConfig) {
|
|
214
|
-
// Check for invalid keys
|
|
215
147
|
const invalidKeys = getInvalidKeys(globalConfig);
|
|
216
148
|
if (invalidKeys.length > 0) {
|
|
217
|
-
// Config has deprecated/unknown keys - backup and reset
|
|
218
149
|
logger.info('config', 'Found invalid config keys', { keys: invalidKeys });
|
|
219
150
|
const backupPath = backupAndResetConfig(configPaths.global, logger);
|
|
220
151
|
if (backupPath) {
|
|
221
152
|
migrations.push(`Old config backed up to ${backupPath}`);
|
|
222
153
|
}
|
|
223
|
-
// Config is now reset to defaults, no need to merge
|
|
224
154
|
}
|
|
225
155
|
else {
|
|
226
|
-
// Valid config - merge with defaults
|
|
227
156
|
config = {
|
|
228
157
|
enabled: globalConfig.enabled ?? config.enabled,
|
|
229
158
|
debug: globalConfig.debug ?? config.debug,
|
|
230
159
|
protectedTools: globalConfig.protectedTools ?? config.protectedTools,
|
|
231
160
|
model: globalConfig.model ?? config.model,
|
|
232
161
|
showModelErrorToasts: globalConfig.showModelErrorToasts ?? config.showModelErrorToasts,
|
|
162
|
+
strictModelSelection: globalConfig.strictModelSelection ?? config.strictModelSelection,
|
|
233
163
|
strategies: mergeStrategies(config.strategies, globalConfig.strategies),
|
|
234
164
|
pruning_summary: globalConfig.pruning_summary ?? config.pruning_summary
|
|
235
165
|
};
|
|
@@ -238,31 +168,28 @@ export function getConfig(ctx) {
|
|
|
238
168
|
}
|
|
239
169
|
}
|
|
240
170
|
else {
|
|
241
|
-
// Create default global config if it doesn't exist
|
|
242
171
|
createDefaultConfig();
|
|
243
172
|
logger.info('config', 'Created default global config', { path: GLOBAL_CONFIG_PATH_JSONC });
|
|
244
173
|
}
|
|
245
|
-
// 2. Load project config (overrides global)
|
|
246
174
|
if (configPaths.project) {
|
|
247
175
|
const projectConfig = loadConfigFile(configPaths.project);
|
|
248
176
|
if (projectConfig) {
|
|
249
|
-
// Check for invalid keys
|
|
250
177
|
const invalidKeys = getInvalidKeys(projectConfig);
|
|
251
178
|
if (invalidKeys.length > 0) {
|
|
252
|
-
// Project config has deprecated/unknown keys - just warn, don't reset project configs
|
|
253
179
|
logger.warn('config', 'Project config has invalid keys (ignored)', {
|
|
254
180
|
path: configPaths.project,
|
|
255
181
|
keys: invalidKeys
|
|
256
182
|
});
|
|
183
|
+
migrations.push(`Project config has invalid keys: ${invalidKeys.join(', ')}`);
|
|
257
184
|
}
|
|
258
185
|
else {
|
|
259
|
-
// Valid config - merge with current config
|
|
260
186
|
config = {
|
|
261
187
|
enabled: projectConfig.enabled ?? config.enabled,
|
|
262
188
|
debug: projectConfig.debug ?? config.debug,
|
|
263
189
|
protectedTools: projectConfig.protectedTools ?? config.protectedTools,
|
|
264
190
|
model: projectConfig.model ?? config.model,
|
|
265
191
|
showModelErrorToasts: projectConfig.showModelErrorToasts ?? config.showModelErrorToasts,
|
|
192
|
+
strictModelSelection: projectConfig.strictModelSelection ?? config.strictModelSelection,
|
|
266
193
|
strategies: mergeStrategies(config.strategies, projectConfig.strategies),
|
|
267
194
|
pruning_summary: projectConfig.pruning_summary ?? config.pruning_summary
|
|
268
195
|
};
|
package/dist/lib/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../lib/config.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../lib/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deduplicator Module
|
|
3
|
-
*
|
|
4
|
-
* Handles automatic detection and removal of duplicate tool calls based on
|
|
5
|
-
* tool name + parameter signature matching.
|
|
6
|
-
*/
|
|
7
1
|
export interface DuplicateDetectionResult {
|
|
8
2
|
duplicateIds: string[];
|
|
9
3
|
deduplicationDetails: Map<string, {
|
|
@@ -14,35 +8,11 @@ export interface DuplicateDetectionResult {
|
|
|
14
8
|
keptId: string;
|
|
15
9
|
}>;
|
|
16
10
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Detects duplicate tool calls based on tool name + parameter signature
|
|
19
|
-
* Keeps only the most recent occurrence of each duplicate set
|
|
20
|
-
* Respects protected tools - they are never deduplicated
|
|
21
|
-
*/
|
|
22
11
|
export declare function detectDuplicates(toolMetadata: Map<string, {
|
|
23
12
|
tool: string;
|
|
24
13
|
parameters?: any;
|
|
25
14
|
}>, unprunedToolCallIds: string[], // In chronological order
|
|
26
15
|
protectedTools: string[]): DuplicateDetectionResult;
|
|
27
|
-
/**
|
|
28
|
-
* Extract human-readable parameter key for notifications
|
|
29
|
-
* Supports all ACTIVE OpenCode tools with appropriate parameter extraction
|
|
30
|
-
*
|
|
31
|
-
* ACTIVE Tools (always available):
|
|
32
|
-
* - File: read, write, edit
|
|
33
|
-
* - Search: list, glob, grep
|
|
34
|
-
* - Execution: bash
|
|
35
|
-
* - Agent: task
|
|
36
|
-
* - Web: webfetch
|
|
37
|
-
* - Todo: todowrite, todoread
|
|
38
|
-
*
|
|
39
|
-
* CONDITIONAL Tools (may be disabled):
|
|
40
|
-
* - batch (experimental.batch_tool flag)
|
|
41
|
-
* - websearch, codesearch (OPENCODE_EXPERIMENTAL_EXA flag)
|
|
42
|
-
*
|
|
43
|
-
* INACTIVE Tools (exist but not registered, skip):
|
|
44
|
-
* - multiedit, patch, lsp_diagnostics, lsp_hover
|
|
45
|
-
*/
|
|
46
16
|
export declare function extractParameterKey(metadata: {
|
|
47
17
|
tool: string;
|
|
48
18
|
parameters?: any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deduplicator.d.ts","sourceRoot":"","sources":["../../lib/deduplicator.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"deduplicator.d.ts","sourceRoot":"","sources":["../../lib/deduplicator.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACrC,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE;QAC9B,QAAQ,EAAE,MAAM,CAAA;QAChB,YAAY,EAAE,MAAM,CAAA;QACpB,cAAc,EAAE,MAAM,CAAA;QACtB,SAAS,EAAE,MAAM,EAAE,CAAA;QACnB,MAAM,EAAE,MAAM,CAAA;KACjB,CAAC,CAAA;CACL;AAED,wBAAgB,gBAAgB,CAC5B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC,EAC7D,mBAAmB,EAAE,MAAM,EAAE,EAAG,yBAAyB;AACzD,cAAc,EAAE,MAAM,EAAE,GACzB,wBAAwB,CAuC1B;AAkCD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,GAAG,CAAA;CAAE,GAAG,MAAM,CAuExF"}
|