efc53de531
- Re-add ATHENA_PROMPT_METADATA with tight triggers and avoidWhen guardrails - Update switch_agent tool description with permanent handoff guardrail - Make buildDelegationTable mode-aware: switch_agent for primary, task() for subagent - Pass agent mode through general-agents to AvailableAgent interface - Improve primary agent error messages in subagent-resolver with alternatives - Add council-first bias to Athena interactive prompt - Update athena-junior trigger text for clearer task() invocation guidance - Add tests for ATHENA_PROMPT_METADATA shape, triggers, useWhen, avoidWhen
126 lines
4.5 KiB
TypeScript
126 lines
4.5 KiB
TypeScript
import type { AgentConfig } from "@opencode-ai/sdk"
|
|
import type { AgentMode, BuiltinAgentName, AgentOverrides, AgentPromptMetadata } from "../types"
|
|
import type { CategoryConfig, GitMasterConfig } from "../../config/schema"
|
|
import type { BrowserAutomationProvider } from "../../config/schema"
|
|
import type { AvailableAgent } from "../dynamic-agent-prompt-builder"
|
|
import { AGENT_MODEL_REQUIREMENTS, isModelAvailable } from "../../shared"
|
|
import { buildAgent, isFactory } from "../agent-builder"
|
|
import { applyOverrides } from "./agent-overrides"
|
|
import { applyEnvironmentContext } from "./environment-context"
|
|
import { applyModelResolution, getFirstFallbackModel } from "./model-resolution"
|
|
import { log } from "../../shared/logger"
|
|
|
|
export function collectPendingBuiltinAgents(input: {
|
|
agentSources: Partial<Record<BuiltinAgentName, import("../agent-builder").AgentSource>>
|
|
agentMetadata: Partial<Record<BuiltinAgentName, AgentPromptMetadata>>
|
|
disabledAgents: string[]
|
|
agentOverrides: AgentOverrides
|
|
directory?: string
|
|
systemDefaultModel?: string
|
|
mergedCategories: Record<string, CategoryConfig>
|
|
gitMasterConfig?: GitMasterConfig
|
|
browserProvider?: BrowserAutomationProvider
|
|
uiSelectedModel?: string
|
|
availableModels: Set<string>
|
|
isFirstRunNoCache: boolean
|
|
disabledSkills?: Set<string>
|
|
useTaskSystem?: boolean
|
|
disableOmoEnv?: boolean
|
|
}): { pendingAgentConfigs: Map<string, AgentConfig>; availableAgents: AvailableAgent[] } {
|
|
const {
|
|
agentSources,
|
|
agentMetadata,
|
|
disabledAgents,
|
|
agentOverrides,
|
|
directory,
|
|
systemDefaultModel,
|
|
mergedCategories,
|
|
gitMasterConfig,
|
|
browserProvider,
|
|
uiSelectedModel,
|
|
availableModels,
|
|
isFirstRunNoCache,
|
|
disabledSkills,
|
|
disableOmoEnv = false,
|
|
} = input
|
|
|
|
const availableAgents: AvailableAgent[] = []
|
|
const pendingAgentConfigs: Map<string, AgentConfig> = new Map()
|
|
|
|
for (const [name, source] of Object.entries(agentSources)) {
|
|
const agentName = name as BuiltinAgentName
|
|
|
|
if (agentName === "sisyphus") continue
|
|
if (agentName === "hephaestus") continue
|
|
if (agentName === "atlas") continue
|
|
if (agentName === "sisyphus-junior") continue
|
|
if (disabledAgents.some((name) => name.toLowerCase() === agentName.toLowerCase())) continue
|
|
|
|
const override = agentOverrides[agentName]
|
|
?? Object.entries(agentOverrides).find(([key]) => key.toLowerCase() === agentName.toLowerCase())?.[1]
|
|
const requirement = AGENT_MODEL_REQUIREMENTS[agentName]
|
|
|
|
// Check if agent requires a specific model
|
|
if (requirement?.requiresModel && availableModels) {
|
|
if (!isModelAvailable(requirement.requiresModel, availableModels)) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
const isPrimaryAgent = isFactory(source) && (source.mode === "primary" || source.mode === "all")
|
|
|
|
let resolution = applyModelResolution({
|
|
uiSelectedModel: (isPrimaryAgent && override?.model === undefined) ? uiSelectedModel : undefined,
|
|
userModel: override?.model,
|
|
requirement,
|
|
availableModels,
|
|
systemDefaultModel,
|
|
})
|
|
if (!resolution) {
|
|
if (override?.model) {
|
|
// User explicitly configured a model but resolution failed (e.g., cold cache).
|
|
// Honor the user's choice directly instead of falling back to hardcoded chain.
|
|
log("[agent-registration] User-configured model not resolved, using as-is", {
|
|
agent: agentName,
|
|
configuredModel: override.model,
|
|
})
|
|
resolution = { model: override.model, provenance: "override" as const }
|
|
} else {
|
|
resolution = getFirstFallbackModel(requirement)
|
|
}
|
|
}
|
|
if (!resolution) continue
|
|
const { model, variant: resolvedVariant } = resolution
|
|
|
|
let config = buildAgent(source, model, mergedCategories, gitMasterConfig, browserProvider, disabledSkills)
|
|
|
|
// Apply resolved variant from model fallback chain
|
|
if (resolvedVariant) {
|
|
config = { ...config, variant: resolvedVariant }
|
|
}
|
|
|
|
if (agentName === "librarian") {
|
|
config = applyEnvironmentContext(config, directory, { disableOmoEnv })
|
|
}
|
|
|
|
config = applyOverrides(config, override, mergedCategories, directory)
|
|
|
|
// Store for later - will be added after sisyphus and hephaestus
|
|
pendingAgentConfigs.set(name, config)
|
|
|
|
const metadata = agentMetadata[agentName]
|
|
if (metadata) {
|
|
const mode = isFactory(source) ? source.mode : (config.mode as AgentMode | undefined)
|
|
|
|
availableAgents.push({
|
|
name: agentName,
|
|
description: config.description ?? "",
|
|
metadata,
|
|
mode,
|
|
})
|
|
}
|
|
}
|
|
|
|
return { pendingAgentConfigs, availableAgents }
|
|
}
|