Skip to content

Commit 508ced3

Browse files
authored
Merge pull request #403 from Opencode-DCP/dev
merge dev into master
2 parents b9f0018 + 7985365 commit 508ced3

File tree

17 files changed

+175
-20
lines changed

17 files changed

+175
-20
lines changed

lib/commands/sweep.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { formatPrunedItemsList } from "../ui/utils"
1515
import { getCurrentParams, getTotalToolTokens } from "../strategies/utils"
1616
import { buildToolIdList, isIgnoredUserMessage } from "../messages/utils"
1717
import { saveSessionState } from "../state/persistence"
18-
import { isMessageCompacted } from "../shared-utils"
18+
import { getLastUserMessage, isMessageCompacted } from "../shared-utils"
1919
import { getFilePathsFromParameters, isProtected } from "../protected-file-patterns"
2020
import { syncToolCache } from "../state/tool-cache"
2121

@@ -215,11 +215,21 @@ export async function handleSweepCommand(ctx: SweepCommandContext): Promise<void
215215
}
216216

217217
const tokensSaved = getTotalToolTokens(state, newToolIds)
218+
const originMessageId = getLastUserMessage(messages)?.info.id || ""
219+
if (!originMessageId) {
220+
logger.warn("Sweep prune origin unavailable - missing user message")
221+
}
218222

219223
// Add to prune list
220224
for (const id of newToolIds) {
221225
const entry = state.toolParameters.get(id)
222226
state.prune.tools.set(id, entry?.tokenCount ?? 0)
227+
if (originMessageId) {
228+
state.prune.origins.set(id, {
229+
source: "sweep",
230+
originMessageId,
231+
})
232+
}
223233
}
224234
state.stats.pruneTokenCounter += tokensSaved
225235
state.stats.totalPruneTokens += state.stats.pruneTokenCounter

lib/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ const DEFAULT_PROTECTED_TOOLS = [
9090
"batch",
9191
"plan_enter",
9292
"plan_exit",
93+
"write",
94+
"edit",
9395
]
9496

9597
// Valid config keys for validation against user config

