-
-
Notifications
You must be signed in to change notification settings - Fork 131
Expand file tree
/
Copy pathtool-cache.ts
More file actions
101 lines (86 loc) · 3.44 KB
/
tool-cache.ts
File metadata and controls
101 lines (86 loc) · 3.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import type { PluginState, ToolStatus } from "./index"
import type { Logger } from "../logger"
import type { ToolTracker } from "../fetch-wrapper/tool-tracker"
/** Maximum number of entries to keep in the tool parameters cache */
const MAX_TOOL_CACHE_SIZE = 500
/**
* Sync tool parameters from OpenCode's session.messages() API.
* This is the single source of truth for tool parameters, replacing
* format-specific parsing from LLM API requests.
*/
export async function syncToolCache(
client: any,
sessionId: string,
state: PluginState,
tracker?: ToolTracker,
protectedTools?: Set<string>,
logger?: Logger
): Promise<void> {
try {
const messagesResponse = await client.session.messages({
path: { id: sessionId },
query: { limit: 500 }
})
const messages = messagesResponse.data || messagesResponse
if (!Array.isArray(messages)) {
return
}
let synced = 0
// Build lowercase set of pruned IDs for comparison (IDs in state may be mixed case)
const prunedIdsLower = tracker
? new Set((state.prunedIds.get(sessionId) ?? []).map(id => id.toLowerCase()))
: null
for (const msg of messages) {
if (!msg.parts) continue
for (const part of msg.parts) {
if (part.type !== "tool" || !part.callID) continue
const id = part.callID.toLowerCase()
// Track tool results for nudge injection
if (tracker && !tracker.seenToolResultIds.has(id)) {
tracker.seenToolResultIds.add(id)
// Only count non-protected tools toward nudge threshold
// Also skip already-pruned tools to avoid re-counting on restart
if ((!part.tool || !protectedTools?.has(part.tool)) && !prunedIdsLower?.has(id)) {
tracker.toolResultCount++
}
}
if (state.toolParameters.has(id)) continue
if (part.tool && protectedTools?.has(part.tool)) continue
const status = part.state?.status as ToolStatus | undefined
state.toolParameters.set(id, {
tool: part.tool,
parameters: part.state?.input ?? {},
status,
error: status === "error" ? part.state?.error : undefined,
})
synced++
}
}
trimToolParametersCache(state)
if (logger && synced > 0) {
logger.debug("tool-cache", "Synced tool parameters from OpenCode", {
sessionId: sessionId.slice(0, 8),
synced
})
}
} catch (error) {
logger?.warn("tool-cache", "Failed to sync tool parameters from OpenCode", {
sessionId: sessionId.slice(0, 8),
error: error instanceof Error ? error.message : String(error)
})
}
}
/**
* Trim the tool parameters cache to prevent unbounded memory growth.
* Uses FIFO eviction - removes oldest entries first.
*/
export function trimToolParametersCache(state: PluginState): void {
if (state.toolParameters.size <= MAX_TOOL_CACHE_SIZE) {
return
}
const keysToRemove = Array.from(state.toolParameters.keys())
.slice(0, state.toolParameters.size - MAX_TOOL_CACHE_SIZE)
for (const key of keysToRemove) {
state.toolParameters.delete(key)
}
}