9885ee34fc
Hard-coded 'scout' references reached the model even when the scout agent was disabled via task.disabledAgents or absent from the session spawn list. Gate every such reference on scout actually being spawnable: the task tool description, the delegation gates, the plan-mode and workflowz notices, the glob/grep/ast-grep guidance, and the task specialization advisory. Prompt shape is otherwise unchanged; only erroneous references to the unavailable subagent are dropped. Closes #7313
73 lines
2.2 KiB
TypeScript
73 lines
2.2 KiB
TypeScript
/** Default agent used when a session has unrestricted spawning. */
|
|
export const DEFAULT_SPAWN_AGENT = "task";
|
|
|
|
/** Spawn policy derived from a parent agent's `spawns` frontmatter. */
|
|
export interface ResolvedSpawnPolicy {
|
|
/** True when at least one subagent may be spawned. */
|
|
enabled: boolean;
|
|
/** Agent used when the caller omits the agent field. */
|
|
defaultAgent: string;
|
|
/** Explicitly allowed agents, or `null` when the policy is unrestricted. */
|
|
allowedAgents: readonly string[] | null;
|
|
/** Text used in spawn rejection messages. */
|
|
allowedErrorText: string;
|
|
/** Backtick-quoted explicit agents for prompt descriptions. */
|
|
allowedPromptText?: string;
|
|
}
|
|
|
|
/** Resolves spawn frontmatter into the default and prompt/error surfaces. */
|
|
export function resolveSpawnPolicy(parentSpawns: string | boolean | null | undefined): ResolvedSpawnPolicy {
|
|
let normalized: string;
|
|
if (parentSpawns === false) {
|
|
normalized = "";
|
|
} else if (parentSpawns === true || parentSpawns === null || parentSpawns === undefined) {
|
|
normalized = "*";
|
|
} else {
|
|
normalized = parentSpawns.trim();
|
|
}
|
|
|
|
if (normalized === "*") {
|
|
return {
|
|
enabled: true,
|
|
defaultAgent: DEFAULT_SPAWN_AGENT,
|
|
allowedAgents: null,
|
|
allowedErrorText: "*",
|
|
};
|
|
}
|
|
|
|
const allowedAgents = normalized
|
|
.split(",")
|
|
.map(spawn => spawn.trim())
|
|
.filter(Boolean);
|
|
if (allowedAgents.length === 0) {
|
|
return {
|
|
enabled: false,
|
|
defaultAgent: DEFAULT_SPAWN_AGENT,
|
|
allowedAgents,
|
|
allowedErrorText: "none (spawns disabled for this agent)",
|
|
};
|
|
}
|
|
|
|
return {
|
|
enabled: true,
|
|
defaultAgent: allowedAgents[0] ?? DEFAULT_SPAWN_AGENT,
|
|
allowedAgents,
|
|
allowedErrorText: allowedAgents.join(","),
|
|
allowedPromptText: allowedAgents.map(agent => `\`${agent}\``).join(", "),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Whether the `scout` agent is spawnable in a session: not disabled via
|
|
* `task.disabledAgents`, and permitted by the session spawn policy.
|
|
*/
|
|
export function isScoutSpawnable(
|
|
disabledAgents: readonly string[] | undefined,
|
|
spawns: string | boolean | null | undefined,
|
|
): boolean {
|
|
if (disabledAgents?.includes("scout")) return false;
|
|
const policy = resolveSpawnPolicy(spawns);
|
|
if (!policy.enabled) return false;
|
|
return policy.allowedAgents === null || policy.allowedAgents.includes("scout");
|
|
}
|