@dev4s/opencode-dcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +160 -0
  3. package/dist/index.d.ts +4 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +87 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/config.d.ts +58 -0
  8. package/dist/lib/config.d.ts.map +1 -0
  9. package/dist/lib/config.js +696 -0
  10. package/dist/lib/config.js.map +1 -0
  11. package/dist/lib/hooks.d.ts +10 -0
  12. package/dist/lib/hooks.d.ts.map +1 -0
  13. package/dist/lib/hooks.js +45 -0
  14. package/dist/lib/hooks.js.map +1 -0
  15. package/dist/lib/logger.d.ts +31 -0
  16. package/dist/lib/logger.d.ts.map +1 -0
  17. package/dist/lib/logger.js +183 -0
  18. package/dist/lib/logger.js.map +1 -0
  19. package/dist/lib/messages/index.d.ts +2 -0
  20. package/dist/lib/messages/index.d.ts.map +1 -0
  21. package/dist/lib/messages/index.js +2 -0
  22. package/dist/lib/messages/index.js.map +1 -0
  23. package/dist/lib/messages/prune.d.ts +6 -0
  24. package/dist/lib/messages/prune.d.ts.map +1 -0
  25. package/dist/lib/messages/prune.js +187 -0
  26. package/dist/lib/messages/prune.js.map +1 -0
  27. package/dist/lib/messages/utils.d.ts +9 -0
  28. package/dist/lib/messages/utils.d.ts.map +1 -0
  29. package/dist/lib/messages/utils.js +124 -0
  30. package/dist/lib/messages/utils.js.map +1 -0
  31. package/dist/lib/model-selector.d.ts +17 -0
  32. package/dist/lib/model-selector.d.ts.map +1 -0
  33. package/dist/lib/model-selector.js +141 -0
  34. package/dist/lib/model-selector.js.map +1 -0
  35. package/dist/lib/prompt.d.ts +3 -0
  36. package/dist/lib/prompt.d.ts.map +1 -0
  37. package/dist/lib/prompt.js +128 -0
  38. package/dist/lib/prompt.js.map +1 -0
  39. package/dist/lib/prompts/discard-tool-spec.txt +41 -0
  40. package/dist/lib/prompts/extract-tool-spec.txt +47 -0
  41. package/dist/lib/prompts/on-idle-analysis.txt +30 -0
  42. package/dist/lib/prompts/user/nudge/nudge-both.txt +10 -0
  43. package/dist/lib/prompts/user/nudge/nudge-discard.txt +9 -0
  44. package/dist/lib/prompts/user/nudge/nudge-extract.txt +9 -0
  45. package/dist/lib/prompts/user/system/system-prompt-both.txt +58 -0
  46. package/dist/lib/prompts/user/system/system-prompt-discard.txt +49 -0
  47. package/dist/lib/prompts/user/system/system-prompt-extract.txt +49 -0
  48. package/dist/lib/shared-utils.d.ts +4 -0
  49. package/dist/lib/shared-utils.d.ts.map +1 -0
  50. package/dist/lib/shared-utils.js +13 -0
  51. package/dist/lib/shared-utils.js.map +1 -0
  52. package/dist/lib/state/index.d.ts +4 -0
  53. package/dist/lib/state/index.d.ts.map +1 -0
  54. package/dist/lib/state/index.js +4 -0
  55. package/dist/lib/state/index.js.map +1 -0
  56. package/dist/lib/state/persistence.d.ts +16 -0
  57. package/dist/lib/state/persistence.d.ts.map +1 -0
  58. package/dist/lib/state/persistence.js +73 -0
  59. package/dist/lib/state/persistence.js.map +1 -0
  60. package/dist/lib/state/state.d.ts +8 -0
  61. package/dist/lib/state/state.d.ts.map +1 -0
  62. package/dist/lib/state/state.js +112 -0
  63. package/dist/lib/state/state.js.map +1 -0
  64. package/dist/lib/state/tool-cache.d.ts +13 -0
  65. package/dist/lib/state/tool-cache.d.ts.map +1 -0
  66. package/dist/lib/state/tool-cache.js +74 -0
  67. package/dist/lib/state/tool-cache.js.map +1 -0
  68. package/dist/lib/state/types.d.ts +32 -0
  69. package/dist/lib/state/types.d.ts.map +1 -0
  70. package/dist/lib/state/types.js +2 -0
  71. package/dist/lib/state/types.js.map +1 -0
  72. package/dist/lib/state/utils.d.ts +2 -0
  73. package/dist/lib/state/utils.d.ts.map +1 -0
  74. package/dist/lib/state/utils.js +10 -0
  75. package/dist/lib/state/utils.js.map +1 -0
  76. package/dist/lib/strategies/deduplication.d.ts +10 -0
  77. package/dist/lib/strategies/deduplication.d.ts.map +1 -0
  78. package/dist/lib/strategies/deduplication.js +89 -0
  79. package/dist/lib/strategies/deduplication.js.map +1 -0
  80. package/dist/lib/strategies/index.d.ts +6 -0
  81. package/dist/lib/strategies/index.d.ts.map +1 -0
  82. package/dist/lib/strategies/index.js +6 -0
  83. package/dist/lib/strategies/index.js.map +1 -0
  84. package/dist/lib/strategies/on-idle.d.ts +14 -0
  85. package/dist/lib/strategies/on-idle.d.ts.map +1 -0
  86. package/dist/lib/strategies/on-idle.js +220 -0
  87. package/dist/lib/strategies/on-idle.js.map +1 -0
  88. package/dist/lib/strategies/purge-errors.d.ts +13 -0
  89. package/dist/lib/strategies/purge-errors.d.ts.map +1 -0
  90. package/dist/lib/strategies/purge-errors.js +54 -0
  91. package/dist/lib/strategies/purge-errors.js.map +1 -0
  92. package/dist/lib/strategies/supersede-writes.d.ts +13 -0
  93. package/dist/lib/strategies/supersede-writes.d.ts.map +1 -0
  94. package/dist/lib/strategies/supersede-writes.js +80 -0
  95. package/dist/lib/strategies/supersede-writes.js.map +1 -0
  96. package/dist/lib/strategies/tools.d.ts +14 -0
  97. package/dist/lib/strategies/tools.d.ts.map +1 -0
  98. package/dist/lib/strategies/tools.js +137 -0
  99. package/dist/lib/strategies/tools.js.map +1 -0
  100. package/dist/lib/strategies/utils.d.ts +12 -0
  101. package/dist/lib/strategies/utils.d.ts.map +1 -0
  102. package/dist/lib/strategies/utils.js +82 -0
  103. package/dist/lib/strategies/utils.js.map +1 -0
  104. package/dist/lib/tokenizer.d.ts +24 -0
  105. package/dist/lib/tokenizer.d.ts.map +1 -0
  106. package/dist/lib/tokenizer.js +45 -0
  107. package/dist/lib/tokenizer.js.map +1 -0
  108. package/dist/lib/ui/notification.d.ts +9 -0
  109. package/dist/lib/ui/notification.d.ts.map +1 -0
  110. package/dist/lib/ui/notification.js +70 -0
  111. package/dist/lib/ui/notification.js.map +1 -0
  112. package/dist/lib/ui/utils.d.ts +15 -0
  113. package/dist/lib/ui/utils.d.ts.map +1 -0
  114. package/dist/lib/ui/utils.js +87 -0
  115. package/dist/lib/ui/utils.js.map +1 -0
  116. package/package.json +57 -0
