refactor: remove dead ultrawork model override code
Remove ultrawork-model-override hook and per-agent ultrawork model swap config that relied on zen opencode.ai free tier (no longer functional). Removed: - src/hooks/ultrawork-model-override/ (hook, test, index) - ultrawork field from AgentOverrideConfigSchema - ultrawork-model-override from HookNameSchema - UltraworkConfig type from model-fallback-types - Non-max20 sonnet+ultrawork-opus codepath from model-fallback - Claude subscription model table from installation docs - All references in plugin-interface, create-session-hooks, schema.json - Related test cases and updated snapshots
This commit is contained in:
@@ -80,7 +80,6 @@
|
|||||||
"non-interactive-env",
|
"non-interactive-env",
|
||||||
"interactive-bash-session",
|
"interactive-bash-session",
|
||||||
"thinking-block-validator",
|
"thinking-block-validator",
|
||||||
"ultrawork-model-override",
|
|
||||||
"ralph-loop",
|
"ralph-loop",
|
||||||
"category-skill-reminder",
|
"category-skill-reminder",
|
||||||
"compaction-context-injector",
|
"compaction-context-injector",
|
||||||
@@ -279,21 +278,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -467,21 +451,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -655,21 +624,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -843,21 +797,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -1031,21 +970,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -1219,21 +1143,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -1407,21 +1316,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -1595,21 +1489,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -1783,21 +1662,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -1971,21 +1835,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -2159,21 +2008,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -2347,21 +2181,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -2535,21 +2354,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -2723,21 +2527,6 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"ultrawork": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"variant": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"reasoningEffort": {
|
"reasoningEffort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -3373,4 +3162,4 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,15 +68,6 @@ Ask the user these questions to determine CLI options:
|
|||||||
|
|
||||||
**Provider Priority**: Native (anthropic/, openai/, google/) > GitHub Copilot > OpenCode Zen > Z.ai Coding Plan
|
**Provider Priority**: Native (anthropic/, openai/, google/) > GitHub Copilot > OpenCode Zen > Z.ai Coding Plan
|
||||||
|
|
||||||
#### Claude Subscription Model Assignments
|
|
||||||
|
|
||||||
| Subscription | Sisyphus (Daily) | Ultrawork Mode |
|
|
||||||
| ------------ | ---------------- | -------------- |
|
|
||||||
| **max20** | `anthropic/claude-opus-4-6` (max) | Already on Opus — no override |
|
|
||||||
| **standard** | `anthropic/claude-sonnet-4-6` (max) | `anthropic/claude-opus-4-6` (max) |
|
|
||||||
|
|
||||||
Standard Claude subscribers use Sonnet 4.6 for daily driving and automatically switch to Opus 4.6 when ultrawork mode is activated (by typing `ultrawork` or `ulw`).
|
|
||||||
|
|
||||||
MUST STRONGLY WARNING, WHEN USER SAID THEY DON'T HAVE CLAUDE SUBSCRIPTION, SISYPHUS AGENT MIGHT NOT WORK IDEALLY.
|
MUST STRONGLY WARNING, WHEN USER SAID THEY DON'T HAVE CLAUDE SUBSCRIPTION, SISYPHUS AGENT MIGHT NOT WORK IDEALLY.
|
||||||
|
|
||||||
### Step 1: Install OpenCode (if not installed)
|
### Step 1: Install OpenCode (if not installed)
|
||||||
|
|||||||
@@ -94,11 +94,7 @@ exports[`generateModelConfig single native provider uses Claude models when only
|
|||||||
"variant": "max",
|
"variant": "max",
|
||||||
},
|
},
|
||||||
"sisyphus": {
|
"sisyphus": {
|
||||||
"model": "anthropic/claude-sonnet-4-6",
|
"model": "anthropic/claude-opus-4-6",
|
||||||
"ultrawork": {
|
|
||||||
"model": "anthropic/claude-opus-4-6",
|
|
||||||
"variant": "max",
|
|
||||||
},
|
|
||||||
"variant": "max",
|
"variant": "max",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -483,11 +479,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal
|
|||||||
"variant": "max",
|
"variant": "max",
|
||||||
},
|
},
|
||||||
"sisyphus": {
|
"sisyphus": {
|
||||||
"model": "anthropic/claude-sonnet-4-6",
|
"model": "anthropic/claude-opus-4-6",
|
||||||
"ultrawork": {
|
|
||||||
"model": "anthropic/claude-opus-4-6",
|
|
||||||
"variant": "max",
|
|
||||||
},
|
|
||||||
"variant": "max",
|
"variant": "max",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1044,11 +1036,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen
|
|||||||
"variant": "max",
|
"variant": "max",
|
||||||
},
|
},
|
||||||
"sisyphus": {
|
"sisyphus": {
|
||||||
"model": "anthropic/claude-sonnet-4-6",
|
"model": "anthropic/claude-opus-4-6",
|
||||||
"ultrawork": {
|
|
||||||
"model": "anthropic/claude-opus-4-6",
|
|
||||||
"variant": "max",
|
|
||||||
},
|
|
||||||
"variant": "max",
|
"variant": "max",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1192,11 +1180,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + ZAI combinat
|
|||||||
"variant": "max",
|
"variant": "max",
|
||||||
},
|
},
|
||||||
"sisyphus": {
|
"sisyphus": {
|
||||||
"model": "anthropic/claude-sonnet-4-6",
|
"model": "anthropic/claude-opus-4-6",
|
||||||
"ultrawork": {
|
|
||||||
"model": "anthropic/claude-opus-4-6",
|
|
||||||
"variant": "max",
|
|
||||||
},
|
|
||||||
"variant": "max",
|
"variant": "max",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1257,11 +1241,7 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi
|
|||||||
"variant": "max",
|
"variant": "max",
|
||||||
},
|
},
|
||||||
"sisyphus": {
|
"sisyphus": {
|
||||||
"model": "anthropic/claude-sonnet-4-6",
|
"model": "anthropic/claude-opus-4-6",
|
||||||
"ultrawork": {
|
|
||||||
"model": "anthropic/claude-opus-4-6",
|
|
||||||
"variant": "max",
|
|
||||||
},
|
|
||||||
"variant": "max",
|
"variant": "max",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1405,11 +1385,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe
|
|||||||
"variant": "max",
|
"variant": "max",
|
||||||
},
|
},
|
||||||
"sisyphus": {
|
"sisyphus": {
|
||||||
"model": "anthropic/claude-sonnet-4-6",
|
"model": "anthropic/claude-opus-4-6",
|
||||||
"ultrawork": {
|
|
||||||
"model": "anthropic/claude-opus-4-6",
|
|
||||||
"variant": "max",
|
|
||||||
},
|
|
||||||
"variant": "max",
|
"variant": "max",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -240,52 +240,6 @@ describe("config-manager ANTIGRAVITY_PROVIDER_CONFIG", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("generateOmoConfig - model fallback system", () => {
|
describe("generateOmoConfig - model fallback system", () => {
|
||||||
test("generates sonnet model with ultrawork opus for Claude standard subscription", () => {
|
|
||||||
// #given user has Claude standard subscription (not max20)
|
|
||||||
const config: InstallConfig = {
|
|
||||||
hasClaude: true,
|
|
||||||
isMax20: false,
|
|
||||||
hasOpenAI: false,
|
|
||||||
hasGemini: false,
|
|
||||||
hasCopilot: false,
|
|
||||||
hasOpencodeZen: false,
|
|
||||||
hasZaiCodingPlan: false,
|
|
||||||
hasKimiForCoding: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// #when generating config
|
|
||||||
const result = generateOmoConfig(config)
|
|
||||||
|
|
||||||
// #then Sisyphus uses sonnet for daily driving with ultrawork opus override
|
|
||||||
const sisyphus = (result.agents as Record<string, { model: string; variant?: string; ultrawork?: { model: string; variant?: string } }>).sisyphus
|
|
||||||
expect(result.$schema).toBe("https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json")
|
|
||||||
expect(sisyphus.model).toBe("anthropic/claude-sonnet-4-6")
|
|
||||||
expect(sisyphus.variant).toBe("max")
|
|
||||||
expect(sisyphus.ultrawork).toEqual({ model: "anthropic/claude-opus-4-6", variant: "max" })
|
|
||||||
})
|
|
||||||
|
|
||||||
test("generates native opus models without ultrawork when Claude max20 subscription", () => {
|
|
||||||
// #given user has Claude max20 subscription
|
|
||||||
const config: InstallConfig = {
|
|
||||||
hasClaude: true,
|
|
||||||
isMax20: true,
|
|
||||||
hasOpenAI: false,
|
|
||||||
hasGemini: false,
|
|
||||||
hasCopilot: false,
|
|
||||||
hasOpencodeZen: false,
|
|
||||||
hasZaiCodingPlan: false,
|
|
||||||
hasKimiForCoding: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// #when generating config
|
|
||||||
const result = generateOmoConfig(config)
|
|
||||||
|
|
||||||
// #then Sisyphus uses opus directly, no ultrawork override needed
|
|
||||||
const sisyphus = (result.agents as Record<string, { model: string; ultrawork?: unknown }>).sisyphus
|
|
||||||
expect(sisyphus.model).toBe("anthropic/claude-opus-4-6")
|
|
||||||
expect(sisyphus.ultrawork).toBeUndefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
test("uses github-copilot sonnet fallback when only copilot available", () => {
|
test("uses github-copilot sonnet fallback when only copilot available", () => {
|
||||||
// #given user has only copilot (no max plan)
|
// #given user has only copilot (no max plan)
|
||||||
const config: InstallConfig = {
|
const config: InstallConfig = {
|
||||||
|
|||||||
@@ -11,15 +11,9 @@ export interface ProviderAvailability {
|
|||||||
isMaxPlan: boolean
|
isMaxPlan: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UltraworkConfig {
|
|
||||||
model: string
|
|
||||||
variant?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AgentConfig {
|
export interface AgentConfig {
|
||||||
model: string
|
model: string
|
||||||
variant?: string
|
variant?: string
|
||||||
ultrawork?: UltraworkConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CategoryConfig {
|
export interface CategoryConfig {
|
||||||
|
|||||||
@@ -76,15 +76,6 @@ export function generateModelConfig(config: InstallConfig): GeneratedOmoConfig {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avail.native.claude && !avail.isMaxPlan) {
|
|
||||||
agents[role] = {
|
|
||||||
model: "anthropic/claude-sonnet-4-6",
|
|
||||||
variant: "max",
|
|
||||||
ultrawork: { model: "anthropic/claude-opus-4-6", variant: "max" },
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolved = resolveModelFromChain(fallbackChain, avail)
|
const resolved = resolveModelFromChain(fallbackChain, avail)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
const variant = resolved.variant ?? req.variant
|
const variant = resolved.variant ?? req.variant
|
||||||
|
|||||||
@@ -32,11 +32,6 @@ export const AgentOverrideConfigSchema = z.object({
|
|||||||
budgetTokens: z.number().optional(),
|
budgetTokens: z.number().optional(),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
/** Ultrawork model override configuration. */
|
|
||||||
ultrawork: z.object({
|
|
||||||
model: z.string(),
|
|
||||||
variant: z.string().optional(),
|
|
||||||
}).optional(),
|
|
||||||
/** Reasoning effort level (OpenAI). Overrides category and default settings. */
|
/** Reasoning effort level (OpenAI). Overrides category and default settings. */
|
||||||
reasoningEffort: z.enum(["low", "medium", "high", "xhigh"]).optional(),
|
reasoningEffort: z.enum(["low", "medium", "high", "xhigh"]).optional(),
|
||||||
/** Text verbosity level. */
|
/** Text verbosity level. */
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ export const HookNameSchema = z.enum([
|
|||||||
"interactive-bash-session",
|
"interactive-bash-session",
|
||||||
|
|
||||||
"thinking-block-validator",
|
"thinking-block-validator",
|
||||||
"ultrawork-model-override",
|
|
||||||
"ralph-loop",
|
"ralph-loop",
|
||||||
"category-skill-reminder",
|
"category-skill-reminder",
|
||||||
|
|
||||||
|
|||||||
@@ -47,4 +47,3 @@ export { createTasksTodowriteDisablerHook } from "./tasks-todowrite-disabler";
|
|||||||
export { createWriteExistingFileGuardHook } from "./write-existing-file-guard";
|
export { createWriteExistingFileGuardHook } from "./write-existing-file-guard";
|
||||||
export { createHashlineReadEnhancerHook } from "./hashline-read-enhancer";
|
export { createHashlineReadEnhancerHook } from "./hashline-read-enhancer";
|
||||||
|
|
||||||
export { createUltraworkModelOverrideHook } from "./ultrawork-model-override";
|
|
||||||
|
|||||||
@@ -1,217 +0,0 @@
|
|||||||
import { describe, expect, it } from "vitest"
|
|
||||||
import { createUltraworkModelOverrideHook } from "./hook"
|
|
||||||
|
|
||||||
interface ChatParamsInput {
|
|
||||||
agent: string
|
|
||||||
message: {
|
|
||||||
variant: string
|
|
||||||
model?: { providerID?: string; modelID?: string }
|
|
||||||
}
|
|
||||||
sessionID?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChatParamsOutput {
|
|
||||||
// Not used by this hook
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMockParams(overrides: {
|
|
||||||
agent?: string
|
|
||||||
variant?: string
|
|
||||||
model?: { providerID?: string; modelID?: string }
|
|
||||||
sessionID?: string
|
|
||||||
}): { input: ChatParamsInput; output: ChatParamsOutput } {
|
|
||||||
const agent = overrides.agent ?? "sisyphus"
|
|
||||||
const variant = overrides.variant ?? "max"
|
|
||||||
const model = overrides.model
|
|
||||||
const sessionID = overrides.sessionID
|
|
||||||
|
|
||||||
return {
|
|
||||||
input: {
|
|
||||||
agent,
|
|
||||||
message: { variant, model },
|
|
||||||
sessionID,
|
|
||||||
},
|
|
||||||
output: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("createUltraworkModelOverrideHook", () => {
|
|
||||||
describe("model swap works", () => {
|
|
||||||
it("variant max, ultrawork config exists → model swapped", async () => {
|
|
||||||
// given
|
|
||||||
const agents = {
|
|
||||||
sisyphus: {
|
|
||||||
ultrawork: {
|
|
||||||
model: "openai/gpt-5.2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const hook = createUltraworkModelOverrideHook({ agents })
|
|
||||||
const { input, output } = createMockParams({})
|
|
||||||
|
|
||||||
// when
|
|
||||||
await hook["chat.params"](input, output)
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(input.message.model).toEqual({
|
|
||||||
providerID: "openai",
|
|
||||||
modelID: "gpt-5.2",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("no-op on non-max variant", () => {
|
|
||||||
it("variant high → model unchanged", async () => {
|
|
||||||
// given
|
|
||||||
const agents = {
|
|
||||||
sisyphus: {
|
|
||||||
ultrawork: {
|
|
||||||
model: "openai/gpt-5.2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const hook = createUltraworkModelOverrideHook({ agents })
|
|
||||||
const { input, output } = createMockParams({ variant: "high" })
|
|
||||||
|
|
||||||
// when
|
|
||||||
await hook["chat.params"](input, output)
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(input.message.model).toBeUndefined()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("no-op without config", () => {
|
|
||||||
it("agent has no ultrawork config → model unchanged", async () => {
|
|
||||||
// given
|
|
||||||
const agents = {
|
|
||||||
hephaestus: {
|
|
||||||
ultrawork: {
|
|
||||||
model: "openai/gpt-5.2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const hook = createUltraworkModelOverrideHook({ agents })
|
|
||||||
const { input, output } = createMockParams({})
|
|
||||||
|
|
||||||
// when
|
|
||||||
await hook["chat.params"](input, output)
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(input.message.model).toBeUndefined()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("empty ultrawork config", () => {
|
|
||||||
it("ultrawork: {} → no-op (model required)", async () => {
|
|
||||||
// given
|
|
||||||
const agents = {
|
|
||||||
sisyphus: {
|
|
||||||
ultrawork: undefined,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const hook = createUltraworkModelOverrideHook({ agents })
|
|
||||||
const { input, output } = createMockParams({})
|
|
||||||
|
|
||||||
// when
|
|
||||||
await hook["chat.params"](input, output)
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(input.message.model).toBeUndefined()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("model string parsing", () => {
|
|
||||||
it("openai/gpt-5.2 → { providerID: openai, modelID: gpt-5.2 }", async () => {
|
|
||||||
// given
|
|
||||||
const agents = {
|
|
||||||
sisyphus: {
|
|
||||||
ultrawork: {
|
|
||||||
model: "openai/gpt-5.2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const hook = createUltraworkModelOverrideHook({ agents })
|
|
||||||
const { input, output } = createMockParams({})
|
|
||||||
|
|
||||||
// when
|
|
||||||
await hook["chat.params"](input, output)
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(input.message.model).toEqual({
|
|
||||||
providerID: "openai",
|
|
||||||
modelID: "gpt-5.2",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("nested slashes", () => {
|
|
||||||
it("google-vertex-anthropic/claude-opus-4-6 → { providerID: google-vertex-anthropic, modelID: claude-opus-4-6 } (first / only)", async () => {
|
|
||||||
// given
|
|
||||||
const agents = {
|
|
||||||
sisyphus: {
|
|
||||||
ultrawork: {
|
|
||||||
model: "google-vertex-anthropic/claude-opus-4-6",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const hook = createUltraworkModelOverrideHook({ agents })
|
|
||||||
const { input, output } = createMockParams({})
|
|
||||||
|
|
||||||
// when
|
|
||||||
await hook["chat.params"](input, output)
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(input.message.model).toEqual({
|
|
||||||
providerID: "google-vertex-anthropic",
|
|
||||||
modelID: "claude-opus-4-6",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("variant override", () => {
|
|
||||||
it("ultrawork.variant exists → message.variant updated", async () => {
|
|
||||||
// given
|
|
||||||
const agents = {
|
|
||||||
sisyphus: {
|
|
||||||
ultrawork: {
|
|
||||||
model: "openai/gpt-5.2",
|
|
||||||
variant: "high",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const hook = createUltraworkModelOverrideHook({ agents })
|
|
||||||
const { input, output } = createMockParams({})
|
|
||||||
|
|
||||||
// when
|
|
||||||
await hook["chat.params"](input, output)
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(input.message.variant).toBe("high")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("agent name normalization", () => {
|
|
||||||
it("Sisyphus (Ultraworker) → sisyphus config key lookup", async () => {
|
|
||||||
// given
|
|
||||||
const agents = {
|
|
||||||
sisyphus: {
|
|
||||||
ultrawork: {
|
|
||||||
model: "openai/gpt-5.2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const hook = createUltraworkModelOverrideHook({ agents })
|
|
||||||
const { input, output } = createMockParams({ agent: "Sisyphus (Ultraworker)" })
|
|
||||||
|
|
||||||
// when
|
|
||||||
await hook["chat.params"](input, output)
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(input.message.model).toEqual({
|
|
||||||
providerID: "openai",
|
|
||||||
modelID: "gpt-5.2",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
import type { AgentOverrides } from "../../config"
|
|
||||||
import { log } from "../../shared"
|
|
||||||
import { getAgentConfigKey } from "../../shared/agent-display-names"
|
|
||||||
|
|
||||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
||||||
return typeof value === "object" && value !== null
|
|
||||||
}
|
|
||||||
|
|
||||||
function getUltraworkConfig(agents: AgentOverrides | undefined, configKey: string) {
|
|
||||||
if (!agents) return undefined
|
|
||||||
|
|
||||||
for (const [agentKey, override] of Object.entries(agents)) {
|
|
||||||
if (getAgentConfigKey(agentKey) === configKey) {
|
|
||||||
return override?.ultrawork
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createUltraworkModelOverrideHook(args: { agents?: AgentOverrides }) {
|
|
||||||
let didLogSpikeInput = false
|
|
||||||
|
|
||||||
return {
|
|
||||||
"chat.params": async (input: unknown, output: unknown): Promise<void> => {
|
|
||||||
if (!didLogSpikeInput) {
|
|
||||||
didLogSpikeInput = true
|
|
||||||
|
|
||||||
const inputRecord = isRecord(input) ? input : null
|
|
||||||
const messageRecord = isRecord(inputRecord?.message) ? inputRecord.message : null
|
|
||||||
|
|
||||||
log("ultrawork-model-override spike: raw chat.params input", {
|
|
||||||
inputType: typeof input,
|
|
||||||
outputType: typeof output,
|
|
||||||
hasMessage: messageRecord !== null,
|
|
||||||
messageKeys: messageRecord ? Object.keys(messageRecord) : [],
|
|
||||||
hasMessageModel: messageRecord ? "model" in messageRecord : false,
|
|
||||||
messageModelType: messageRecord ? typeof messageRecord.model : "undefined",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isRecord(input)) return
|
|
||||||
|
|
||||||
const message = input.message
|
|
||||||
if (!isRecord(message)) return
|
|
||||||
if (message.variant !== "max") return
|
|
||||||
|
|
||||||
const agentName = input.agent
|
|
||||||
if (typeof agentName !== "string") return
|
|
||||||
|
|
||||||
const configKey = getAgentConfigKey(agentName)
|
|
||||||
const ultrawork = getUltraworkConfig(args.agents, configKey)
|
|
||||||
if (!ultrawork?.model) return
|
|
||||||
|
|
||||||
const separatorIndex = ultrawork.model.indexOf("/")
|
|
||||||
const providerID = separatorIndex === -1 ? ultrawork.model : ultrawork.model.slice(0, separatorIndex)
|
|
||||||
const modelID = separatorIndex === -1 ? "" : ultrawork.model.slice(separatorIndex + 1)
|
|
||||||
|
|
||||||
const previousModel = isRecord(message.model)
|
|
||||||
? {
|
|
||||||
providerID:
|
|
||||||
typeof message.model.providerID === "string" ? message.model.providerID : undefined,
|
|
||||||
modelID: typeof message.model.modelID === "string" ? message.model.modelID : undefined,
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
message.model = { providerID, modelID }
|
|
||||||
|
|
||||||
if (ultrawork.variant !== undefined) {
|
|
||||||
message.variant = ultrawork.variant
|
|
||||||
}
|
|
||||||
|
|
||||||
log("ultrawork-model-override: swapped model", {
|
|
||||||
sessionID: typeof input.sessionID === "string" ? input.sessionID : undefined,
|
|
||||||
agent: agentName,
|
|
||||||
configKey,
|
|
||||||
from: previousModel,
|
|
||||||
to: message.model,
|
|
||||||
variant: message.variant,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { createUltraworkModelOverrideHook } from "./hook"
|
|
||||||
@@ -31,7 +31,6 @@ export function createPluginInterface(args: {
|
|||||||
tool: tools,
|
tool: tools,
|
||||||
|
|
||||||
"chat.params": async (input, output) => {
|
"chat.params": async (input, output) => {
|
||||||
await hooks.ultraworkModelOverride?.["chat.params"]?.(input, output)
|
|
||||||
const handler = createChatParamsHandler({ anthropicEffort: hooks.anthropicEffort })
|
const handler = createChatParamsHandler({ anthropicEffort: hooks.anthropicEffort })
|
||||||
await handler(input, output)
|
await handler(input, output)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import {
|
|||||||
createPreemptiveCompactionHook,
|
createPreemptiveCompactionHook,
|
||||||
} from "../../hooks"
|
} from "../../hooks"
|
||||||
import { createAnthropicEffortHook } from "../../hooks/anthropic-effort"
|
import { createAnthropicEffortHook } from "../../hooks/anthropic-effort"
|
||||||
import { createUltraworkModelOverrideHook } from "../../hooks/ultrawork-model-override"
|
|
||||||
import {
|
import {
|
||||||
detectExternalNotificationPlugin,
|
detectExternalNotificationPlugin,
|
||||||
getNotificationConflictWarning,
|
getNotificationConflictWarning,
|
||||||
@@ -56,7 +55,6 @@ export type SessionHooks = {
|
|||||||
questionLabelTruncator: ReturnType<typeof createQuestionLabelTruncatorHook>
|
questionLabelTruncator: ReturnType<typeof createQuestionLabelTruncatorHook>
|
||||||
taskResumeInfo: ReturnType<typeof createTaskResumeInfoHook>
|
taskResumeInfo: ReturnType<typeof createTaskResumeInfoHook>
|
||||||
anthropicEffort: ReturnType<typeof createAnthropicEffortHook> | null
|
anthropicEffort: ReturnType<typeof createAnthropicEffortHook> | null
|
||||||
ultraworkModelOverride: ReturnType<typeof createUltraworkModelOverrideHook> | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSessionHooks(args: {
|
export function createSessionHooks(args: {
|
||||||
@@ -171,10 +169,6 @@ export function createSessionHooks(args: {
|
|||||||
? safeHook("anthropic-effort", () => createAnthropicEffortHook())
|
? safeHook("anthropic-effort", () => createAnthropicEffortHook())
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const ultraworkModelOverride = isHookEnabled("ultrawork-model-override")
|
|
||||||
? safeHook("ultrawork-model-override", () => createUltraworkModelOverrideHook({ agents: pluginConfig.agents }))
|
|
||||||
: null
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
contextWindowMonitor,
|
contextWindowMonitor,
|
||||||
preemptiveCompaction,
|
preemptiveCompaction,
|
||||||
@@ -197,6 +191,5 @@ export function createSessionHooks(args: {
|
|||||||
questionLabelTruncator,
|
questionLabelTruncator,
|
||||||
taskResumeInfo,
|
taskResumeInfo,
|
||||||
anthropicEffort,
|
anthropicEffort,
|
||||||
ultraworkModelOverride,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user