openclaw / src /agents /subagents /spawn /subagent-spawn-plan.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
4c76b0d verified
Raw History Blame Contribute Delete
5.35 kB
/**
* Subagent spawn planning helpers.
*
* Resolves model, thinking, and timeout choices before the sessions_spawn executor launches work.
*/
import { formatThinkingLevels } from "../../../auto-reply/thinking.js";
import type { OpenClawConfig } from "../../../config/types.openclaw.js";
import type { FastMode } from "../../../shared/fast-mode.js";
import { splitTrailingAuthProfile } from "../../model-ref-profile.js";
import {
type ModelRef,
resolveDefaultModelForAgent,
resolveSubagentConfiguredModelSelection,
resolveSubagentSpawnModelSelection,
} from "../../model-selection.js";
import { resolveSubagentThinkingOverride } from "./subagent-spawn-thinking.js";
/** Splits a provider/model ref while preserving model-only refs. */
export function splitModelRef(ref?: string) {
if (!ref) {
return { provider: undefined, model: undefined };
}
const trimmed = ref.trim();
if (!trimmed) {
return { provider: undefined, model: undefined };
}
const slash = trimmed.indexOf("/");
if (slash > 0 && slash < trimmed.length - 1) {
const provider = trimmed.slice(0, slash);
const model = trimmed.slice(slash + 1);
return { provider, model };
}
const provider = undefined;
const model = trimmed;
if (model) {
return { provider, model };
}
return { provider: undefined, model: trimmed };
}
/** Resolves the effective subagent run timeout from per-call override or config default. */
export function resolveConfiguredSubagentRunTimeoutSeconds(params: {
cfg: OpenClawConfig;
runTimeoutSeconds?: number;
}) {
const cfgSubagentTimeout =
typeof params.cfg?.agents?.defaults?.subagents?.runTimeoutSeconds === "number" &&
Number.isFinite(params.cfg.agents.defaults.subagents.runTimeoutSeconds)
? Math.max(0, Math.floor(params.cfg.agents.defaults.subagents.runTimeoutSeconds))
: 0;
return typeof params.runTimeoutSeconds === "number" && Number.isFinite(params.runTimeoutSeconds)
? Math.max(0, Math.floor(params.runTimeoutSeconds))
: cfgSubagentTimeout;
}
/** Resolves the subagent model plus thinking patch to apply to the spawned session. */
export function resolveSubagentModelAndThinkingPlan(params: {
cfg: OpenClawConfig;
targetAgentId: string;
requesterAgentConfig?: unknown;
targetAgentConfig?: unknown;
modelOverride?: string;
thinkingOverrideRaw?: string;
callerThinkingRaw?: string;
inheritedModel?: ModelRef;
fastMode?: FastMode;
}) {
const { model: rawResolvedModel, resolvedModel: inheritedModel } =
resolveSubagentSpawnModelSelection({
cfg: params.cfg,
agentId: params.targetAgentId,
modelOverride: params.modelOverride,
inheritedModel: params.inheritedModel,
});
const { model: resolvedModel, profile: authProfileId } =
splitTrailingAuthProfile(rawResolvedModel);
const thinkingPlan = resolveSubagentThinkingOverride({
cfg: params.cfg,
requesterAgentConfig: params.requesterAgentConfig,
targetAgentConfig: params.targetAgentConfig,
thinkingOverrideRaw: params.thinkingOverrideRaw,
callerThinkingRaw: params.callerThinkingRaw,
});
if (thinkingPlan.status === "error") {
const { provider, model } = splitModelRef(resolvedModel);
// The hint is provider/model-specific because valid thinking levels vary by backend.
const hint = formatThinkingLevels(provider, model);
return {
status: "error" as const,
resolvedModel,
error: `Invalid thinking level "${thinkingPlan.thinkingCandidateRaw}". Use one of: ${hint}.`,
};
}
const modelOverrideSource = params.modelOverride?.trim() ? "user" : "auto";
const hasSelectedAutoModel =
modelOverrideSource === "auto" &&
Boolean(
params.inheritedModel ??
resolveSubagentConfiguredModelSelection({
cfg: params.cfg,
agentId: params.targetAgentId,
}),
);
const configuredModelRef = hasSelectedAutoModel ? splitModelRef(resolvedModel) : undefined;
const modelOrigin = configuredModelRef?.model
? {
provider:
configuredModelRef.provider ??
resolveDefaultModelForAgent({
cfg: params.cfg,
agentId: params.targetAgentId,
}).provider,
model: configuredModelRef.model,
}
: undefined;
return {
status: "ok" as const,
resolvedModel,
...(inheritedModel ? { inheritedModel } : {}),
modelApplied: Boolean(resolvedModel),
thinkingOverride: thinkingPlan.thinkingOverride,
initialSessionPatch: {
...(resolvedModel
? {
model: resolvedModel,
modelOverrideSource,
...(modelOrigin
? {
// Selected child models are session overrides, not legacy fallback residue.
// Self-origin metadata keeps cleanup from discarding them before first use.
modelOverrideFallbackOriginProvider: modelOrigin.provider,
modelOverrideFallbackOriginModel: modelOrigin.model,
}
: {}),
}
: {}),
...(authProfileId
? {
authProfileOverride: authProfileId,
authProfileOverrideSource: "user" as const,
}
: {}),
...thinkingPlan.initialSessionPatch,
...(params.fastMode !== undefined ? { fastMode: params.fastMode } : {}),
},
};
}