Merge remote-tracking branch 'origin/dev' into opencode/mighty-wolf
This commit is contained in:
@@ -767,4 +767,18 @@ describe("createChatMessageHandler - TUI variant passthrough", () => {
|
||||
expect(output.message["model"]).toBeUndefined()
|
||||
expect(getSessionModel("test-session")).toEqual(nextModel)
|
||||
})
|
||||
|
||||
test("strips legacy ZWSP-prefixed agent names from persisted prompt body session state (GH-3259)", async () => {
|
||||
//#given - persisted prompt body from v3.14.0-v3.16.0 may contain ZWSP-prefixed agent
|
||||
const args = createMockHandlerArgs()
|
||||
const handler = createChatMessageHandler(args)
|
||||
const input = createMockInput("\u200B\u200BHephaestus - Deep Agent")
|
||||
const output = createMockOutput()
|
||||
|
||||
//#when
|
||||
await handler(input, output)
|
||||
|
||||
//#then
|
||||
expect(getSessionAgent("test-session")).toBe("Hephaestus - Deep Agent")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -81,9 +81,9 @@ function getStoredMainSessionModel(
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (output.message["model"] !== undefined) {
|
||||
return undefined
|
||||
}
|
||||
// Removed: `output.message["model"] !== undefined` guard was unreachable.
|
||||
// OpenCode always populates output.message.model before triggering chat.message,
|
||||
// so the guard short-circuited every time, preventing session model recovery.
|
||||
|
||||
if (hasExplicitAgentModelOverride(input.agent, pluginConfig)) {
|
||||
return undefined
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { HookName, OhMyOpenCodeConfig } from "../../config"
|
||||
import type { BackgroundManager } from "../../features/background-agent"
|
||||
import type { ModelFallbackControllerAccessor } from "../../hooks/model-fallback"
|
||||
import type { PluginContext } from "../types"
|
||||
import type { ModelCacheState } from "../../plugin-state"
|
||||
@@ -11,16 +12,18 @@ export function createCoreHooks(args: {
|
||||
ctx: PluginContext
|
||||
pluginConfig: OhMyOpenCodeConfig
|
||||
modelCacheState: ModelCacheState
|
||||
backgroundManager: BackgroundManager
|
||||
modelFallbackControllerAccessor?: ModelFallbackControllerAccessor
|
||||
isHookEnabled: (hookName: HookName) => boolean
|
||||
safeHookEnabled: boolean
|
||||
}) {
|
||||
const { ctx, pluginConfig, modelCacheState, modelFallbackControllerAccessor, isHookEnabled, safeHookEnabled } = args
|
||||
const { ctx, pluginConfig, modelCacheState, backgroundManager, modelFallbackControllerAccessor, isHookEnabled, safeHookEnabled } = args
|
||||
|
||||
const session = createSessionHooks({
|
||||
ctx,
|
||||
pluginConfig,
|
||||
modelCacheState,
|
||||
backgroundManager,
|
||||
modelFallbackControllerAccessor,
|
||||
isHookEnabled,
|
||||
safeHookEnabled,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { OhMyOpenCodeConfig, HookName } from "../../config"
|
||||
import type { BackgroundManager } from "../../features/background-agent"
|
||||
import type { ModelFallbackControllerAccessor } from "../../hooks/model-fallback"
|
||||
import type { ModelCacheState } from "../../plugin-state"
|
||||
import type { PluginContext } from "../types"
|
||||
@@ -70,11 +71,12 @@ export function createSessionHooks(args: {
|
||||
ctx: PluginContext
|
||||
pluginConfig: OhMyOpenCodeConfig
|
||||
modelCacheState: ModelCacheState
|
||||
backgroundManager: BackgroundManager
|
||||
modelFallbackControllerAccessor?: ModelFallbackControllerAccessor
|
||||
isHookEnabled: (hookName: HookName) => boolean
|
||||
safeHookEnabled: boolean
|
||||
}): SessionHooks {
|
||||
const { ctx, pluginConfig, modelCacheState, modelFallbackControllerAccessor, isHookEnabled, safeHookEnabled } = args
|
||||
const { ctx, pluginConfig, modelCacheState, backgroundManager, modelFallbackControllerAccessor, isHookEnabled, safeHookEnabled } = args
|
||||
const safeHook = <T>(hookName: HookName, factory: () => T): T | null =>
|
||||
safeCreateHook(hookName, factory, { enabled: safeHookEnabled })
|
||||
|
||||
@@ -211,6 +213,7 @@ export function createSessionHooks(args: {
|
||||
createRalphLoopHook(ctx, {
|
||||
config: pluginConfig.ralph_loop,
|
||||
checkSessionExists: async (sessionId) => await sessionExists(sessionId),
|
||||
backgroundManager,
|
||||
}))
|
||||
: null
|
||||
|
||||
|
||||
@@ -85,4 +85,68 @@ describe("createSkillContext", () => {
|
||||
getSystemMcpServerNamesSpy.mockRestore()
|
||||
}
|
||||
})
|
||||
|
||||
it("excludes discovered dev-browser skill when browser provider is playwright", async () => {
|
||||
// given
|
||||
const discoveredDevBrowserSkill = {
|
||||
name: "dev-browser",
|
||||
definition: { description: "Discovered dev-browser skill" },
|
||||
scope: "user" as const,
|
||||
}
|
||||
|
||||
const discoverConfigSourceSkillsSpy = spyOn(
|
||||
skillLoader,
|
||||
"discoverConfigSourceSkills",
|
||||
).mockResolvedValue([])
|
||||
const discoverUserClaudeSkillsSpy = spyOn(
|
||||
skillLoader,
|
||||
"discoverUserClaudeSkills",
|
||||
).mockResolvedValue([discoveredDevBrowserSkill])
|
||||
const discoverProjectClaudeSkillsSpy = spyOn(
|
||||
skillLoader,
|
||||
"discoverProjectClaudeSkills",
|
||||
).mockResolvedValue([])
|
||||
const discoverOpencodeGlobalSkillsSpy = spyOn(
|
||||
skillLoader,
|
||||
"discoverOpencodeGlobalSkills",
|
||||
).mockResolvedValue([])
|
||||
const discoverProjectAgentsSkillsSpy = spyOn(
|
||||
skillLoader,
|
||||
"discoverProjectAgentsSkills",
|
||||
).mockResolvedValue([])
|
||||
const discoverGlobalAgentsSkillsSpy = spyOn(
|
||||
skillLoader,
|
||||
"discoverGlobalAgentsSkills",
|
||||
).mockResolvedValue([])
|
||||
const getSystemMcpServerNamesSpy = spyOn(
|
||||
mcpLoader,
|
||||
"getSystemMcpServerNames",
|
||||
).mockReturnValue(new Set<string>())
|
||||
|
||||
const pluginConfig = OhMyOpenCodeConfigSchema.parse({
|
||||
browser_automation_engine: { provider: "playwright" },
|
||||
})
|
||||
|
||||
try {
|
||||
// when
|
||||
const result = await createSkillContext({
|
||||
directory: testDirectory,
|
||||
pluginConfig,
|
||||
})
|
||||
|
||||
// then
|
||||
expect(result.browserProvider).toBe("playwright")
|
||||
expect(result.mergedSkills.some((skill) => skill.name === "playwright")).toBe(true)
|
||||
expect(result.mergedSkills.some((skill) => skill.name === "dev-browser")).toBe(false)
|
||||
expect(result.availableSkills.some((skill) => skill.name === "dev-browser")).toBe(false)
|
||||
} finally {
|
||||
discoverConfigSourceSkillsSpy.mockRestore()
|
||||
discoverUserClaudeSkillsSpy.mockRestore()
|
||||
discoverProjectClaudeSkillsSpy.mockRestore()
|
||||
discoverOpencodeGlobalSkillsSpy.mockRestore()
|
||||
discoverProjectAgentsSkillsSpy.mockRestore()
|
||||
discoverGlobalAgentsSkillsSpy.mockRestore()
|
||||
getSystemMcpServerNamesSpy.mockRestore()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -26,7 +26,7 @@ export type SkillContext = {
|
||||
disabledSkills: Set<string>
|
||||
}
|
||||
|
||||
const PROVIDER_GATED_SKILL_NAMES = new Set(["agent-browser", "playwright"])
|
||||
const PROVIDER_GATED_SKILL_NAMES = new Set(["agent-browser", "dev-browser", "playwright"])
|
||||
|
||||
function mapScopeToLocation(scope: SkillScope): AvailableSkill["location"] {
|
||||
if (scope === "user" || scope === "opencode") return "user"
|
||||
|
||||
Reference in New Issue
Block a user