mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 21:07:01 -06:00
refactor(agents): fold single-consumer subagent-spawn helpers back into callers (#114301)
This commit is contained in:
committed by
GitHub
parent
e0eed257c8
commit
beef065fe4
@@ -3,6 +3,10 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { isIncognitoSessionKey } from "../routing/session-key.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import { resolveAgentDir } from "./agent-scope-config.js";
|
||||
import { findModelCatalogEntry } from "./model-catalog-lookup.js";
|
||||
import { resolveDefaultModelForAgent } from "./model-selection.js";
|
||||
import { supportsModelTools } from "./model-tool-support.js";
|
||||
import { summarizeSpawnError } from "./spawn-pipeline.js";
|
||||
import { resolveSpawnSandboxError, mintSpawnSessionKey } from "./spawn-plan.js";
|
||||
import { resolveRequesterOriginForChild } from "./spawn-requester-origin.js";
|
||||
import {
|
||||
@@ -15,21 +19,68 @@ import type {
|
||||
SpawnSubagentParams,
|
||||
SpawnSubagentResult,
|
||||
} from "./subagent-spawn-contract.js";
|
||||
import {
|
||||
buildResolvedSubagentModelMetadata,
|
||||
resolveCollectorOutputModelError,
|
||||
} from "./subagent-spawn-model.js";
|
||||
import { getSubagentSpawnDeps } from "./subagent-spawn-deps.js";
|
||||
import { resolveSubagentModelAndThinkingPlan, splitModelRef } from "./subagent-spawn-plan.js";
|
||||
import {
|
||||
readRequesterFastMode,
|
||||
readRequesterThinkingLevel,
|
||||
} from "./subagent-spawn-requester-prefs.js";
|
||||
import {
|
||||
loadPreparedModelCatalog,
|
||||
normalizeDeliveryContext,
|
||||
resolveAgentConfig,
|
||||
resolveSandboxRuntimeStatus,
|
||||
} from "./subagent-spawn.runtime.js";
|
||||
|
||||
function buildResolvedSubagentModelMetadata(resolvedModel?: string): {
|
||||
resolvedModel?: string;
|
||||
resolvedProvider?: string;
|
||||
} {
|
||||
const modelRef = resolvedModel?.trim();
|
||||
if (!modelRef) {
|
||||
return {};
|
||||
}
|
||||
const { provider } = splitModelRef(modelRef);
|
||||
return {
|
||||
resolvedModel: modelRef,
|
||||
...(provider ? { resolvedProvider: provider } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
async function resolveCollectorOutputModelError(params: {
|
||||
cfg: OpenClawConfig;
|
||||
targetAgentId: string;
|
||||
targetAgentDir: string;
|
||||
workspaceDir?: string;
|
||||
resolvedModel?: string;
|
||||
}): Promise<string | undefined> {
|
||||
const selected = splitModelRef(params.resolvedModel);
|
||||
const fallback = resolveDefaultModelForAgent({
|
||||
cfg: params.cfg,
|
||||
agentId: params.targetAgentId,
|
||||
});
|
||||
const provider = selected.provider ?? fallback.provider;
|
||||
const model = selected.model ?? fallback.model;
|
||||
if (!provider || !model) {
|
||||
return undefined;
|
||||
}
|
||||
let catalog: Awaited<ReturnType<typeof loadPreparedModelCatalog>>;
|
||||
try {
|
||||
catalog = await getSubagentSpawnDeps().loadPreparedModelCatalog({
|
||||
config: params.cfg,
|
||||
agentDir: params.targetAgentDir,
|
||||
workspaceDir: params.workspaceDir,
|
||||
});
|
||||
} catch (error) {
|
||||
return `sessions_spawn could not verify outputSchema model capabilities: ${summarizeSpawnError(error)}`;
|
||||
}
|
||||
const entry = findModelCatalogEntry(catalog, { provider, modelId: model });
|
||||
if (!entry || supportsModelTools(entry)) {
|
||||
return undefined;
|
||||
}
|
||||
return `sessions_spawn outputSchema requires a tool-capable target model; "${provider}/${model}" declares compat.supportsTools=false.`;
|
||||
}
|
||||
|
||||
type ResolvedSubagentChildPlan = {
|
||||
spawnedCwd?: string;
|
||||
toolSpawnMetadata: ReturnType<typeof mapToolContextToSpawnedRunMetadata>;
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { findModelCatalogEntry } from "./model-catalog-lookup.js";
|
||||
import { resolveDefaultModelForAgent } from "./model-selection.js";
|
||||
import { supportsModelTools } from "./model-tool-support.js";
|
||||
import { summarizeSpawnError } from "./spawn-pipeline.js";
|
||||
import { getSubagentSpawnDeps } from "./subagent-spawn-deps.js";
|
||||
import { splitModelRef } from "./subagent-spawn-plan.js";
|
||||
import { loadPreparedModelCatalog } from "./subagent-spawn.runtime.js";
|
||||
|
||||
export function buildResolvedSubagentModelMetadata(resolvedModel?: string): {
|
||||
resolvedModel?: string;
|
||||
resolvedProvider?: string;
|
||||
} {
|
||||
const modelRef = resolvedModel?.trim();
|
||||
if (!modelRef) {
|
||||
return {};
|
||||
}
|
||||
const { provider } = splitModelRef(modelRef);
|
||||
return {
|
||||
resolvedModel: modelRef,
|
||||
...(provider ? { resolvedProvider: provider } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export async function resolveCollectorOutputModelError(params: {
|
||||
cfg: OpenClawConfig;
|
||||
targetAgentId: string;
|
||||
targetAgentDir: string;
|
||||
workspaceDir?: string;
|
||||
resolvedModel?: string;
|
||||
}): Promise<string | undefined> {
|
||||
const selected = splitModelRef(params.resolvedModel);
|
||||
const fallback = resolveDefaultModelForAgent({
|
||||
cfg: params.cfg,
|
||||
agentId: params.targetAgentId,
|
||||
});
|
||||
const provider = selected.provider ?? fallback.provider;
|
||||
const model = selected.model ?? fallback.model;
|
||||
if (!provider || !model) {
|
||||
return undefined;
|
||||
}
|
||||
let catalog: Awaited<ReturnType<typeof loadPreparedModelCatalog>>;
|
||||
try {
|
||||
catalog = await getSubagentSpawnDeps().loadPreparedModelCatalog({
|
||||
config: params.cfg,
|
||||
agentDir: params.targetAgentDir,
|
||||
workspaceDir: params.workspaceDir,
|
||||
});
|
||||
} catch (error) {
|
||||
return `sessions_spawn could not verify outputSchema model capabilities: ${summarizeSpawnError(error)}`;
|
||||
}
|
||||
const entry = findModelCatalogEntry(catalog, { provider, modelId: model });
|
||||
if (!entry || supportsModelTools(entry)) {
|
||||
return undefined;
|
||||
}
|
||||
return `sessions_spawn outputSchema requires a tool-capable target model; "${provider}/${model}" declares compat.supportsTools=false.`;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coe
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { SubagentLifecycleHookRunner } from "../plugins/hooks.js";
|
||||
import { isValidAgentId, normalizeAgentId, parseAgentSessionKey } from "../routing/session-key.js";
|
||||
import { listAgentIds } from "./agent-scope-config.js";
|
||||
import { resolveSpawnAdmission, resolveSpawnMode } from "./spawn-plan.js";
|
||||
import { listSwarmRunsForGroup } from "./subagent-registry.js";
|
||||
import { resolveSubagentContextMode } from "./subagent-spawn-context.js";
|
||||
@@ -15,13 +16,16 @@ import { getSubagentSpawnDeps } from "./subagent-spawn-deps.js";
|
||||
import { resolveSubagentSpawnOwnership } from "./subagent-spawn-ownership.js";
|
||||
import { resolveConfiguredSubagentRunTimeoutSeconds } from "./subagent-spawn-plan.js";
|
||||
import { loadSubagentConfig } from "./subagent-spawn-session-patch.js";
|
||||
import { resolveConfiguredAgentIds } from "./subagent-spawn-validation.js";
|
||||
import { resolveInternalSessionKey, resolveMainSessionAlias } from "./subagent-spawn.runtime.js";
|
||||
import { normalizeSubagentTaskName } from "./subagent-task-name.js";
|
||||
import { resolveSwarmConfig } from "./swarm-config.js";
|
||||
import { validateStructuredOutputSchema } from "./swarm-output-schema.js";
|
||||
import { reserveSwarmRun } from "./swarm-scheduler.js";
|
||||
|
||||
function resolveConfiguredAgentIds(cfg: OpenClawConfig): string[] {
|
||||
return listAgentIds(cfg);
|
||||
}
|
||||
|
||||
type ResolvedSubagentSpawnRequest = {
|
||||
request: {
|
||||
taskName?: string;
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { listAgentIds } from "./agent-scope-config.js";
|
||||
|
||||
export function resolveConfiguredAgentIds(cfg: OpenClawConfig): string[] {
|
||||
return listAgentIds(cfg);
|
||||
}
|
||||
|
||||
export function sanitizeMountPathHint(value?: string): string | undefined {
|
||||
const trimmed = normalizeOptionalString(value);
|
||||
if (!trimmed) {
|
||||
return undefined;
|
||||
}
|
||||
if (hasPromptUnsafeControlCharacter(trimmed)) {
|
||||
return undefined;
|
||||
}
|
||||
if (!/^[A-Za-z0-9._\-/:]+$/.test(trimmed)) {
|
||||
return undefined;
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function hasPromptUnsafeControlCharacter(value: string): boolean {
|
||||
for (const char of value) {
|
||||
const code = char.charCodeAt(0);
|
||||
if (code <= 0x1f || code === 0x7f || code === 0x85 || code === 0x2028 || code === 0x2029) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
* Validates spawn requests, prepares child sessions, stages attachments, binds delivery context, and registers runs.
|
||||
*/
|
||||
import { promises as fs } from "node:fs";
|
||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||
import { isAcpRuntimeSpawnAvailable } from "../acp/runtime/availability.js";
|
||||
import type { SubagentSpawnPreparation } from "../context-engine/types.js";
|
||||
import { isFastTestRuntimeEnv } from "../infra/env.js";
|
||||
@@ -57,7 +58,6 @@ import {
|
||||
bindThreadForSubagentSpawn,
|
||||
hasRoutableDeliveryOrigin,
|
||||
} from "./subagent-spawn-thread-binding.js";
|
||||
import { sanitizeMountPathHint } from "./subagent-spawn-validation.js";
|
||||
import {
|
||||
buildSubagentSystemPrompt,
|
||||
emitSessionLifecycleEvent,
|
||||
@@ -69,6 +69,30 @@ export { SUBAGENT_SPAWN_CONTEXT_MODES, SUBAGENT_SPAWN_MODES } from "./subagent-s
|
||||
|
||||
const SUBAGENT_CONTROL_GATEWAY_TIMEOUT_MS = 60_000;
|
||||
|
||||
function sanitizeMountPathHint(value?: string): string | undefined {
|
||||
const trimmed = normalizeOptionalString(value);
|
||||
if (!trimmed) {
|
||||
return undefined;
|
||||
}
|
||||
if (hasPromptUnsafeControlCharacter(trimmed)) {
|
||||
return undefined;
|
||||
}
|
||||
if (!/^[A-Za-z0-9._\-/:]+$/.test(trimmed)) {
|
||||
return undefined;
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function hasPromptUnsafeControlCharacter(value: string): boolean {
|
||||
for (const char of value) {
|
||||
const code = char.charCodeAt(0);
|
||||
if (code <= 0x1f || code === 0x7f || code === 0x85 || code === 0x2028 || code === 0x2029) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function spawnSubagentDirect(
|
||||
params: SpawnSubagentParams,
|
||||
ctx: SpawnSubagentContext,
|
||||
|
||||
Reference in New Issue
Block a user