@@ -0,0 +1,89 @@
1
+ import { buildToolIdList } from "../messages/utils";
2
+ import { calculateTokensSaved } from "./utils";
3
+ /**
4
+ * Deduplication strategy - prunes older tool calls that have identical
5
+ * tool name and parameters, keeping only the most recent occurrence.
6
+ * Modifies the session state in place to add pruned tool call IDs.
7
+ */
8
+ export const deduplicate = (state, logger, config, messages) => {
9
+ if (!config.strategies.deduplication.enabled) {
10
+ return;
11
+ }
12
+ // Build list of all tool call IDs from messages (chronological order)
13
+ const allToolIds = buildToolIdList(state, messages, logger);
14
+ if (allToolIds.length === 0) {
15
+ return;
16
+ }
17
+ // Filter out IDs already pruned
18
+ const alreadyPruned = new Set(state.prune.toolIds);
19
+ const unprunedIds = allToolIds.filter((id) => !alreadyPruned.has(id));
20
+ if (unprunedIds.length === 0) {
21
+ return;
22
+ }
23
+ const protectedTools = config.strategies.deduplication.protectedTools;
24
+ // Group by signature (tool name + normalized parameters)
25
+ const signatureMap = new Map();
26
+ for (const id of unprunedIds) {
27
+ const metadata = state.toolParameters.get(id);
28
+ if (!metadata) {
29
+ // logger.warn(`Missing metadata for tool call ID: ${id}`)
30
+ continue;
31
+ }
32
+ // Skip protected tools
33
+ if (protectedTools.includes(metadata.tool)) {
34
+ continue;
35
+ }
36
+ const signature = createToolSignature(metadata.tool, metadata.parameters);
37
+ if (!signatureMap.has(signature)) {
38
+ signatureMap.set(signature, []);
39
+ }
40
+ signatureMap.get(signature).push(id);
41
+ }
42
+ // Find duplicates - keep only the most recent (last) in each group
43
+ const newPruneIds = [];
44
+ for (const [, ids] of signatureMap.entries()) {
45
+ if (ids.length > 1) {
46
+ // All except last (most recent) should be pruned
47
+ const idsToRemove = ids.slice(0, -1);
48
+ newPruneIds.push(...idsToRemove);
49
+ }
50
+ }
51
+ state.stats.totalPruneTokens += calculateTokensSaved(state, messages, newPruneIds);
52
+ if (newPruneIds.length > 0) {
53
+ state.prune.toolIds.push(...newPruneIds);
54
+ logger.debug(`Marked ${newPruneIds.length} duplicate tool calls for pruning`);
55
+ }
56
+ };
57
+ function createToolSignature(tool, parameters) {
58
+ if (!parameters) {
59
+ return tool;
60
+ }
61
+ const normalized = normalizeParameters(parameters);
62
+ const sorted = sortObjectKeys(normalized);
63
+ return `${tool}::${JSON.stringify(sorted)}`;
64
+ }
65
+ function normalizeParameters(params) {
66
+ if (typeof params !== "object" || params === null)
67
+ return params;
68
+ if (Array.isArray(params))
69
+ return params;
70
+ const normalized = {};
71
+ for (const [key, value] of Object.entries(params)) {
72
+ if (value !== undefined && value !== null) {
73
+ normalized[key] = value;
74
+ }
75
+ }
76
+ return normalized;
77
+ }
78
+ function sortObjectKeys(obj) {
79
+ if (typeof obj !== "object" || obj === null)
80
+ return obj;
81
+ if (Array.isArray(obj))
82
+ return obj.map(sortObjectKeys);
83
+ const sorted = {};
84
+ for (const key of Object.keys(obj).sort()) {
85
+ sorted[key] = sortObjectKeys(obj[key]);
86
+ }
87
+ return sorted;
88
+ }
89
+ //# sourceMappingURL=deduplication.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deduplication.js","sourceRoot":"","sources":["../../../lib/strategies/deduplication.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { deduplicate } from "./deduplication";
2
+ export { runOnIdle } from "./on-idle";
3
+ export { createDiscardTool, createExtractTool } from "./tools";
4
+ export { supersedeWrites } from "./supersede-writes";
5
+ export { purgeErrors } from "./purge-errors";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/strategies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,6 @@
1
+ export { deduplicate } from "./deduplication";
2
+ export { runOnIdle } from "./on-idle";
3
+ export { createDiscardTool, createExtractTool } from "./tools";
4
+ export { supersedeWrites } from "./supersede-writes";
5
+ export { purgeErrors } from "./purge-errors";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/strategies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,14 @@
1
+ import type { SessionState } from "../state";
2
+ import type { Logger } from "../logger";
3
+ import type { PluginConfig } from "../config";
4
+ export interface OnIdleResult {
5
+ prunedCount: number;
6
+ tokensSaved: number;
7
+ prunedIds: string[];
8
+ }
9
+ /**
10
+ * Run the onIdle pruning strategy.
11
+ * This is called when the session transitions to idle state.
12
+ */
13
+ export declare function runOnIdle(client: any, state: SessionState, logger: Logger, config: PluginConfig, workingDirectory?: string): Promise<void | null>;
14
+ //# sourceMappingURL=on-idle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"on-idle.d.ts","sourceRoot":"","sources":["../../../lib/strategies/on-idle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAiC,MAAM,UAAU,CAAA;AAC3E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAQ7C,MAAM,WAAW,YAAY;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,EAAE,CAAA;CACtB;AA6LD;;;GAGG;AACH,wBAAsB,SAAS,CAC3B,MAAM,EAAE,GAAG,EACX,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,EACpB,gBAAgB,CAAC,EAAE,MAAM,GAC1B,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAgHtB"}
@@ -0,0 +1,220 @@
1
+ import { z } from "zod";
2
+ import { buildAnalysisPrompt } from "../prompt";
3
+ import { selectModel } from "../model-selector";
4
+ import { saveSessionState } from "../state/persistence";
5
+ import { sendUnifiedNotification } from "../ui/notification";
6
+ import { calculateTokensSaved, getCurrentParams } from "./utils";
7
+ import { isMessageCompacted } from "../shared-utils";
8
+ /**
9
+ * Parse messages to extract tool information.
10
+ */
11
+ function parseMessages(state, messages, toolParametersCache) {
12
+ const toolCallIds = [];
13
+ const toolMetadata = new Map();
14
+ for (const msg of messages) {
15
+ if (isMessageCompacted(state, msg)) {
16
+ continue;
17
+ }
18
+ if (msg.parts) {
19
+ for (const part of msg.parts) {
20
+ if (part.type === "tool" && part.callID) {
21
+ toolCallIds.push(part.callID);
22
+ const cachedData = toolParametersCache.get(part.callID);
23
+ const parameters = cachedData?.parameters ?? part.state?.input ?? {};
24
+ toolMetadata.set(part.callID, {
25
+ tool: part.tool,
26
+ parameters: parameters,
27
+ status: part.state?.status,
28
+ error: part.state?.status === "error" ? part.state.error : undefined,
29
+ turn: cachedData?.turn ?? 0,
30
+ });
31
+ }
32
+ }
33
+ }
34
+ }
35
+ return { toolCallIds, toolMetadata };
36
+ }
37
+ /**
38
+ * Replace pruned tool outputs in messages for LLM analysis.
39
+ */
40
+ function replacePrunedToolOutputs(messages, prunedIds) {
41
+ if (prunedIds.length === 0)
42
+ return messages;
43
+ const prunedIdsSet = new Set(prunedIds);
44
+ return messages.map((msg) => {
45
+ if (!msg.parts)
46
+ return msg;
47
+ return {
48
+ ...msg,
49
+ parts: msg.parts.map((part) => {
50
+ if (part.type === "tool" &&
51
+ part.callID &&
52
+ prunedIdsSet.has(part.callID) &&
53
+ part.state?.output) {
54
+ return {
55
+ ...part,
56
+ state: {
57
+ ...part.state,
58
+ output: "[Output removed to save context - information superseded or no longer needed]",
59
+ },
60
+ };
61
+ }
62
+ return part;
63
+ }),
64
+ };
65
+ });
66
+ }
67
+ /**
68
+ * Run LLM analysis to determine which tool calls can be pruned.
69
+ */
70
+ async function runLlmAnalysis(client, state, logger, config, messages, unprunedToolCallIds, alreadyPrunedIds, toolMetadata, workingDirectory) {
71
+ const protectedToolCallIds = [];
72
+ const prunableToolCallIds = unprunedToolCallIds.filter((id) => {
73
+ const metadata = toolMetadata.get(id);
74
+ if (metadata && config.strategies.onIdle.protectedTools.includes(metadata.tool)) {
75
+ protectedToolCallIds.push(id);
76
+ return false;
77
+ }
78
+ return true;
79
+ });
80
+ if (prunableToolCallIds.length === 0) {
81
+ return [];
82
+ }
83
+ // Get model info from messages
84
+ let validModelInfo = undefined;
85
+ if (messages.length > 0) {
86
+ const lastMessage = messages[messages.length - 1];
87
+ const model = lastMessage.info?.model;
88
+ if (model?.providerID && model?.modelID) {
89
+ validModelInfo = {
90
+ providerID: model.providerID,
91
+ modelID: model.modelID,
92
+ };
93
+ }
94
+ }
95
+ const modelSelection = await selectModel(validModelInfo, logger, config.strategies.onIdle.model, workingDirectory);
96
+ logger.info(`OnIdle Model: ${modelSelection.modelInfo.providerID}/${modelSelection.modelInfo.modelID}`, {
97
+ source: modelSelection.source,
98
+ });
99
+ if (modelSelection.failedModel && config.strategies.onIdle.showModelErrorToasts) {
100
+ const skipAi = modelSelection.source === "fallback" && config.strategies.onIdle.strictModelSelection;
101
+ try {
102
+ await client.tui.showToast({
103
+ body: {
104
+ title: skipAi ? "DCP: AI analysis skipped" : "DCP: Model fallback",
105
+ message: skipAi
106
+ ? `${modelSelection.failedModel.providerID}/${modelSelection.failedModel.modelID} failed\nAI analysis skipped (strictModelSelection enabled)`
107
+ : `${modelSelection.failedModel.providerID}/${modelSelection.failedModel.modelID} failed\nUsing ${modelSelection.modelInfo.providerID}/${modelSelection.modelInfo.modelID}`,
108
+ variant: "info",
109
+ duration: 5000,
110
+ },
111
+ });
112
+ }
113
+ catch {
114
+ // Ignore toast errors
115
+ }
116
+ }
117
+ if (modelSelection.source === "fallback" && config.strategies.onIdle.strictModelSelection) {
118
+ logger.info("Skipping AI analysis (fallback model, strictModelSelection enabled)");
119
+ return [];
120
+ }
121
+ const { generateObject } = await import("ai");
122
+ const sanitizedMessages = replacePrunedToolOutputs(messages, alreadyPrunedIds);
123
+ const analysisPrompt = buildAnalysisPrompt(prunableToolCallIds, sanitizedMessages, alreadyPrunedIds, protectedToolCallIds);
124
+ const result = await generateObject({
125
+ model: modelSelection.model,
126
+ schema: z.object({
127
+ pruned_tool_call_ids: z.array(z.string()),
128
+ reasoning: z.string(),
129
+ }),
130
+ prompt: analysisPrompt,
131
+ });
132
+ const rawLlmPrunedIds = result.object.pruned_tool_call_ids;
133
+ const llmPrunedIds = rawLlmPrunedIds.filter((id) => prunableToolCallIds.includes(id));
134
+ // Always log LLM output as debug
135
+ const reasoning = result.object.reasoning.replace(/\n+/g, " ").replace(/\s+/g, " ").trim();
136
+ logger.debug(`OnIdle LLM output`, {
137
+ pruned_tool_call_ids: rawLlmPrunedIds,
138
+ reasoning: reasoning,
139
+ });
140
+ return llmPrunedIds;
141
+ }
142
+ /**
143
+ * Run the onIdle pruning strategy.
144
+ * This is called when the session transitions to idle state.
145
+ */
146
+ export async function runOnIdle(client, state, logger, config, workingDirectory) {
147
+ try {
148
+ if (!state.sessionId) {
149
+ return null;
150
+ }
151
+ const sessionId = state.sessionId;
152
+ // Fetch session info and messages
153
+ const [sessionInfoResponse, messagesResponse] = await Promise.all([
154
+ client.session.get({ path: { id: sessionId } }),
155
+ client.session.messages({ path: { id: sessionId } }),
156
+ ]);
157
+ const sessionInfo = sessionInfoResponse.data;
158
+ const messages = messagesResponse.data || messagesResponse;
159
+ if (!messages || messages.length < 3) {
160
+ return null;
161
+ }
162
+ const currentParams = getCurrentParams(messages, logger);
163
+ const { toolCallIds, toolMetadata } = parseMessages(state, messages, state.toolParameters);
164
+ const alreadyPrunedIds = state.prune.toolIds;
165
+ const unprunedToolCallIds = toolCallIds.filter((id) => !alreadyPrunedIds.includes(id));
166
+ if (unprunedToolCallIds.length === 0) {
167
+ return null;
168
+ }
169
+ // Count prunable tools (excluding protected)
170
+ const candidateCount = unprunedToolCallIds.filter((id) => {
171
+ const metadata = toolMetadata.get(id);
172
+ return !metadata || !config.strategies.onIdle.protectedTools.includes(metadata.tool);
173
+ }).length;
174
+ if (candidateCount === 0) {
175
+ return null;
176
+ }
177
+ // Run LLM analysis
178
+ const llmPrunedIds = await runLlmAnalysis(client, state, logger, config, messages, unprunedToolCallIds, alreadyPrunedIds, toolMetadata, workingDirectory);
179
+ const newlyPrunedIds = llmPrunedIds.filter((id) => !alreadyPrunedIds.includes(id));
180
+ if (newlyPrunedIds.length === 0) {
181
+ return null;
182
+ }
183
+ // Log the tool IDs being pruned with their tool names
184
+ for (const id of newlyPrunedIds) {
185
+ const metadata = toolMetadata.get(id);
186
+ const toolName = metadata?.tool || "unknown";
187
+ logger.info(`OnIdle pruning tool: ${toolName}`, { callID: id });
188
+ }
189
+ // Update state
190
+ const allPrunedIds = [...new Set([...alreadyPrunedIds, ...newlyPrunedIds])];
191
+ state.prune.toolIds = allPrunedIds;
192
+ state.stats.pruneTokenCounter += calculateTokensSaved(state, messages, newlyPrunedIds);
193
+ // Build tool metadata map for notification
194
+ const prunedToolMetadata = new Map();
195
+ for (const id of newlyPrunedIds) {
196
+ const metadata = toolMetadata.get(id);
197
+ if (metadata) {
198
+ prunedToolMetadata.set(id, metadata);
199
+ }
200
+ }
201
+ // Send notification
202
+ await sendUnifiedNotification(client, logger, config, state, sessionId, newlyPrunedIds, prunedToolMetadata, undefined, // reason
203
+ currentParams, workingDirectory || "");
204
+ state.stats.totalPruneTokens += state.stats.pruneTokenCounter;
205
+ state.stats.pruneTokenCounter = 0;
206
+ state.nudgeCounter = 0;
207
+ state.lastToolPrune = true;
208
+ // Persist state
209
+ const sessionName = sessionInfo?.title;
210
+ saveSessionState(state, logger, sessionName).catch((err) => {
211
+ logger.error("Failed to persist state", { error: err.message });
212
+ });
213
+ logger.info(`OnIdle: Pruned ${newlyPrunedIds.length}/${candidateCount} tools`);
214
+ }
215
+ catch (error) {
216
+ logger.error("OnIdle analysis failed", { error: error.message });
217
+ return null;
218
+ }
219
+ }
220
+ //# sourceMappingURL=on-idle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"on-idle.js","sourceRoot":"","sources":["../../../lib/strategies/on-idle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { PluginConfig } from "../config";
2
+ import { Logger } from "../logger";
3
+ import type { SessionState, WithParts } from "../state";
4
+ /**
5
+ * Purge Errors strategy - prunes tool inputs for tools that errored
6
+ * after they are older than a configurable number of turns.
7
+ * The error message is preserved, but the (potentially large) inputs
8
+ * are removed to save context.
9
+ *
10
+ * Modifies the session state in place to add pruned tool call IDs.
11
+ */
12
+ export declare const purgeErrors: (state: SessionState, logger: Logger, config: PluginConfig, messages: WithParts[]) => void;
13
+ //# sourceMappingURL=purge-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"purge-errors.d.ts","sourceRoot":"","sources":["../../../lib/strategies/purge-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAIvD;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,GACpB,OAAO,YAAY,EACnB,QAAQ,MAAM,EACd,QAAQ,YAAY,EACpB,UAAU,SAAS,EAAE,KACtB,IAsDF,CAAA"}
@@ -0,0 +1,54 @@
1
+ import { buildToolIdList } from "../messages/utils";
2
+ import { calculateTokensSaved } from "./utils";
3
+ /**
4
+ * Purge Errors strategy - prunes tool inputs for tools that errored
5
+ * after they are older than a configurable number of turns.
6
+ * The error message is preserved, but the (potentially large) inputs
7
+ * are removed to save context.
8
+ *
9
+ * Modifies the session state in place to add pruned tool call IDs.
10
+ */
11
+ export const purgeErrors = (state, logger, config, messages) => {
12
+ if (!config.strategies.purgeErrors.enabled) {
13
+ return;
14
+ }
15
+ // Build list of all tool call IDs from messages (chronological order)
16
+ const allToolIds = buildToolIdList(state, messages, logger);
17
+ if (allToolIds.length === 0) {
18
+ return;
19
+ }
20
+ // Filter out IDs already pruned
21
+ const alreadyPruned = new Set(state.prune.toolIds);
22
+ const unprunedIds = allToolIds.filter((id) => !alreadyPruned.has(id));
23
+ if (unprunedIds.length === 0) {
24
+ return;
25
+ }
26
+ const protectedTools = config.strategies.purgeErrors.protectedTools;
27
+ const turnThreshold = config.strategies.purgeErrors.turns;
28
+ const newPruneIds = [];
29
+ for (const id of unprunedIds) {
30
+ const metadata = state.toolParameters.get(id);
31
+ if (!metadata) {
32
+ continue;
33
+ }
34
+ // Skip protected tools
35
+ if (protectedTools.includes(metadata.tool)) {
36
+ continue;
37
+ }
38
+ // Only process error tools
39
+ if (metadata.status !== "error") {
40
+ continue;
41
+ }
42
+ // Check if the tool is old enough to prune
43
+ const turnAge = state.currentTurn - metadata.turn;
44
+ if (turnAge >= turnThreshold) {
45
+ newPruneIds.push(id);
46
+ }
47
+ }
48
+ if (newPruneIds.length > 0) {
49
+ state.stats.totalPruneTokens += calculateTokensSaved(state, messages, newPruneIds);
50
+ state.prune.toolIds.push(...newPruneIds);
51
+ logger.debug(`Marked ${newPruneIds.length} error tool calls for pruning (older than ${turnThreshold} turns)`);
52
+ }
53
+ };
54
+ //# sourceMappingURL=purge-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"purge-errors.js","sourceRoot":"","sources":["../../../lib/strategies/purge-errors.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAE9C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACvB,KAAmB,EACnB,MAAc,EACd,MAAoB,EACpB,QAAqB,EACjB,EAAE;IACN,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;QACzC,OAAM;IACV,CAAC;IAED,sEAAsE;IACtE,MAAM,UAAU,GAAG,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;IAC3D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAM;IACV,CAAC;IAED,gCAAgC;IAChC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAClD,MAAM,WAAW,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IAErE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAM;IACV,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,cAAc,CAAA;IACnE,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,KAAK,CAAA;IAEzD,MAAM,WAAW,GAAa,EAAE,CAAA;IAEhC,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAC7C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,SAAQ;QACZ,CAAC;QAED,uBAAuB;QACvB,IAAI,cAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,SAAQ;QACZ,CAAC;QAED,2BAA2B;QAC3B,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YAC9B,SAAQ;QACZ,CAAC;QAED,2CAA2C;QAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAA;QACjD,IAAI,OAAO,IAAI,aAAa,EAAE,CAAC;YAC3B,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACxB,CAAC;IACL,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,KAAK,CAAC,gBAAgB,IAAI,oBAAoB,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAA;QAClF,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAA;QACxC,MAAM,CAAC,KAAK,CACR,UAAU,WAAW,CAAC,MAAM,6CAA6C,aAAa,SAAS,CAClG,CAAA;IACL,CAAC;AACL,CAAC,CAAA"}
@@ -0,0 +1,13 @@
1
+ import { PluginConfig } from "../config";
2
+ import { Logger } from "../logger";
3
+ import type { SessionState, WithParts } from "../state";
4
+ /**
5
+ * Supersede Writes strategy - prunes write tool inputs for files that have
6
+ * subsequently been read. When a file is written and later read, the original
7
+ * write content becomes redundant since the current file state is captured
8
+ * in the read result.
9
+ *
10
+ * Modifies the session state in place to add pruned tool call IDs.
11
+ */
12
+ export declare const supersedeWrites: (state: SessionState, logger: Logger, config: PluginConfig, messages: WithParts[]) => void;
13
+ //# sourceMappingURL=supersede-writes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supersede-writes.d.ts","sourceRoot":"","sources":["../../../lib/strategies/supersede-writes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAIvD;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GACxB,OAAO,YAAY,EACnB,QAAQ,MAAM,EACd,QAAQ,YAAY,EACpB,UAAU,SAAS,EAAE,KACtB,IAgFF,CAAA"}
@@ -0,0 +1,80 @@
1
+ import { buildToolIdList } from "../messages/utils";
2
+ import { calculateTokensSaved } from "./utils";
3
+ /**
4
+ * Supersede Writes strategy - prunes write tool inputs for files that have
5
+ * subsequently been read. When a file is written and later read, the original
6
+ * write content becomes redundant since the current file state is captured
7
+ * in the read result.
8
+ *
9
+ * Modifies the session state in place to add pruned tool call IDs.
10
+ */
11
+ export const supersedeWrites = (state, logger, config, messages) => {
12
+ if (!config.strategies.supersedeWrites.enabled) {
13
+ return;
14
+ }
15
+ // Build list of all tool call IDs from messages (chronological order)
16
+ const allToolIds = buildToolIdList(state, messages, logger);
17
+ if (allToolIds.length === 0) {
18
+ return;
19
+ }
20
+ // Filter out IDs already pruned
21
+ const alreadyPruned = new Set(state.prune.toolIds);
22
+ const unprunedIds = allToolIds.filter((id) => !alreadyPruned.has(id));
23
+ if (unprunedIds.length === 0) {
24
+ return;
25
+ }
26
+ // Track write tools by file path: filePath -> [{ id, index }]
27
+ // We track index to determine chronological order
28
+ const writesByFile = new Map();
29
+ // Track read file paths with their index
30
+ const readsByFile = new Map();
31
+ for (let i = 0; i < allToolIds.length; i++) {
32
+ const id = allToolIds[i];
33
+ const metadata = state.toolParameters.get(id);
34
+ if (!metadata) {
35
+ continue;
36
+ }
37
+ const filePath = metadata.parameters?.filePath;
38
+ if (!filePath) {
39
+ continue;
40
+ }
41
+ if (metadata.tool === "write") {
42
+ if (!writesByFile.has(filePath)) {
43
+ writesByFile.set(filePath, []);
44
+ }
45
+ writesByFile.get(filePath).push({ id, index: i });
46
+ }
47
+ else if (metadata.tool === "read") {
48
+ if (!readsByFile.has(filePath)) {
49
+ readsByFile.set(filePath, []);
50
+ }
51
+ readsByFile.get(filePath).push(i);
52
+ }
53
+ }
54
+ // Find writes that are superseded by subsequent reads
55
+ const newPruneIds = [];
56
+ for (const [filePath, writes] of writesByFile.entries()) {
57
+ const reads = readsByFile.get(filePath);
58
+ if (!reads || reads.length === 0) {
59
+ continue;
60
+ }
61
+ // For each write, check if there's a read that comes after it
62
+ for (const write of writes) {
63
+ // Skip if already pruned
64
+ if (alreadyPruned.has(write.id)) {
65
+ continue;
66
+ }
67
+ // Check if any read comes after this write
68
+ const hasSubsequentRead = reads.some((readIndex) => readIndex > write.index);
69
+ if (hasSubsequentRead) {
70
+ newPruneIds.push(write.id);
71
+ }
72
+ }
73
+ }
74
+ if (newPruneIds.length > 0) {
75
+ state.stats.totalPruneTokens += calculateTokensSaved(state, messages, newPruneIds);
76
+ state.prune.toolIds.push(...newPruneIds);
77
+ logger.debug(`Marked ${newPruneIds.length} superseded write tool calls for pruning`);
78
+ }
79
+ };
80
+ //# sourceMappingURL=supersede-writes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supersede-writes.js","sourceRoot":"","sources":["../../../lib/strategies/supersede-writes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import type { SessionState } from "../state";
3
+ import type { PluginConfig } from "../config";
4
+ import type { Logger } from "../logger";
5
+ export interface PruneToolContext {
6
+ client: any;
7
+ state: SessionState;
8
+ logger: Logger;
9
+ config: PluginConfig;
10
+ workingDirectory: string;
11
+ }
12
+ export declare function createDiscardTool(ctx: PruneToolContext): ReturnType<typeof tool>;
13
+ export declare function createExtractTool(ctx: PruneToolContext): ReturnType<typeof tool>;
14
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../lib/strategies/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAiC,MAAM,UAAU,CAAA;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAM7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAsBvC,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,GAAG,CAAA;IACX,KAAK,EAAE,YAAY,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,YAAY,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;CAC3B;AA+GD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,gBAAgB,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAwBhF;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,gBAAgB,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAmChF"}