lib/hooks.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { PluginConfig } from "./config"
44
import { assignMessageRefs } from "./message-ids"
55
import { syncToolCache } from "./state/tool-cache"
66
import { deduplicate, supersedeWrites, purgeErrors } from "./strategies"
7-
import { prune, insertPruneToolContext, insertMessageIdContext } from "./messages"
7+
import { prune, syncToolOrigins, insertPruneToolContext, insertMessageIdContext } from "./messages"
88
import { buildToolIdList, isIgnoredUserMessage } from "./messages/utils"
99
import { checkSession } from "./state"
1010
import { renderSystemPrompt } from "./prompts"
@@ -113,6 +113,7 @@ export function createChatMessageTransformHandler(
113113

114114
syncToolCache(state, config, logger, output.messages)
115115
buildToolIdList(state, output.messages, logger)
116+
syncToolOrigins(state, logger, output.messages)
116117

117118
deduplicate(state, logger, config, output.messages)
118119
supersedeWrites(state, logger, config, output.messages)

lib/messages/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { prune } from "./prune"
2+
export { syncToolOrigins } from "./sync"
23
export { insertPruneToolContext } from "./inject"
34
export { insertMessageIdContext } from "./inject"

lib/messages/prune.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,14 @@ const pruneToolInputs = (state: SessionState, logger: Logger, messages: WithPart
108108
if (part.type !== "tool") {
109109
continue
110110
}
111-
if (part.tool === "compress" && part.state.status === "completed") {
112-
const content = part.state.input?.content
113-
if (content && typeof content === "object" && "summary" in content) {
114-
content.summary = PRUNED_COMPRESS_SUMMARY_REPLACEMENT
115-
}
116-
continue
117-
}
111+
112+
// if (part.tool === "compress" && part.state.status === "completed") {
113+
// const content = part.state.input?.content
114+
// if (content && typeof content === "object" && "summary" in content) {
115+
// content.summary = PRUNED_COMPRESS_SUMMARY_REPLACEMENT
116+
// }
117+
// continue
118+
// }
118119

119120
if (!state.prune.tools.has(part.callID)) {
120121
continue

lib/messages/sync.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { SessionState, WithParts } from "../state"
2+
import type { Logger } from "../logger"
3+
4+
export const syncToolOrigins = (
5+
state: SessionState,
6+
logger: Logger,
7+
messages: WithParts[],
8+
): void => {
9+
if (!state.prune.origins?.size) {
10+
return
11+
}
12+
13+
const messageIds = new Set(messages.map((msg) => msg.info.id))
14+
let removedToolCount = 0
15+
let removedOriginCount = 0
16+
17+
for (const [toolId, origin] of state.prune.origins.entries()) {
18+
if (!state.prune.tools.has(toolId)) {
19+
state.prune.origins.delete(toolId)
20+
removedOriginCount++
21+
continue
22+
}
23+
24+
if (!messageIds.has(origin.originMessageId)) {
25+
state.prune.origins.delete(toolId)
26+
removedOriginCount++
27+
if (state.prune.tools.delete(toolId)) {
28+
removedToolCount++
29+
}
30+
}
31+
}
32+
33+
if (removedToolCount > 0 || removedOriginCount > 0) {
34+
logger.info("Synced prune origins", {
35+
removedToolCount,
36+
removedOriginCount,
37+
})
38+
}
39+
}

lib/messages/utils.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,15 @@ export const createSyntheticToolPart = (
8484
const partId = generateStableId("prt_dcp_tool", deterministicSeed)
8585
const callId = generateStableId("call_dcp_tool", deterministicSeed)
8686

87-
// Gemini requires thoughtSignature bypass to accept synthetic tool parts
87+
// Gemini requires a thought signature on synthetic function calls.
88+
// Keep this metadata both on the part and on state so whichever
89+
// conversion path is used can forward it to providerOptions.
8890
const toolPartMetadata = isGeminiModel(modelID)
89-
? { google: { thoughtSignature: "skip_thought_signature_validator" } }
90-
: {}
91+
? {
92+
google: { thoughtSignature: "skip_thought_signature_validator" },
93+
vertex: { thoughtSignature: "skip_thought_signature_validator" },
94+
}
95+
: undefined
9196

9297
return {
9398
id: partId,
@@ -96,14 +101,15 @@ export const createSyntheticToolPart = (
96101
type: "tool" as const,
97102
callID: callId,
98103
tool: "context_info",
104+
...(toolPartMetadata ? { metadata: toolPartMetadata } : {}),
99105
state: {
100106
status: "completed" as const,
101107
input: {},
102108
output: content,
103109
title: "Context Info",
104-
metadata: toolPartMetadata,
110+
...(toolPartMetadata ? { metadata: toolPartMetadata } : {}),
105111
time: { start: now, end: now },
106-
},
112+
} as any,
107113
}
108114
}
109115

lib/state/persistence.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import * as fs from "fs/promises"
88
import { existsSync } from "fs"
99
import { homedir } from "os"
1010
import { join } from "path"
11-
import type { SessionState, SessionStats, CompressSummary } from "./types"
11+
import type { SessionState, SessionStats, CompressSummary, PruneOrigin } from "./types"
1212
import type { Logger } from "../logger"
1313

1414
/** Prune state as stored on disk */
1515
export interface PersistedPrune {
1616
// New format: tool/message IDs with token counts
1717
tools?: Record<string, number>
1818
messages?: Record<string, number>
19+
origins?: Record<string, PruneOrigin>
1920
// Legacy format: plain ID arrays (backward compatibility)
2021
toolIds?: string[]
2122
messageIds?: string[]
@@ -64,6 +65,7 @@ export async function saveSessionState(
6465
prune: {
6566
tools: Object.fromEntries(sessionState.prune.tools),
6667
messages: Object.fromEntries(sessionState.prune.messages),
68+
origins: Object.fromEntries(sessionState.prune.origins),
6769
},
6870
compressSummaries: sessionState.compressSummaries,
6971
stats: sessionState.stats,

lib/state/state.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
countTurns,
88
resetOnCompaction,
99
loadPruneMap,
10+
loadPruneOriginMap,
1011
} from "./utils"
1112
import { getLastUserMessage } from "../shared-utils"
1213

@@ -67,6 +68,7 @@ export function createSessionState(): SessionState {
6768
prune: {
6869
tools: new Map<string, number>(),
6970
messages: new Map<string, number>(),
71+
origins: new Map(),
7072
},
7173
compressSummaries: [],
7274
stats: {
@@ -97,6 +99,7 @@ export function resetSessionState(state: SessionState): void {
9799
state.prune = {
98100
tools: new Map<string, number>(),
99101
messages: new Map<string, number>(),
102+
origins: new Map(),
100103
}
101104
state.compressSummaries = []
102105
state.stats = {
@@ -151,6 +154,7 @@ export async function ensureSessionInitialized(
151154

152155
state.prune.tools = loadPruneMap(persisted.prune.tools, persisted.prune.toolIds)
153156
state.prune.messages = loadPruneMap(persisted.prune.messages, persisted.prune.messageIds)
157+
state.prune.origins = loadPruneOriginMap(persisted.prune.origins)
154158
state.compressSummaries = persisted.compressSummaries || []
155159
state.stats = {
156160
pruneTokenCounter: persisted.stats?.pruneTokenCounter || 0,

lib/state/types.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,23 @@ export interface CompressSummary {
2727
summary: string
2828
}
2929

30+
export type PruneOriginSource =
31+
| "prune"
32+
| "distill"
33+
| "sweep"
34+
| "deduplication"
35+
| "supersedeWrites"
36+
| "purgeErrors"
37+
38+
export interface PruneOrigin {
39+
source: PruneOriginSource
40+
originMessageId: string
41+
}
42+
3043
export interface Prune {
3144
tools: Map<string, number>
3245
messages: Map<string, number>
46+
origins: Map<string, PruneOrigin>
3347
}
3448

3549
export interface PendingManualTrigger {

0 commit comments

Comments
 (0)