Files
oh-my-opencode/src/hooks/auto-slash-command/processed-command-store.ts
T
YeonGyu-Kim 2b8ae214b6 fix(auto-slash-command): expire duplicate suppression after 30s
Allow legitimate repeated slash commands in long sessions by replacing session-lifetime dedup with a short-lived TTL cache.

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-16 10:38:54 +09:00

56 lines
1.7 KiB
TypeScript

const MAX_PROCESSED_ENTRY_COUNT = 10_000
const PROCESSED_COMMAND_TTL_MS = 30_000
function pruneExpiredEntries(entries: Map<string, number>, now: number): Map<string, number> {
return new Map(Array.from(entries.entries()).filter(([, expiresAt]) => expiresAt > now))
}
function trimProcessedEntries(entries: Map<string, number>): Map<string, number> {
if (entries.size <= MAX_PROCESSED_ENTRY_COUNT) {
return entries
}
return new Map(
Array.from(entries.entries())
.sort((left, right) => left[1] - right[1])
.slice(Math.floor(entries.size / 2))
)
}
function removeSessionEntries(entries: Map<string, number>, sessionID: string): Map<string, number> {
const sessionPrefix = `${sessionID}:`
return new Map(Array.from(entries.entries()).filter(([entry]) => !entry.startsWith(sessionPrefix)))
}
export interface ProcessedCommandStore {
has(commandKey: string): boolean
add(commandKey: string): void
cleanupSession(sessionID: string): void
clear(): void
}
export function createProcessedCommandStore(): ProcessedCommandStore {
let entries = new Map<string, number>()
return {
has(commandKey: string): boolean {
const now = Date.now()
entries = pruneExpiredEntries(entries, now)
return entries.has(commandKey)
},
add(commandKey: string): void {
const now = Date.now()
entries = pruneExpiredEntries(entries, now)
entries.delete(commandKey)
entries.set(commandKey, now + PROCESSED_COMMAND_TTL_MS)
entries = trimProcessedEntries(entries)
},
cleanupSession(sessionID: string): void {
entries = removeSessionEntries(entries, sessionID)
},
clear(): void {
entries.clear()
},
}
}