Merge pull request #3605 from Jay1/jay/gpt-5-5-native-sisyphus
feat(agents): add GPT-5.5 native Sisyphus support
This commit is contained in:
@@ -23,6 +23,23 @@ describe("getHephaestusPromptSource", () => {
|
||||
expect(source3).toBe("gpt-5-4");
|
||||
});
|
||||
|
||||
test("returns 'gpt-5-4' for gpt-5.5 models", () => {
|
||||
// given
|
||||
const model1 = "openai/gpt-5.5";
|
||||
const model2 = "openai/gpt-5-5";
|
||||
const model3 = "github-copilot/gpt-5.5";
|
||||
|
||||
// when
|
||||
const source1 = getHephaestusPromptSource(model1);
|
||||
const source2 = getHephaestusPromptSource(model2);
|
||||
const source3 = getHephaestusPromptSource(model3);
|
||||
|
||||
// then
|
||||
expect(source1).toBe("gpt-5-4");
|
||||
expect(source2).toBe("gpt-5-4");
|
||||
expect(source3).toBe("gpt-5-4");
|
||||
});
|
||||
|
||||
test("returns 'gpt-5-3-codex' for GPT 5.3 Codex models", () => {
|
||||
// given
|
||||
const model1 = "openai/gpt-5.3-codex";
|
||||
@@ -96,6 +113,19 @@ describe("getHephaestusPrompt", () => {
|
||||
expect(prompt).toContain("<tool_usage_rules>");
|
||||
});
|
||||
|
||||
test("GPT 5.5 model returns GPT-5.4 optimized prompt", () => {
|
||||
// given
|
||||
const model = "openai/gpt-5.5";
|
||||
|
||||
// when
|
||||
const prompt = getHephaestusPrompt(model);
|
||||
|
||||
// then
|
||||
expect(prompt).toContain("You build context by examining");
|
||||
expect(prompt).toContain("Never chain together bash commands");
|
||||
expect(prompt).toContain("<tool_usage_rules>");
|
||||
});
|
||||
|
||||
test("GPT 5.3-codex model returns GPT-5.3 prompt", () => {
|
||||
// given
|
||||
const model = "openai/gpt-5.3-codex";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AgentConfig } from "@opencode-ai/sdk";
|
||||
import type { AgentMode, AgentPromptMetadata } from "../types";
|
||||
import { isGpt5_4Model, isGpt5_3CodexModel } from "../types";
|
||||
import { isGpt5_3CodexModel, isGptNativeSisyphusModel } from "../types";
|
||||
import type {
|
||||
AvailableAgent,
|
||||
AvailableTool,
|
||||
@@ -21,7 +21,7 @@ export type HephaestusPromptSource = "gpt-5-4" | "gpt-5-3-codex" | "gpt";
|
||||
export function getHephaestusPromptSource(
|
||||
model?: string,
|
||||
): HephaestusPromptSource {
|
||||
if (model && isGpt5_4Model(model)) {
|
||||
if (model && isGptNativeSisyphusModel(model)) {
|
||||
return "gpt-5-4";
|
||||
}
|
||||
if (model && isGpt5_3CodexModel(model)) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AgentConfig } from "@opencode-ai/sdk";
|
||||
import type { AgentMode, AgentPromptMetadata } from "./types";
|
||||
import { isGptModel, isGeminiModel, isGpt5_4Model } from "./types";
|
||||
import { isGptModel, isGeminiModel, isGptNativeSisyphusModel } from "./types";
|
||||
import {
|
||||
buildGeminiToolMandate,
|
||||
buildGeminiDelegationOverride,
|
||||
@@ -480,7 +480,7 @@ export function createSisyphusAgent(
|
||||
const categories = availableCategories ?? [];
|
||||
const agents = availableAgents ?? [];
|
||||
|
||||
if (isGpt5_4Model(model)) {
|
||||
if (isGptNativeSisyphusModel(model)) {
|
||||
const prompt = buildGpt54SisyphusPrompt(
|
||||
model,
|
||||
agents,
|
||||
|
||||
+37
-17
@@ -1,26 +1,46 @@
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import { isGptModel, isGeminiModel, isGlmModel, isGpt5_4Model, isMiniMaxModel } from "./types";
|
||||
import {
|
||||
isGptModel,
|
||||
isGeminiModel,
|
||||
isGlmModel,
|
||||
isGptNativeSisyphusModel,
|
||||
isMiniMaxModel,
|
||||
} from "./types";
|
||||
|
||||
describe("isGpt5_4Model", () => {
|
||||
test("detects gpt-5.4 models", () => {
|
||||
expect(isGpt5_4Model("openai/gpt-5.4")).toBe(true);
|
||||
expect(isGpt5_4Model("openai/gpt-5-4")).toBe(true);
|
||||
expect(isGpt5_4Model("openai/gpt-5.4-codex")).toBe(true);
|
||||
expect(isGpt5_4Model("github-copilot/gpt-5.4")).toBe(true);
|
||||
expect(isGpt5_4Model("venice/gpt-5-4")).toBe(true);
|
||||
describe("isGptNativeSisyphusModel", () => {
|
||||
test("allows GPT-5.x where x >= 4", () => {
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-5.4")).toBe(true);
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-5-4")).toBe(true);
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-5.5")).toBe(true);
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-5-5")).toBe(true);
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-5.9")).toBe(true);
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-5-9")).toBe(true);
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-5.10")).toBe(true);
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-5-10")).toBe(true);
|
||||
});
|
||||
|
||||
test("does not match other GPT models", () => {
|
||||
expect(isGpt5_4Model("openai/gpt-5.3-codex")).toBe(false);
|
||||
expect(isGpt5_4Model("openai/gpt-5.1")).toBe(false);
|
||||
expect(isGpt5_4Model("openai/gpt-4o")).toBe(false);
|
||||
expect(isGpt5_4Model("github-copilot/gpt-4o")).toBe(false);
|
||||
test("allows with various providers and suffixes", () => {
|
||||
expect(isGptNativeSisyphusModel("github-copilot/gpt-5.4")).toBe(true);
|
||||
expect(isGptNativeSisyphusModel("venice/gpt-5-4")).toBe(true);
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-5.4-codex")).toBe(true);
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-5.5-mini")).toBe(true);
|
||||
});
|
||||
|
||||
test("does not match non-GPT models", () => {
|
||||
expect(isGpt5_4Model("anthropic/claude-opus-4-7")).toBe(false);
|
||||
expect(isGpt5_4Model("google/gemini-3.1-pro")).toBe(false);
|
||||
expect(isGpt5_4Model("openai/o1")).toBe(false);
|
||||
test("rejects GPT-5.x where x < 4", () => {
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-5.3-codex")).toBe(false);
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-5.1")).toBe(false);
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-5-0")).toBe(false);
|
||||
});
|
||||
|
||||
test("rejects other GPT models", () => {
|
||||
expect(isGptNativeSisyphusModel("openai/gpt-4o")).toBe(false);
|
||||
expect(isGptNativeSisyphusModel("github-copilot/gpt-4o")).toBe(false);
|
||||
});
|
||||
|
||||
test("rejects non-GPT models", () => {
|
||||
expect(isGptNativeSisyphusModel("anthropic/claude-opus-4-7")).toBe(false);
|
||||
expect(isGptNativeSisyphusModel("google/gemini-3.1-pro")).toBe(false);
|
||||
expect(isGptNativeSisyphusModel("openai/o1")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+4
-2
@@ -79,9 +79,11 @@ export function isGptModel(model: string): boolean {
|
||||
return modelName.includes("gpt");
|
||||
}
|
||||
|
||||
export function isGpt5_4Model(model: string): boolean {
|
||||
const GPT_NATIVE_SISYPHUS_RE = /gpt-5[.-](?:[4-9]|\d{2,})/i;
|
||||
|
||||
export function isGptNativeSisyphusModel(model: string): boolean {
|
||||
const modelName = extractModelName(model).toLowerCase();
|
||||
return modelName.includes("gpt-5.4") || modelName.includes("gpt-5-4");
|
||||
return GPT_NATIVE_SISYPHUS_RE.test(modelName);
|
||||
}
|
||||
|
||||
export function isGpt5_3CodexModel(model: string): boolean {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { isGptModel, isGpt5_4Model } from "../../agents/types"
|
||||
import { isGptModel, isGptNativeSisyphusModel } from "../../agents/types"
|
||||
import {
|
||||
getSessionAgent,
|
||||
resolveRegisteredAgentName,
|
||||
@@ -11,8 +11,8 @@ import { getAgentConfigKey } from "../../shared/agent-display-names"
|
||||
const TOAST_TITLE = "NEVER Use Sisyphus with GPT"
|
||||
const TOAST_MESSAGE = [
|
||||
"Sisyphus works best with Claude Opus, and works fine with Kimi/GLM models.",
|
||||
"Do NOT use Sisyphus with GPT (except GPT-5.4 which has specialized support).",
|
||||
"For GPT models (other than 5.4), always use Hephaestus.",
|
||||
"Do NOT use Sisyphus with GPT (except GPT-5.4 and GPT-5.5 which have specialized support).",
|
||||
"For other GPT models, always use Hephaestus.",
|
||||
].join("\n")
|
||||
function showToast(ctx: PluginInput, sessionID: string): void {
|
||||
ctx.client.tui.showToast({
|
||||
@@ -43,7 +43,7 @@ export function createNoSisyphusGptHook(ctx: PluginInput) {
|
||||
const agentKey = getAgentConfigKey(rawAgent)
|
||||
const modelID = input.model?.modelID
|
||||
|
||||
if (agentKey === "sisyphus" && modelID && isGptModel(modelID) && !isGpt5_4Model(modelID)) {
|
||||
if (agentKey === "sisyphus" && modelID && isGptModel(modelID) && !isGptNativeSisyphusModel(modelID)) {
|
||||
showToast(ctx, input.sessionID)
|
||||
input.agent = resolveRegisteredAgentName("hephaestus") ?? "hephaestus"
|
||||
if (output?.message) {
|
||||
|
||||
@@ -43,7 +43,7 @@ describe("no-sisyphus-gpt hook", () => {
|
||||
expect(showToast.mock.calls[0]?.[0]).toMatchObject({
|
||||
body: {
|
||||
title: "NEVER Use Sisyphus with GPT",
|
||||
message: expect.stringContaining("For GPT models (other than 5.4), always use Hephaestus."),
|
||||
message: expect.stringContaining("For other GPT models, always use Hephaestus."),
|
||||
variant: "error",
|
||||
},
|
||||
})
|
||||
@@ -70,6 +70,27 @@ describe("no-sisyphus-gpt hook", () => {
|
||||
expect(output.message.agent).toBeUndefined()
|
||||
})
|
||||
|
||||
test("does not show toast for gpt-5.5 model (native Sisyphus support)", async () => {
|
||||
// given - sisyphus with gpt-5.5 model (should be allowed)
|
||||
const showToast = spyOn({ fn: async () => ({}) }, "fn")
|
||||
const hook = createNoSisyphusGptHook({
|
||||
client: { tui: { showToast } },
|
||||
} as any)
|
||||
|
||||
const output = createOutput()
|
||||
|
||||
// when - chat.message runs with gpt-5.5
|
||||
await hook["chat.message"]?.({
|
||||
sessionID: "ses_gpt55",
|
||||
agent: SISYPHUS_DISPLAY,
|
||||
model: { providerID: "openai", modelID: "gpt-5.5" },
|
||||
}, output)
|
||||
|
||||
// then - no toast, agent NOT switched to Hephaestus
|
||||
expect(showToast).toHaveBeenCalledTimes(0)
|
||||
expect(output.message.agent).toBeUndefined()
|
||||
})
|
||||
|
||||
test("does not show toast for non-gpt model", async () => {
|
||||
// given - sisyphus with claude model
|
||||
const showToast = spyOn({ fn: async () => ({}) }, "fn")
|
||||
|
||||
Reference in New Issue
Block a user