From 5aa64932c5dafaba7b88d9d50373e355a5da2734 Mon Sep 17 00:00:00 2001 From: ismeth Date: Thu, 19 Feb 2026 13:53:04 +0100 Subject: [PATCH] feat(athena): add temperature support to council member schema Allow per-member temperature overrides in council config. Adds temperature field to CouncilMemberSchema (0-2 range), CouncilMemberConfig type, and auto-generated JSON schema. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- assets/oh-my-opencode.schema.json | 262 ++++++++++++++++++++++++++++++ src/agents/athena/types.ts | 1 + src/config/schema/athena.test.ts | 40 ++++- src/config/schema/athena.ts | 1 + 4 files changed, 303 insertions(+), 1 deletion(-) diff --git a/assets/oh-my-opencode.schema.json b/assets/oh-my-opencode.schema.json index 607988931..2702103f4 100644 --- a/assets/oh-my-opencode.schema.json +++ b/assets/oh-my-opencode.schema.json @@ -4972,6 +4972,188 @@ } }, "additionalProperties": false + } + }, + "additionalProperties": false + }, + "council-member": { + "type": "object", + "properties": { + "model": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "category": { + "type": "string" + }, + "skills": { + "type": "array", + "items": { + "type": "string" + } + }, + "temperature": { + "type": "number", + "minimum": 0, + "maximum": 2 + }, + "top_p": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "prompt": { + "type": "string" + }, + "prompt_append": { + "type": "string" + }, + "tools": { + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, + "disable": { + "type": "boolean" + }, + "description": { + "type": "string" + }, + "mode": { + "type": "string", + "enum": [ + "subagent", + "primary", + "all" + ] + }, + "color": { + "type": "string", + "pattern": "^#[0-9A-Fa-f]{6}$" + }, + "permission": { + "type": "object", + "properties": { + "edit": { + "type": "string", + "enum": [ + "ask", + "allow", + "deny" + ] + }, + "bash": { + "anyOf": [ + { + "type": "string", + "enum": [ + "ask", + "allow", + "deny" + ] + }, + { + "type": "object", + "additionalProperties": { + "type": "string", + "enum": [ + "ask", + "allow", + "deny" + ] + } + } + ] + }, + "webfetch": { + "type": "string", + "enum": [ + "ask", + "allow", + "deny" + ] + }, + "task": { + "type": "string", + "enum": [ + "ask", + "allow", + "deny" + ] + }, + "doom_loop": { + "type": "string", + "enum": [ + "ask", + "allow", + "deny" + ] + }, + "external_directory": { + "type": "string", + "enum": [ + "ask", + "allow", + "deny" + ] + } + }, + "additionalProperties": false + }, + "maxTokens": { + "type": "number" + }, + "thinking": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "enabled", + "disabled" + ] + }, + "budgetTokens": { + "type": "number" + } + }, + "required": [ + "type" + ], + "additionalProperties": false + }, + "reasoningEffort": { + "type": "string", + "enum": [ + "low", + "medium", + "high", + "xhigh" + ] + }, + "textVerbosity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ] + }, + "providerOptions": { + "type": "object", + "additionalProperties": {} + } + }, + "additionalProperties": false + }, + "athena": { + "type": "object", + "properties": { + "model": { + "type": "string" }, "compaction": { "type": "object", @@ -4984,6 +5166,86 @@ } }, "additionalProperties": false + }, + "maxTokens": { + "type": "number" + }, + "thinking": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "enabled", + "disabled" + ] + }, + "budgetTokens": { + "type": "number" + } + }, + "required": [ + "type" + ], + "additionalProperties": false + }, + "reasoningEffort": { + "type": "string", + "enum": [ + "low", + "medium", + "high", + "xhigh" + ] + }, + "textVerbosity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ] + }, + "providerOptions": { + "type": "object", + "additionalProperties": {} + }, + "council": { + "type": "object", + "properties": { + "members": { + "minItems": 2, + "type": "array", + "items": { + "type": "object", + "properties": { + "model": { + "type": "string", + "minLength": 1 + }, + "variant": { + "type": "string" + }, + "name": { + "type": "string" + }, + "temperature": { + "type": "number", + "minimum": 0, + "maximum": 2 + } + }, + "required": [ + "model" + ], + "additionalProperties": false + } + } + }, + "required": [ + "members" + ], + "additionalProperties": false } }, "additionalProperties": false diff --git a/src/agents/athena/types.ts b/src/agents/athena/types.ts index cb668c0ce..eb88861ef 100644 --- a/src/agents/athena/types.ts +++ b/src/agents/athena/types.ts @@ -2,6 +2,7 @@ export interface CouncilMemberConfig { model: string variant?: string name?: string + temperature?: number } export interface CouncilConfig { diff --git a/src/config/schema/athena.test.ts b/src/config/schema/athena.test.ts index 713c15047..f3bf6d8fd 100644 --- a/src/config/schema/athena.test.ts +++ b/src/config/schema/athena.test.ts @@ -20,6 +20,7 @@ describe("CouncilMemberSchema", () => { model: "openai/gpt-5.3-codex", variant: "high", name: "analyst-a", + temperature: 0.3, } //#when @@ -110,11 +111,48 @@ describe("CouncilMemberSchema", () => { //#then expect(parsed.variant).toBeUndefined() expect(parsed.name).toBeUndefined() + expect(parsed.temperature).toBeUndefined() + }) + + test("accepts member config with temperature", () => { + //#given + const config = { model: "openai/gpt-5.3-codex", temperature: 0.5 } + + //#when + const result = CouncilMemberSchema.safeParse(config) + + //#then + expect(result.success).toBe(true) + if (result.success) { + expect(result.data.temperature).toBe(0.5) + } + }) + + test("rejects temperature below 0", () => { + //#given + const config = { model: "openai/gpt-5.3-codex", temperature: -0.1 } + + //#when + const result = CouncilMemberSchema.safeParse(config) + + //#then + expect(result.success).toBe(false) + }) + + test("rejects temperature above 2", () => { + //#given + const config = { model: "openai/gpt-5.3-codex", temperature: 2.1 } + + //#when + const result = CouncilMemberSchema.safeParse(config) + + //#then + expect(result.success).toBe(false) }) test("rejects member config with unknown fields", () => { //#given - const config = { model: "openai/gpt-5.3-codex", temperature: 0.2 } + const config = { model: "openai/gpt-5.3-codex", unknownField: true } //#when const result = CouncilMemberSchema.safeParse(config) diff --git a/src/config/schema/athena.ts b/src/config/schema/athena.ts index 2ac56af93..3972aaf04 100644 --- a/src/config/schema/athena.ts +++ b/src/config/schema/athena.ts @@ -14,6 +14,7 @@ export const CouncilMemberSchema = z.object({ model: ModelStringSchema, variant: z.string().optional(), name: z.string().optional(), + temperature: z.number().min(0).max(2).optional(), }).strict() export const CouncilConfigSchema = z.object({