fix(athena): address 6 council review findings — launcher, schema, filtering, presentation
- Forward temperature and permission through council-launcher to background manager - Add LaunchInput.temperature and LaunchInput.permission to background-agent types - Extract session guard with 5-minute timeout to prevent stale council locks - Make council optional in AthenaOverrideConfigSchema for partial user overrides - Support member lookup by both name and model ID in filterCouncilMembers - Add provider/model-id format validation to CouncilMemberSchema - Fix findings-presenter group header to show finding count instead of first finding's reporter count
This commit is contained in:
@@ -625,7 +625,7 @@ describe("Athena agent override", () => {
|
||||
expect(result.data.agents?.athena?.model).toBe("openai/gpt-5.3-codex")
|
||||
expect(result.data.agents?.athena?.temperature).toBe(0.2)
|
||||
expect(result.data.agents?.athena?.prompt_append).toBe("Use consensus-first synthesis.")
|
||||
expect(result.data.agents?.athena?.council.members).toHaveLength(3)
|
||||
expect(result.data.agents?.athena?.council?.members).toHaveLength(3)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -648,7 +648,7 @@ describe("Athena agent override", () => {
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test("rejects athena override when council is missing", () => {
|
||||
test("accepts athena override without council (temperature-only override)", () => {
|
||||
// given
|
||||
const config = {
|
||||
agents: {
|
||||
@@ -662,7 +662,7 @@ describe("Athena agent override", () => {
|
||||
const result = OhMyOpenCodeConfigSchema.safeParse(config)
|
||||
|
||||
// then
|
||||
expect(result.success).toBe(false)
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ export const AgentOverrideConfigSchema = z.object({
|
||||
|
||||
export const AthenaOverrideConfigSchema = AgentOverrideConfigSchema.merge(
|
||||
z.object({
|
||||
council: AthenaConfigSchema.shape.council,
|
||||
council: AthenaConfigSchema.shape.council.optional(),
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
@@ -41,6 +41,50 @@ describe("CouncilMemberSchema", () => {
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test("rejects model string without provider/model separator", () => {
|
||||
//#given
|
||||
const config = { model: "invalid-model" }
|
||||
|
||||
//#when
|
||||
const result = CouncilMemberSchema.safeParse(config)
|
||||
|
||||
//#then
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test("rejects model string with empty provider", () => {
|
||||
//#given
|
||||
const config = { model: "/gpt-5.3-codex" }
|
||||
|
||||
//#when
|
||||
const result = CouncilMemberSchema.safeParse(config)
|
||||
|
||||
//#then
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test("rejects model string with empty model ID", () => {
|
||||
//#given
|
||||
const config = { model: "openai/" }
|
||||
|
||||
//#when
|
||||
const result = CouncilMemberSchema.safeParse(config)
|
||||
|
||||
//#then
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test("rejects empty model string", () => {
|
||||
//#given
|
||||
const config = { model: "" }
|
||||
|
||||
//#when
|
||||
const result = CouncilMemberSchema.safeParse(config)
|
||||
|
||||
//#then
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
test("rejects temperature below 0", () => {
|
||||
//#given
|
||||
const config = { model: "openai/gpt-5.3-codex", temperature: -0.1 }
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
import { z } from "zod"
|
||||
|
||||
/** Validates model string format: "provider/model-id" (e.g., "openai/gpt-5.3-codex"). */
|
||||
const ModelStringSchema = z
|
||||
.string()
|
||||
.min(1)
|
||||
.refine(
|
||||
(model) => {
|
||||
const slashIndex = model.indexOf("/")
|
||||
return slashIndex > 0 && slashIndex < model.length - 1
|
||||
},
|
||||
{ message: 'Model must be in "provider/model-id" format (e.g., "openai/gpt-5.3-codex")' }
|
||||
)
|
||||
|
||||
export const CouncilMemberSchema = z.object({
|
||||
model: z.string(),
|
||||
model: ModelStringSchema,
|
||||
temperature: z.number().min(0).max(2).optional(),
|
||||
variant: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
|
||||
Reference in New Issue
Block a user