UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

141 lines (140 loc) • 6.28 kB
import { o as resolveUserPath } from "./home-dir-CjKH62jd.mjs"; import "./utils-DUAwk2p5.mjs"; import { t as formatErrorMessage } from "./errors-BSdPMFao.mjs"; import { n as normalizeAgentId } from "./agent-id-D1-Q6vR4.mjs"; import "./agent-scope-config-BU4qc9bA.mjs"; import "./session-key-BC4m_Ly5.mjs"; import { n as listAgentEntries } from "./agent-roster-B45JERT5.mjs"; import { t as resolveDefaultAgentWorkspaceDir } from "./workspace-default-path-BG9KN4ds.mjs"; import "./workspace-default-CQbD5G-j.mjs"; import { p as resolveConfigSnapshotHash } from "./io.read-helpers-CK7CM5YG.mjs"; import { t as ConfigMutationConflictError } from "./mutation-conflict-Be0wSyDG.mjs"; import { r as hasResolvedRosterBeforeMigrations } from "./agent-roster-provenance-B1o8pZab.mjs"; import { c as readConfigFileSnapshot } from "./io.runtime-FunTy0y1.mjs"; import { o as transformConfigFileWithRetry, s as withConfigMutationExclusive } from "./mutate-cU6JOWHQ.mjs"; import "./config-B-SAEPyK.mjs"; import { o as readAgentDeletionJournal } from "./agent-deletion-journal-DWltVExw.mjs"; import { i as validateAgentTeamMemberIds, n as loadAgentRole, r as loadAgentTeamPreset } from "./agent-roles-SMAJH9dQ.mjs"; import { t as isUnconfiguredConfigSource } from "./fresh-install-config-Bi-9QLEM.mjs"; import { n as createAgent, r as validateAgentIdInput } from "./agent-create-rVkU1Boh.mjs"; import path from "node:path"; //#region src/agents/agent-team.ts /** Create a directed fleet through the same lifecycle owner as individual agents. */ async function createAgentTeam(params = {}) { const preset = await loadAgentTeamPreset(params.preset); const members = [preset.coordinator, ...preset.specialists].map((member, index) => { const id = index === 0 ? params.coordinator ?? member.id : member.id; const validation = validateAgentIdInput(params.prefix ? `${params.prefix}-${id}` : id); if (!validation.ok) throw new Error(validation.message); return { role: member.role, id: validation.agentId }; }); const ids = members.map(({ id }) => id); const memberIdsError = validateAgentTeamMemberIds(ids); if (memberIdsError) return { status: "error", message: memberIdsError }; await Promise.all(members.map(({ role }) => loadAgentRole(role))); const coordinatorId = ids[0]; return await withConfigMutationExclusive(async (lockedConfig) => { const snapshot = await readConfigFileSnapshot(); if (!snapshot.valid) return { status: "error", message: "Cannot create a team from an invalid OpenClaw config." }; if (Object.hasOwn(params, "expectedConfigHash") && (resolveConfigSnapshotHash(snapshot) ?? null) !== params.expectedConfigHash) throw new ConfigMutationConflictError("config changed before team creation", { retryable: false }); const authoredRoster = hasResolvedRosterBeforeMigrations(snapshot); if (params.bootstrapFirstAgent && authoredRoster) return { status: "error", message: "Cannot create the first team: an agent roster already exists." }; const bootstrapFirstAgent = !authoredRoster && (params.bootstrapFirstAgent === true || !snapshot.exists || isUnconfiguredConfigSource(snapshot.sourceConfigBeforeMigrations ?? lockedConfig)); const existingIds = new Set(bootstrapFirstAgent ? [] : listAgentEntries(lockedConfig).map(({ id }) => normalizeAgentId(id))); const collisions = ids.filter((id) => existingIds.has(id)); if (collisions.length) return { status: "error", message: `Agents already exist: ${collisions.join(", ")}. Choose another coordinator or --prefix.` }; const pending = ids.filter((id) => { const deletion = readAgentDeletionJournal(id, {}, "runtime"); return deletion && !deletion.cleanupCompleted; }); if (pending.length) return { status: "error", message: `Agent deletion cleanup is pending: ${pending.join(", ")}.` }; const workspaceRoot = resolveUserPath(params.workspaceRoot?.trim() || lockedConfig.agents?.defaults?.workspace || resolveDefaultAgentWorkspaceDir()); const existingAmbientOwnerId = lockedConfig.agents?.defaults?.systemAgent?.agentId?.trim(); const ambientOwnerId = existingAmbientOwnerId || coordinatorId; const agents = []; const fail = (message) => ({ status: "error", message: `${message}${agents.length ? ` Created agents retained: ${agents.map(({ agentId }) => agentId).join(", ")}.` : ""}`, ...agents.length ? { retainedAgents: agents } : {} }); let config = lockedConfig; let configHash; try { for (const [index, member] of members.entries()) { const created = await createAgent({ role: member.role, entry: { id: member.id, workspace: path.join(workspaceRoot, member.id), subagents: index === 0 ? { allowAgents: ids.slice(1), delegationMode: "prefer" } : { allowAgents: [] } }, bootstrapFirstAgent: index === 0 && bootstrapFirstAgent, ...index === 0 && Object.hasOwn(params, "expectedConfigHash") ? { expectedConfigHash: params.expectedConfigHash } : {}, beforePersistentApply: params.beforePersistentApply, provenance: params.provenance, onCommitted: ({ config: createdConfig, ...summary }) => { config = createdConfig; configHash = summary.configHash; agents.push(summary); } }); if (created.status === "error") return fail(created.message); } if (!existingAmbientOwnerId) { const committed = await transformConfigFileWithRetry({ maxAttempts: 1, writeOptions: params.beforePersistentApply ? { assertConfigPathForWrite: params.beforePersistentApply } : void 0, transform: (currentConfig) => ({ nextConfig: { ...currentConfig, agents: { ...currentConfig.agents, defaults: { ...currentConfig.agents?.defaults, systemAgent: { ...currentConfig.agents?.defaults?.systemAgent, agentId: coordinatorId } } } } }) }); config = committed.nextConfig; configHash = committed.persistedHash ?? void 0; } } catch (error) { if (!agents.length) throw error; return fail(formatErrorMessage(error)); } return { status: "created", coordinatorId, ambientOwnerId, agents, config, ...configHash ? { configHash } : {} }; }); } //#endregion export { createAgentTeam as t };