fix(tools/skill): invalidate skill cache at session boundary
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
const seenSessionIDs = new Set<string>()
|
||||||
|
|
||||||
|
export function shouldInvalidateSkillCacheForSession(sessionID?: string): boolean {
|
||||||
|
if (!sessionID || seenSessionIDs.has(sessionID)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
seenSessionIDs.add(sessionID)
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ import { afterEach, beforeEach, describe, expect, it, mock, spyOn } from "bun:te
|
|||||||
import type { ToolContext } from "@opencode-ai/plugin/tool"
|
import type { ToolContext } from "@opencode-ai/plugin/tool"
|
||||||
import type { LoadedSkill } from "../../features/opencode-skill-loader/types"
|
import type { LoadedSkill } from "../../features/opencode-skill-loader/types"
|
||||||
import * as skillContent from "../../features/opencode-skill-loader/skill-content"
|
import * as skillContent from "../../features/opencode-skill-loader/skill-content"
|
||||||
import * as commandDiscovery from "../slashcommand/command-discovery"
|
|
||||||
|
|
||||||
const discoverCommandsSync = mock(() => [])
|
const discoverCommandsSync = mock(() => [])
|
||||||
|
|
||||||
@@ -94,19 +93,21 @@ describe("createSkillTool", () => {
|
|||||||
expect(getAllSkills.mock.calls.length).toBe(baselineGetAllSkillsCalls + 1)
|
expect(getAllSkills.mock.calls.length).toBe(baselineGetAllSkillsCalls + 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("does not clear the shared skill cache during description or execute refresh", async () => {
|
it("clears the shared skill cache once on first execute in a session", async () => {
|
||||||
// given
|
// given
|
||||||
const baselineClearSkillCacheCalls = clearSkillCache.mock.calls.length
|
const baselineClearSkillCacheCalls = clearSkillCache.mock.calls.length
|
||||||
|
const sessionContext = createMockContext("session-clear-once")
|
||||||
|
|
||||||
// when
|
// when
|
||||||
const { createSkillTool } = await import("./tools")
|
const { createSkillTool } = await import("./tools")
|
||||||
const skillTool = createSkillTool({})
|
const skillTool = createSkillTool({})
|
||||||
void skillTool.description
|
void skillTool.description
|
||||||
await flushMicrotasks()
|
await flushMicrotasks()
|
||||||
await skillTool.execute({ name: "lazy-skill" }, mockContext)
|
await skillTool.execute({ name: "lazy-skill" }, sessionContext)
|
||||||
|
await skillTool.execute({ name: "lazy-skill" }, sessionContext)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(clearSkillCache.mock.calls.length).toBe(baselineClearSkillCacheCalls)
|
expect(clearSkillCache.mock.calls.length).toBe(baselineClearSkillCacheCalls + 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("clears the skill discovery cache once per session", async () => {
|
it("clears the skill discovery cache once per session", async () => {
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import { dirname } from "node:path"
|
|||||||
import { tool, type ToolDefinition } from "@opencode-ai/plugin"
|
import { tool, type ToolDefinition } from "@opencode-ai/plugin"
|
||||||
import type { ToolContext } from "@opencode-ai/plugin/tool"
|
import type { ToolContext } from "@opencode-ai/plugin/tool"
|
||||||
import { TOOL_DESCRIPTION_PREFIX } from "./constants"
|
import { TOOL_DESCRIPTION_PREFIX } from "./constants"
|
||||||
|
import { shouldInvalidateSkillCacheForSession } from "./session-skill-cache"
|
||||||
import type { SkillArgs, SkillLoadOptions } from "./types"
|
import type { SkillArgs, SkillLoadOptions } from "./types"
|
||||||
import type { LoadedSkill } from "../../features/opencode-skill-loader"
|
import type { LoadedSkill } from "../../features/opencode-skill-loader"
|
||||||
import { getAllSkills } from "../../features/opencode-skill-loader/skill-content"
|
import { clearSkillCache, getAllSkills } from "../../features/opencode-skill-loader/skill-content"
|
||||||
import { injectGitMasterConfig } from "../../features/opencode-skill-loader/skill-content"
|
import { injectGitMasterConfig } from "../../features/opencode-skill-loader/skill-content"
|
||||||
import { discoverCommandsSync } from "../slashcommand/command-discovery"
|
import { discoverCommandsSync } from "../slashcommand/command-discovery"
|
||||||
import type { CommandInfo } from "../slashcommand/types"
|
import type { CommandInfo } from "../slashcommand/types"
|
||||||
@@ -27,7 +28,11 @@ import {
|
|||||||
export function createSkillTool(options: SkillLoadOptions = {}): ToolDefinition {
|
export function createSkillTool(options: SkillLoadOptions = {}): ToolDefinition {
|
||||||
let cachedDescription: string | null = null
|
let cachedDescription: string | null = null
|
||||||
|
|
||||||
const getSkills = async (): Promise<LoadedSkill[]> => {
|
const getSkills = async (context?: ToolContext): Promise<LoadedSkill[]> => {
|
||||||
|
if (shouldInvalidateSkillCacheForSession(context?.sessionID)) {
|
||||||
|
clearSkillCache()
|
||||||
|
}
|
||||||
|
|
||||||
const discovered = (await getAllSkills({
|
const discovered = (await getAllSkills({
|
||||||
disabledSkills: options?.disabledSkills,
|
disabledSkills: options?.disabledSkills,
|
||||||
browserProvider: options?.browserProvider,
|
browserProvider: options?.browserProvider,
|
||||||
@@ -108,7 +113,7 @@ export function createSkillTool(options: SkillLoadOptions = {}): ToolDefinition
|
|||||||
.describe("Optional arguments or context for command invocation. Example: name='publish', user_message='patch'"),
|
.describe("Optional arguments or context for command invocation. Example: name='publish', user_message='patch'"),
|
||||||
},
|
},
|
||||||
async execute(args: SkillArgs, ctx?: ToolContext) {
|
async execute(args: SkillArgs, ctx?: ToolContext) {
|
||||||
const skills = await getSkills()
|
const skills = await getSkills(ctx)
|
||||||
const commands = getCommands()
|
const commands = getCommands()
|
||||||
cachedDescription = formatCombinedDescription(skills.map(loadedSkillToInfo), commands)
|
cachedDescription = formatCombinedDescription(skills.map(loadedSkillToInfo), commands)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user