Merge pull request #3436 from code-yeongyu/fix/bug-batch-1

fix: numeric skill names, ZWSP agent lookups, ultrawork run_in_background
This commit is contained in:
YeonGyu-Kim
2026-04-15 10:53:44 +09:00
committed by GitHub
14 changed files with 61 additions and 48 deletions
@@ -87,7 +87,7 @@ export async function loadSkillFromPathAsync(
const mcpJsonMcp = await loadMcpJsonFromDirAsync(resolvedPath) const mcpJsonMcp = await loadMcpJsonFromDirAsync(resolvedPath)
const mcpConfig = mcpJsonMcp || frontmatterMcp const mcpConfig = mcpJsonMcp || frontmatterMcp
const baseName = data.name || defaultName const baseName = String(data.name || defaultName)
const skillName = namePrefix ? `${namePrefix}/${baseName}` : baseName const skillName = namePrefix ? `${namePrefix}/${baseName}` : baseName
const originalDescription = data.description || "" const originalDescription = data.description || ""
const isOpencodeSource = scope === "opencode" || scope === "opencode-project" const isOpencodeSource = scope === "opencode" || scope === "opencode-project"
@@ -25,7 +25,7 @@ export async function loadSkillFromPath(options: {
const mcpJsonMcp = await loadMcpJsonFromDir(options.resolvedPath) const mcpJsonMcp = await loadMcpJsonFromDir(options.resolvedPath)
const mcpConfig = mcpJsonMcp || frontmatterMcp const mcpConfig = mcpJsonMcp || frontmatterMcp
const baseName = data.name || options.defaultName const baseName = String(data.name || options.defaultName)
const skillName = namePrefix ? `${namePrefix}/${baseName}` : baseName const skillName = namePrefix ? `${namePrefix}/${baseName}` : baseName
const originalDescription = data.description || "" const originalDescription = data.description || ""
const isOpencodeSource = options.scope === "opencode" || options.scope === "opencode-project" const isOpencodeSource = options.scope === "opencode" || options.scope === "opencode-project"
@@ -1,3 +1,4 @@
import { stripInvisibleAgentCharacters } from "../../shared/agent-display-names"
import type { CompactionAgentConfigCheckpoint } from "../../shared/compaction-agent-config-checkpoint" import type { CompactionAgentConfigCheckpoint } from "../../shared/compaction-agent-config-checkpoint"
export type RecoveryPromptConfig = CompactionAgentConfigCheckpoint & { export type RecoveryPromptConfig = CompactionAgentConfigCheckpoint & {
@@ -66,7 +67,7 @@ export function isPromptConfigRecovered(
const agentMatches = const agentMatches =
typeof actualAgent === "string" && typeof actualAgent === "string" &&
!isCompactionAgent(actualAgent) && !isCompactionAgent(actualAgent) &&
actualAgent.toLowerCase() === expectedPromptConfig.agent.toLowerCase() stripInvisibleAgentCharacters(actualAgent).toLowerCase() === stripInvisibleAgentCharacters(expectedPromptConfig.agent).toLowerCase()
return ( return (
agentMatches && agentMatches &&
+13 -13
View File
@@ -104,7 +104,7 @@ TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
| Architecture decision needed | MUST call plan agent | | Architecture decision needed | MUST call plan agent |
\`\`\` \`\`\`
task(subagent_type="plan", load_skills=[], prompt="<gathered context + user request>") task(subagent_type="plan", load_skills=[], run_in_background=false, prompt="<gathered context + user request>")
\`\`\` \`\`\`
**WHY PLAN AGENT IS MANDATORY:** **WHY PLAN AGENT IS MANDATORY:**
@@ -119,9 +119,9 @@ task(subagent_type="plan", load_skills=[], prompt="<gathered context + user requ
| Scenario | Action | | Scenario | Action |
|----------|--------| |----------|--------|
| Plan agent asks clarifying questions | \`task(session_id="{returned_session_id}", load_skills=[], prompt="<your answer>")\` | | Plan agent asks clarifying questions | \`task(session_id="{returned_session_id}", load_skills=[], run_in_background=false, prompt="<your answer>")\` |
| Need to refine the plan | \`task(session_id="{returned_session_id}", load_skills=[], prompt="Please adjust: <feedback>")\` | | Need to refine the plan | \`task(session_id="{returned_session_id}", load_skills=[], run_in_background=false, prompt="Please adjust: <feedback>")\` |
| Plan needs more detail | \`task(session_id="{returned_session_id}", load_skills=[], prompt="Add more detail to Task N")\` | | Plan needs more detail | \`task(session_id="{returned_session_id}", load_skills=[], run_in_background=false, prompt="Add more detail to Task N")\` |
**WHY SESSION_ID IS CRITICAL:** **WHY SESSION_ID IS CRITICAL:**
- Plan agent retains FULL conversation context - Plan agent retains FULL conversation context
@@ -131,10 +131,10 @@ task(subagent_type="plan", load_skills=[], prompt="<gathered context + user requ
\`\`\` \`\`\`
// WRONG: Starting fresh loses all context // WRONG: Starting fresh loses all context
task(subagent_type="plan", load_skills=[], prompt="Here's more info...") task(subagent_type="plan", load_skills=[], run_in_background=false, prompt="Here's more info...")
// CORRECT: Resume preserves everything // CORRECT: Resume preserves everything
task(session_id="ses_abc123", load_skills=[], prompt="Here's my answer to your question: ...") task(session_id="ses_abc123", load_skills=[], run_in_background=false, prompt="Here's my answer to your question: ...")
\`\`\` \`\`\`
**FAILURE TO CALL PLAN AGENT = INCOMPLETE WORK.** **FAILURE TO CALL PLAN AGENT = INCOMPLETE WORK.**
@@ -149,21 +149,21 @@ task(session_id="ses_abc123", load_skills=[], prompt="Here's my answer to your q
|-----------|--------|-----| |-----------|--------|-----|
| Codebase exploration | task(subagent_type="explore", load_skills=[], run_in_background=true) | Parallel, context-efficient | | Codebase exploration | task(subagent_type="explore", load_skills=[], run_in_background=true) | Parallel, context-efficient |
| Documentation lookup | task(subagent_type="librarian", load_skills=[], run_in_background=true) | Specialized knowledge | | Documentation lookup | task(subagent_type="librarian", load_skills=[], run_in_background=true) | Specialized knowledge |
| Planning | task(subagent_type="plan", load_skills=[]) | Parallel task graph + structured TODO list | | Planning | task(subagent_type="plan", load_skills=[], run_in_background=false) | Parallel task graph + structured TODO list |
| Hard problem (conventional) | task(subagent_type="oracle", load_skills=[]) | Architecture, debugging, complex logic | | Hard problem (conventional) | task(subagent_type="oracle", load_skills=[], run_in_background=false) | Architecture, debugging, complex logic |
| Hard problem (non-conventional) | task(category="artistry", load_skills=[...]) | Different approach needed | | Hard problem (non-conventional) | task(category="artistry", load_skills=[...], run_in_background=true) | Different approach needed |
| Implementation | task(category="...", load_skills=[...]) | Domain-optimized models | | Implementation | task(category="...", load_skills=[...], run_in_background=true) | Domain-optimized models |
**CATEGORY + SKILL DELEGATION:** **CATEGORY + SKILL DELEGATION:**
\`\`\` \`\`\`
// Frontend work // Frontend work
task(category="visual-engineering", load_skills=["frontend-ui-ux"]) task(category="visual-engineering", load_skills=["frontend-ui-ux"], run_in_background=true)
// Complex logic // Complex logic
task(category="ultrabrain", load_skills=["typescript-programmer"]) task(category="ultrabrain", load_skills=["typescript-programmer"], run_in_background=true)
// Quick fixes // Quick fixes
task(category="quick", load_skills=["git-master"]) task(category="quick", load_skills=["git-master"], run_in_background=true)
\`\`\` \`\`\`
**YOU SHOULD ONLY DO IT YOURSELF WHEN:** **YOU SHOULD ONLY DO IT YOURSELF WHEN:**
@@ -156,7 +156,7 @@ TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
| Architecture decision needed | MUST call plan agent | | Architecture decision needed | MUST call plan agent |
\`\`\` \`\`\`
task(subagent_type="plan", load_skills=[], prompt="<gathered context + user request>") task(subagent_type="plan", load_skills=[], run_in_background=false, prompt="<gathered context + user request>")
\`\`\` \`\`\`
### SESSION CONTINUITY WITH PLAN AGENT (CRITICAL) ### SESSION CONTINUITY WITH PLAN AGENT (CRITICAL)
@@ -165,9 +165,9 @@ task(subagent_type="plan", load_skills=[], prompt="<gathered context + user requ
| Scenario | Action | | Scenario | Action |
|----------|--------| |----------|--------|
| Plan agent asks clarifying questions | \`task(session_id="{returned_session_id}", load_skills=[], prompt="<your answer>")\` | | Plan agent asks clarifying questions | \`task(session_id="{returned_session_id}", load_skills=[], run_in_background=false, prompt="<your answer>")\` |
| Need to refine the plan | \`task(session_id="{returned_session_id}", load_skills=[], prompt="Please adjust: <feedback>")\` | | Need to refine the plan | \`task(session_id="{returned_session_id}", load_skills=[], run_in_background=false, prompt="Please adjust: <feedback>")\` |
| Plan needs more detail | \`task(session_id="{returned_session_id}", load_skills=[], prompt="Add more detail to Task N")\` | | Plan needs more detail | \`task(session_id="{returned_session_id}", load_skills=[], run_in_background=false, prompt="Add more detail to Task N")\` |
**FAILURE TO CALL PLAN AGENT = INCOMPLETE WORK.** **FAILURE TO CALL PLAN AGENT = INCOMPLETE WORK.**
@@ -183,10 +183,10 @@ task(subagent_type="plan", load_skills=[], prompt="<gathered context + user requ
|-----------|--------|-----| |-----------|--------|-----|
| Codebase exploration | task(subagent_type="explore", load_skills=[], run_in_background=true) | Parallel, context-efficient | | Codebase exploration | task(subagent_type="explore", load_skills=[], run_in_background=true) | Parallel, context-efficient |
| Documentation lookup | task(subagent_type="librarian", load_skills=[], run_in_background=true) | Specialized knowledge | | Documentation lookup | task(subagent_type="librarian", load_skills=[], run_in_background=true) | Specialized knowledge |
| Planning | task(subagent_type="plan", load_skills=[]) | Parallel task graph + structured TODO list | | Planning | task(subagent_type="plan", load_skills=[], run_in_background=false) | Parallel task graph + structured TODO list |
| Hard problem (conventional) | task(subagent_type="oracle", load_skills=[]) | Architecture, debugging, complex logic | | Hard problem (conventional) | task(subagent_type="oracle", load_skills=[], run_in_background=false) | Architecture, debugging, complex logic |
| Hard problem (non-conventional) | task(category="artistry", load_skills=[...]) | Different approach needed | | Hard problem (non-conventional) | task(category="artistry", load_skills=[...], run_in_background=true) | Different approach needed |
| Implementation | task(category="...", load_skills=[...]) | Domain-optimized models | | Implementation | task(category="...", load_skills=[...], run_in_background=true) | Domain-optimized models |
**YOU SHOULD ONLY DO IT YOURSELF WHEN:** **YOU SHOULD ONLY DO IT YOURSELF WHEN:**
- Task is trivially simple (1-2 lines, obvious change) - Task is trivially simple (1-2 lines, obvious change)
+3 -3
View File
@@ -71,9 +71,9 @@ Use these when they provide clear value based on the decision framework above:
|----------|-------------|------------| |----------|-------------|------------|
| explore agent | Need codebase patterns you don't have | \`task(subagent_type="explore", load_skills=[], run_in_background=true, ...)\` | | explore agent | Need codebase patterns you don't have | \`task(subagent_type="explore", load_skills=[], run_in_background=true, ...)\` |
| librarian agent | External library docs, OSS examples | \`task(subagent_type="librarian", load_skills=[], run_in_background=true, ...)\` | | librarian agent | External library docs, OSS examples | \`task(subagent_type="librarian", load_skills=[], run_in_background=true, ...)\` |
| oracle agent | Stuck on architecture/debugging after 2+ attempts | \`task(subagent_type="oracle", load_skills=[], ...)\` | | oracle agent | Stuck on architecture/debugging after 2+ attempts | \`task(subagent_type="oracle", load_skills=[], run_in_background=false, ...)\` |
| plan agent | Complex multi-step with dependencies (5+ steps) | \`task(subagent_type="plan", load_skills=[], ...)\` | | plan agent | Complex multi-step with dependencies (5+ steps) | \`task(subagent_type="plan", load_skills=[], run_in_background=false, ...)\` |
| task category | Specialized work matching a category | \`task(category="...", load_skills=[...])\` | | task category | Specialized work matching a category | \`task(category="...", load_skills=[...], run_in_background=true)\` |
<tool_usage_rules> <tool_usage_rules>
- Prefer tools over internal knowledge for fresh or user-specific data - Prefer tools over internal knowledge for fresh or user-specific data
@@ -117,7 +117,7 @@ Each TODO item MUST include:
| Wave | Tasks | Dispatch Command | | Wave | Tasks | Dispatch Command |
|------|-------|------------------| |------|-------|------------------|
| 1 | 1, 4 | \`task(category="...", load_skills=[...], run_in_background=false)\` × 2 | | 1 | 1, 4 | \`task(category="...", load_skills=[...], run_in_background=true)\` × 2 |
| 2 | 2, 3, 5 | \`task(...)\` × 3 after Wave 1 completes | | 2 | 2, 3, 5 | \`task(...)\` × 3 after Wave 1 completes |
| 3 | 6 | \`task(...)\` final integration | | 3 | 6 | \`task(...)\` final integration |
@@ -1,3 +1,4 @@
import { stripInvisibleAgentCharacters } from "../../shared/agent-display-names"
import { ULTRAWORK_VERIFICATION_PROMISE } from "./constants" import { ULTRAWORK_VERIFICATION_PROMISE } from "./constants"
export interface OracleVerificationEvidence { export interface OracleVerificationEvidence {
@@ -54,7 +55,7 @@ export function isOracleVerified(text: string): boolean {
return false return false
} }
const isOracleAgent = evidence.agent.toLowerCase() === "oracle" const isOracleAgent = stripInvisibleAgentCharacters(evidence.agent).toLowerCase() === "oracle"
const isVerifiedPromise = evidence.promise === ULTRAWORK_VERIFICATION_PROMISE const isVerifiedPromise = evidence.promise === ULTRAWORK_VERIFICATION_PROMISE
return isOracleAgent && isVerifiedPromise return isOracleAgent && isVerifiedPromise
@@ -62,7 +63,7 @@ export function isOracleVerified(text: string): boolean {
export function extractOracleSessionID(text: string): string | undefined { export function extractOracleSessionID(text: string): string | undefined {
const evidence = parseOracleVerificationEvidence(text) const evidence = parseOracleVerificationEvidence(text)
if (!evidence || evidence.agent.toLowerCase() !== "oracle") { if (!evidence || stripInvisibleAgentCharacters(evidence.agent).toLowerCase() !== "oracle") {
return undefined return undefined
} }
+2 -1
View File
@@ -1,6 +1,7 @@
import { consumeToolMetadata } from "../features/tool-metadata-store" import { consumeToolMetadata } from "../features/tool-metadata-store"
import type { CreatedHooks } from "../create-hooks" import type { CreatedHooks } from "../create-hooks"
import { log } from "../shared" import { log } from "../shared"
import { stripInvisibleAgentCharacters } from "../shared/agent-display-names"
import type { PluginContext } from "./types" import type { PluginContext } from "./types"
import { readState, writeState } from "../hooks/ralph-loop/storage" import { readState, writeState } from "../hooks/ralph-loop/storage"
@@ -60,7 +61,7 @@ export function createToolExecuteAfterHandler(args: {
const verificationAttemptId = prompt?.match(VERIFICATION_ATTEMPT_PATTERN)?.[1]?.trim() const verificationAttemptId = prompt?.match(VERIFICATION_ATTEMPT_PATTERN)?.[1]?.trim()
const loopState = directory ? readState(directory) : null const loopState = directory ? readState(directory) : null
const isVerificationContext = const isVerificationContext =
agent === "oracle" (agent ? stripInvisibleAgentCharacters(agent) : agent) === "oracle"
&& !!sessionId && !!sessionId
&& !!directory && !!directory
&& loopState?.active === true && loopState?.active === true
+2 -1
View File
@@ -4,6 +4,7 @@ import { randomUUID } from "node:crypto"
import { getMainSessionID } from "../features/claude-code-session-state" import { getMainSessionID } from "../features/claude-code-session-state"
import { clearBoulderState } from "../features/boulder-state" import { clearBoulderState } from "../features/boulder-state"
import { log } from "../shared" import { log } from "../shared"
import { stripInvisibleAgentCharacters } from "../shared/agent-display-names"
import { resolveSessionAgent } from "./session-agent-resolver" import { resolveSessionAgent } from "./session-agent-resolver"
import { parseRalphLoopArguments } from "../hooks/ralph-loop/command-arguments" import { parseRalphLoopArguments } from "../hooks/ralph-loop/command-arguments"
import { ULTRAWORK_VERIFICATION_PROMISE } from "../hooks/ralph-loop/constants" import { ULTRAWORK_VERIFICATION_PROMISE } from "../hooks/ralph-loop/constants"
@@ -109,7 +110,7 @@ export function createToolExecuteBeforeHandler(args: {
} }
const normalizedSubagentType = const normalizedSubagentType =
typeof argsObject.subagent_type === "string" ? argsObject.subagent_type : undefined typeof argsObject.subagent_type === "string" ? stripInvisibleAgentCharacters(argsObject.subagent_type) : undefined
const prompt = typeof argsObject.prompt === "string" ? argsObject.prompt : "" const prompt = typeof argsObject.prompt === "string" ? argsObject.prompt : ""
const loopState = typeof ctx.directory === "string" ? readState(ctx.directory) : null const loopState = typeof ctx.directory === "string" ? readState(ctx.directory) : null
const shouldInjectOracleVerification = const shouldInjectOracleVerification =
+8 -4
View File
@@ -1,3 +1,5 @@
import { stripInvisibleAgentCharacters } from "./agent-display-names"
/** /**
* Agent tool restrictions for session.prompt calls. * Agent tool restrictions for session.prompt calls.
* OpenCode SDK's session.prompt `tools` parameter expects boolean values. * OpenCode SDK's session.prompt `tools` parameter expects boolean values.
@@ -45,13 +47,15 @@ const AGENT_RESTRICTIONS: Record<string, Record<string, boolean>> = {
} }
export function getAgentToolRestrictions(agentName: string): Record<string, boolean> { export function getAgentToolRestrictions(agentName: string): Record<string, boolean> {
return AGENT_RESTRICTIONS[agentName] const stripped = stripInvisibleAgentCharacters(agentName)
?? Object.entries(AGENT_RESTRICTIONS).find(([key]) => key.toLowerCase() === agentName.toLowerCase())?.[1] return AGENT_RESTRICTIONS[stripped]
?? Object.entries(AGENT_RESTRICTIONS).find(([key]) => key.toLowerCase() === stripped.toLowerCase())?.[1]
?? {} ?? {}
} }
export function hasAgentToolRestrictions(agentName: string): boolean { export function hasAgentToolRestrictions(agentName: string): boolean {
const restrictions = AGENT_RESTRICTIONS[agentName] const stripped = stripInvisibleAgentCharacters(agentName)
?? Object.entries(AGENT_RESTRICTIONS).find(([key]) => key.toLowerCase() === agentName.toLowerCase())?.[1] const restrictions = AGENT_RESTRICTIONS[stripped]
?? Object.entries(AGENT_RESTRICTIONS).find(([key]) => key.toLowerCase() === stripped.toLowerCase())?.[1]
return restrictions !== undefined && Object.keys(restrictions).length > 0 return restrictions !== undefined && Object.keys(restrictions).length > 0
} }
+8 -5
View File
@@ -1,4 +1,5 @@
import type { OhMyOpenCodeConfig } from "../config" import type { OhMyOpenCodeConfig } from "../config"
import { stripInvisibleAgentCharacters } from "./agent-display-names"
import { AGENT_MODEL_REQUIREMENTS, CATEGORY_MODEL_REQUIREMENTS } from "./model-requirements" import { AGENT_MODEL_REQUIREMENTS, CATEGORY_MODEL_REQUIREMENTS } from "./model-requirements"
export function resolveAgentVariant( export function resolveAgentVariant(
@@ -9,12 +10,13 @@ export function resolveAgentVariant(
return undefined return undefined
} }
const stripped = stripInvisibleAgentCharacters(agentName)
const agentOverrides = config.agents as const agentOverrides = config.agents as
| Record<string, { variant?: string; category?: string }> | Record<string, { variant?: string; category?: string }>
| undefined | undefined
const agentOverride = agentOverrides const agentOverride = agentOverrides
? agentOverrides[agentName] ? agentOverrides[stripped]
?? Object.entries(agentOverrides).find(([key]) => key.toLowerCase() === agentName.toLowerCase())?.[1] ?? Object.entries(agentOverrides).find(([key]) => key.toLowerCase() === stripped.toLowerCase())?.[1]
: undefined : undefined
if (!agentOverride) { if (!agentOverride) {
return undefined return undefined
@@ -37,18 +39,19 @@ export function resolveVariantForModel(
agentName: string, agentName: string,
currentModel: { providerID: string; modelID: string }, currentModel: { providerID: string; modelID: string },
): string | undefined { ): string | undefined {
const stripped = stripInvisibleAgentCharacters(agentName)
const agentOverrides = config.agents as const agentOverrides = config.agents as
| Record<string, { variant?: string; category?: string }> | Record<string, { variant?: string; category?: string }>
| undefined | undefined
const agentOverride = agentOverrides const agentOverride = agentOverrides
? agentOverrides[agentName] ? agentOverrides[stripped]
?? Object.entries(agentOverrides).find(([key]) => key.toLowerCase() === agentName.toLowerCase())?.[1] ?? Object.entries(agentOverrides).find(([key]) => key.toLowerCase() === stripped.toLowerCase())?.[1]
: undefined : undefined
if (agentOverride?.variant) { if (agentOverride?.variant) {
return agentOverride.variant return agentOverride.variant
} }
const agentRequirement = AGENT_MODEL_REQUIREMENTS[agentName] const agentRequirement = AGENT_MODEL_REQUIREMENTS[stripped]
if (agentRequirement) { if (agentRequirement) {
return findVariantInChain(agentRequirement.fallbackChain, currentModel) return findVariantInChain(agentRequirement.fallbackChain, currentModel)
} }
+6 -5
View File
@@ -6,7 +6,7 @@ import type { CategoriesConfig, AgentOverrides } from "../../config/schema"
import type { DelegatedModelConfig } from "../../shared/model-resolution-types" import type { DelegatedModelConfig } from "../../shared/model-resolution-types"
import type { FallbackEntry } from "../../shared/model-requirements" import type { FallbackEntry } from "../../shared/model-requirements"
import { AGENT_MODEL_REQUIREMENTS } from "../../shared/model-requirements" import { AGENT_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
import { getAgentConfigKey } from "../../shared/agent-display-names" import { getAgentConfigKey, stripInvisibleAgentCharacters } from "../../shared/agent-display-names"
import { normalizeFallbackModels } from "../../shared/model-resolver" import { normalizeFallbackModels } from "../../shared/model-resolver"
import { buildFallbackChainFromModels } from "../../shared/fallback-chain-from-models" import { buildFallbackChainFromModels } from "../../shared/fallback-chain-from-models"
import { log } from "../../shared" import { log } from "../../shared"
@@ -104,20 +104,21 @@ export function createCallOmoAgent(
const toolCtx = toolContext as ToolContextWithMetadata const toolCtx = toolContext as ToolContextWithMetadata
log(`[call_omo_agent] Starting with agent: ${args.subagent_type}, background: ${args.run_in_background}`) log(`[call_omo_agent] Starting with agent: ${args.subagent_type}, background: ${args.run_in_background}`)
// Case-insensitive agent validation - allows "Explore", "EXPLORE", "explore" etc. // Strip ZWSP and case-insensitive agent validation - allows "Explore", "EXPLORE", "explore" etc.
const strippedAgentType = stripInvisibleAgentCharacters(args.subagent_type)
if ( if (
!ALLOWED_AGENTS.some( !ALLOWED_AGENTS.some(
(name) => name.toLowerCase() === args.subagent_type.toLowerCase(), (name) => name.toLowerCase() === strippedAgentType.toLowerCase(),
) )
) { ) {
return `Error: Invalid agent type "${args.subagent_type}". Only ${ALLOWED_AGENTS.join(", ")} are allowed.` return `Error: Invalid agent type "${args.subagent_type}". Only ${ALLOWED_AGENTS.join(", ")} are allowed.`
} }
const normalizedAgent = args.subagent_type.toLowerCase() as AllowedAgentType const normalizedAgent = strippedAgentType.toLowerCase() as AllowedAgentType
args = { ...args, subagent_type: normalizedAgent } args = { ...args, subagent_type: normalizedAgent }
// Check if agent is disabled // Check if agent is disabled
if (disabledAgents.some((disabled) => disabled.toLowerCase() === normalizedAgent)) { if (disabledAgents.some((disabled) => stripInvisibleAgentCharacters(disabled).toLowerCase() === normalizedAgent)) {
return `Error: Agent "${normalizedAgent}" is disabled via disabled_agents configuration. Remove it from disabled_agents in your ${CONFIG_BASENAME}.json to use it.` return `Error: Agent "${normalizedAgent}" is disabled via disabled_agents configuration. Remove it from disabled_agents in your ${CONFIG_BASENAME}.json to use it.`
} }
@@ -8,6 +8,7 @@ import {
} from "../../shared/model-suggestion-retry" } from "../../shared/model-suggestion-retry"
import { formatDetailedError } from "./error-formatting" import { formatDetailedError } from "./error-formatting"
import { getAgentToolRestrictions } from "../../shared/agent-tool-restrictions" import { getAgentToolRestrictions } from "../../shared/agent-tool-restrictions"
import { stripInvisibleAgentCharacters } from "../../shared/agent-display-names"
import { applySessionPromptParams } from "../../shared/session-prompt-params-helpers" import { applySessionPromptParams } from "../../shared/session-prompt-params-helpers"
import { setSessionTools } from "../../shared/session-tools-store" import { setSessionTools } from "../../shared/session-tools-store"
import { createInternalAgentTextPart } from "../../shared/internal-initiator-marker" import { createInternalAgentTextPart } from "../../shared/internal-initiator-marker"
@@ -41,7 +42,7 @@ function buildPromptGenerationParams(model: DelegatedModelConfig | undefined): R
} }
function isOracleAgent(agentToUse: string): boolean { function isOracleAgent(agentToUse: string): boolean {
return agentToUse.toLowerCase() === "oracle" return stripInvisibleAgentCharacters(agentToUse).toLowerCase() === "oracle"
} }
function isUnexpectedEofError(error: unknown): boolean { function isUnexpectedEofError(error: unknown): boolean {
@@ -80,7 +81,7 @@ export async function sendSyncPrompt(
const promptArgs = { const promptArgs = {
path: { id: input.sessionID }, path: { id: input.sessionID },
body: { body: {
agent: input.agentToUse.replace(/^\u200B+/, ""), agent: stripInvisibleAgentCharacters(input.agentToUse),
system: input.systemContent, system: input.systemContent,
tools, tools,
parts: [createInternalAgentTextPart(effectivePrompt)], parts: [createInternalAgentTextPart(effectivePrompt)],