UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

654 lines (653 loc) 28.3 kB
import "./src-vebZIeLe.js"; import { t as stableStringify } from "./stable-stringify-C8X7niaI.js"; import { a as writeRuntimeJson } from "./runtime-CF2WjnNZ.js"; import { c as resolveUserPath } from "./home-dir-BPhrG-aM.js"; import { n as resolvePathViaExistingAncestorSync } from "./boundary-path-DMNeww4q.js"; import { p as redactSensitiveText } from "./redact-BtvPPfTi.js"; import { M as assertNoSymlinkParents, t as FsSafeError, w as root } from "./fs-safe-B6pvPGnf.js"; import "./utils-P__uGsPB.js"; import { C as digestClawMcpServer, a as MAX_MANAGED_WORKSPACE_BYTES, g as CLAW_BOOTSTRAP_FILE_NAMES, h as CLAW_ADD_PLAN_SCHEMA_VERSION, i as MAX_MANAGED_FILE_BYTES, l as materializeClawToolProfile, o as clawManifestWorkspaceConflictsWithPath, v as CLAW_OUTPUT_STABILITY } from "./reader-lgRaBKW0.js"; import { relative, resolve } from "node:path"; import { lstat, realpath } from "node:fs/promises"; import { homedir } from "node:os"; import { createHash } from "node:crypto"; //#region src/claws/application-plan.ts function clawProfileExtensionPackages(profile) { return (profile?.extensions ?? []).map((extension) => ({ kind: "plugin", source: extension.source, ref: extension.ref, version: extension.version })); } function blocker$1(code, path, message) { return { level: "error", code, phase: "plan", path, message }; } function findClawExtensionPackageCollisions(params) { const declaredPackageIds = new Set(params.packages.map((pkg) => `${pkg.kind}:${pkg.ref}`)); const collisions = []; for (const [index, extension] of params.extensions.entries()) { const packageId = `plugin:${extension.ref}`; if (declaredPackageIds.has(packageId)) { collisions.push({ index, diagnostic: blocker$1("extension_package_collision", `$.profiles.openclaw.extensions[${index}]`, `Extension package ${JSON.stringify(packageId)} is already declared by the portable manifest or another profile extension.`) }); continue; } declaredPackageIds.add(packageId); } return collisions; } function extensionCapabilityChange(params) { const effect = { id: params.extension.id, source: params.extension.source, ref: params.extension.ref, version: params.extension.version, expectedFormat: params.extension.format, detectedFormat: params.preflight.detectedFormat ?? "unresolved", integrity: params.preflight.integrity ?? "unresolved", mapped: params.preflight.mapped ?? [], unavailable: params.preflight.unavailable ?? [], adapterIdentity: params.preflight.adapterIdentity ?? "unresolved", ...params.preflight.installId ? { installId: params.preflight.installId } : {}, ...params.preflight.warning ? { riskWarning: params.preflight.warning } : {} }; return { kind: "package", id: `extension:${params.extension.id}`, path: `openclaw.extensions.${params.extension.id}`, action: params.preflight.action === "reuse" ? "reuse" : "install", reason: params.preflight.action === "reuse" ? "The OpenClaw profile requires access to an existing native extension." : "The OpenClaw profile requires installation of native extension content or executable code.", effect, classification: "escalation", requiresDistinctConsent: true, digest: `sha256:${createHash("sha256").update(stableStringify(effect)).digest("hex")}` }; } async function planClawExtensions(params) { const extensions = []; const actions = []; const capabilityChanges = []; const requirements = []; const blockers = []; for (const [index, extension] of params.extensions.entries()) { const preflight = params.packagePreflight ? await params.packagePreflight({ kind: "plugin", source: extension.source, ref: extension.ref, version: extension.version }, params.workspace) : { ok: false, code: "package_install_unavailable", message: "Extension preflight is unavailable." }; const completeProvenance = preflight.ok && Boolean(preflight.integrity && preflight.installId && preflight.action && preflight.detectedFormat && preflight.adapterIdentity); const incompleteProvenance = preflight.ok && !completeProvenance ? blocker$1("extension_provenance_incomplete", `$.profiles.openclaw.extensions[${index}]`, `Extension ${JSON.stringify(extension.id)} did not resolve complete canonical identity and adapter provenance.`) : void 0; const formatMismatch = preflight.ok && completeProvenance && preflight.detectedFormat !== extension.format ? blocker$1("extension_format_mismatch", `$.profiles.openclaw.extensions[${index}].format`, `Extension ${JSON.stringify(extension.id)} declares format ${JSON.stringify(extension.format)}, but the canonical plugin detector found ${JSON.stringify(preflight.detectedFormat ?? "unknown")}.`) : void 0; const diagnostic = !preflight.ok ? blocker$1(preflight.code ?? "extension_preflight_failed", `$.profiles.openclaw.extensions[${index}]`, preflight.message ?? "Extension preflight failed.") : incompleteProvenance ?? formatMismatch; if (diagnostic) blockers.push(diagnostic); if (preflight.ok && preflight.requirements) requirements.push(...preflight.requirements); const requirementState = diagnostic ? "conflicting" : preflight.action === "install" ? "missing-installable" : preflight.requirements && preflight.requirements.length > 0 ? "setup-required" : "satisfied"; const extensionPlan = { ...extension, ...preflight.detectedFormat ? { detectedFormat: preflight.detectedFormat } : {}, ...preflight.integrity ? { integrity: preflight.integrity } : {}, ...preflight.installId ? { installId: preflight.installId } : {}, ...preflight.action ? { ownerAction: preflight.action } : {}, requirementState, mapped: preflight.mapped ?? [], unavailable: preflight.unavailable ?? [], ...preflight.adapterIdentity ? { adapterIdentity: preflight.adapterIdentity } : {}, blocked: Boolean(diagnostic) }; extensions.push(extensionPlan); actions.push({ kind: "package", id: `plugin:${extension.ref}`, action: preflight.ok && preflight.action === "reuse" ? "reuse" : "install", target: `${extension.source}:${extension.ref}@${extension.version}`, ...preflight.integrity ? { digest: preflight.integrity } : {}, details: { kind: "plugin", source: extension.source, ref: extension.ref, version: extension.version, ...preflight.integrity ? { integrity: preflight.integrity } : {}, ...preflight.installId ? { installId: preflight.installId } : {}, ...preflight.action ? { ownerAction: preflight.action } : {}, requirementState, ...preflight.requirements ? { prerequisites: preflight.requirements } : {}, ...completeProvenance ? { extension: { id: extension.id, format: extension.format, detectedFormat: preflight.detectedFormat, mapped: preflight.mapped ?? [], unavailable: preflight.unavailable ?? [], adapterIdentity: preflight.adapterIdentity } } : {}, expectedState: !preflight.ok ? "unresolved" : preflight.action === "reuse" ? "present-exact" : "absent", ...preflight.warning ? { riskWarning: preflight.warning } : {} }, blocked: extensionPlan.blocked, ...diagnostic ? { reason: diagnostic.message } : {} }); capabilityChanges.push(extensionCapabilityChange({ extension, preflight })); } return { extensions, actions, capabilityChanges, requirements, blockers }; } //#endregion //#region src/claws/lifecycle.ts const AGENT_ID_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/; function capabilityChange(change) { return { ...change, classification: "escalation", requiresDistinctConsent: true, digest: `sha256:${createHash("sha256").update(stableStringify(change.effect)).digest("hex")}` }; } function sourceReferencePath(root, path) { return `${root.replace(/\/+$/u, "")}/${path.replaceAll("\\", "/")}`; } function canonicalWorkspacePath(value) { return resolvePathViaExistingAncestorSync(resolve(resolveUserPath(value))); } function blocker(code, path, message) { return { level: "error", code, phase: "plan", path, message }; } function blockedWorkspaceFileAction(params) { return { kind: "workspaceFile", id: params.id, action: "write", target: params.target, source: params.source, blocked: true, reason: params.reason }; } function workspaceSourceErrorCode(error) { if (error instanceof FsSafeError) { if (error.code === "too-large") return "workspace_source_too_large"; if (error.code === "symlink" || error.code === "hardlink" || error.code === "path-mismatch") return "workspace_source_unsafe"; } if (error instanceof Error && error.message.includes("symlinked directory")) return "workspace_source_unsafe"; return "workspace_source_invalid"; } function workspaceSourceMessage(code, sourcePath) { if (code === "workspace_source_too_large") return `Workspace source ${JSON.stringify(sourcePath)} exceeds ${MAX_MANAGED_FILE_BYTES} bytes.`; if (code === "workspace_sources_too_large") return `Workspace sources exceed ${MAX_MANAGED_WORKSPACE_BYTES} aggregate bytes.`; if (code === "workspace_source_unsafe") return `Workspace source ${JSON.stringify(sourcePath)} must be a regular, non-symlinked, non-hardlinked file.`; return `Workspace source ${JSON.stringify(sourcePath)} must resolve to a file inside the Claw package.`; } async function inspectWorkspaceFileAction(params) { const requestedSource = resolve(params.source.packageRoot, params.sourcePath); const requestedTarget = resolve(params.workspace, params.targetPath); try { await assertNoSymlinkParents({ rootDir: params.source.packageRoot, targetPath: requestedSource, allowMissing: false, messagePrefix: "Workspace source" }); const opened = await params.sourceRoot.open(params.sourcePath, { hardlinks: "reject", symlinks: "reject" }); await opened[Symbol.asyncDispose](); if (opened.stat.size > 1048576) throw new FsSafeError("too-large", `file exceeds limit of ${MAX_MANAGED_FILE_BYTES} bytes (got ${opened.stat.size})`); return { pending: { sourcePath: params.sourcePath, manifestPath: params.manifestPath, byteLength: opened.stat.size, action: { kind: "workspaceFile", id: params.id, action: "write", target: requestedTarget, source: opened.realPath, details: { expectedState: "absent" }, blocked: false } } }; } catch (error) { const code = workspaceSourceErrorCode(error); const message = workspaceSourceMessage(code, params.sourcePath); const diagnostic = blocker(code, params.manifestPath, message); return { action: blockedWorkspaceFileAction({ id: params.id, target: requestedTarget, source: requestedSource, reason: diagnostic.message }), blocker: diagnostic }; } } async function buildClawAddPlan(params) { const context = params.context ?? {}; const finalId = context.agentId ?? params.manifest.agent.id; const workspace = canonicalWorkspacePath(context.workspace ?? resolve(homedir(), ".openclaw", `workspace-${finalId}`)); const packageRoot = await realpath(params.source.packageRoot).catch(() => params.source.packageRoot); const manifestPath = resolvePathViaExistingAncestorSync(resolve(params.source.manifestPath)); const source = { ...params.source, packageRoot, manifestPath }; const planSource = context.sourceReferenceRoot ? { ...source, packageRoot: context.sourceReferenceRoot, manifestPath: sourceReferencePath(context.sourceReferenceRoot, relative(packageRoot, manifestPath)) } : source; const planSourcePath = (path, fallback) => context.sourceReferenceRoot ? sourceReferencePath(context.sourceReferenceRoot, path) : fallback; const sourceRoot = await root(packageRoot); const blockers = []; const actions = []; const capabilityChanges = []; const readinessRequirements = []; if (!AGENT_ID_PATTERN.test(finalId)) blockers.push(blocker("invalid_agent_id", "$.agent.id", `Final agent id ${JSON.stringify(finalId)} is not a valid portable agent id.`)); const agentBlocked = new Set(context.existingAgentIds ?? []).has(finalId); const openClawAgentSettings = params.openClawProfile?.agent ?? {}; const persistedOpenClawAgentSettings = params.reconstructLegacyDynamicToolProfilePlan ? openClawAgentSettings : materializeClawToolProfile(openClawAgentSettings); const agentConfig = { ...params.manifest.agent, ...persistedOpenClawAgentSettings, id: finalId, workspace }; if (agentBlocked) blockers.push(blocker("agent_id_collision", "$.agent.id", `Agent id ${JSON.stringify(finalId)} already exists; Claws never merge into existing agents.`)); actions.push({ kind: "agent", id: finalId, action: "create", target: `agents.entries[${JSON.stringify(finalId)}]`, details: { ...agentConfig, expectedState: "absent" }, blocked: agentBlocked || !AGENT_ID_PATTERN.test(finalId) }); const agentCapabilityEffect = { ...openClawAgentSettings.sandbox ? { sandbox: openClawAgentSettings.sandbox } : {}, ...openClawAgentSettings.tools ? { tools: openClawAgentSettings.tools } : {}, ...openClawAgentSettings.memory ? { memory: openClawAgentSettings.memory } : {}, ...openClawAgentSettings.heartbeat ? { heartbeat: openClawAgentSettings.heartbeat } : {} }; if (Object.keys(agentCapabilityEffect).length > 0) capabilityChanges.push(capabilityChange({ kind: "agent", id: finalId, path: "agent", action: "create", reason: "The new agent declares sandbox, tool, memory-search, or recurring heartbeat capabilities.", effect: agentCapabilityEffect })); const configuredWorkspaceConflict = new Set([...context.existingWorkspacePaths ?? []].map((path) => canonicalWorkspacePath(path))).has(workspace); const workspaceExistsOnDisk = await lstat(workspace).then(() => true).catch(() => false); const resumableWorkspace = context.resumableWorkspace ? canonicalWorkspacePath(context.resumableWorkspace) : void 0; const workspaceBlocked = configuredWorkspaceConflict || workspaceExistsOnDisk && resumableWorkspace !== workspace; if (workspaceBlocked) blockers.push(blocker("workspace_collision", "$.workspace", `Workspace ${JSON.stringify(workspace)} already exists; a Claw requires a new workspace.`)); actions.push({ kind: "workspace", id: finalId, action: "create", target: workspace, details: { expectedState: "absent" }, blocked: workspaceBlocked, ...workspaceBlocked ? { reason: `Workspace ${JSON.stringify(workspace)} already exists.` } : {} }); if (params.packageBootstrap && params.includePackageBootstrap !== false) actions.push({ kind: "bootstrap", id: "BOOTSTRAP.md", action: "write", target: resolve(workspace, "BOOTSTRAP.md"), source: planSourcePath(params.packageBootstrap.sourcePath, params.packageBootstrap.realPath), digest: params.packageBootstrap.digest, details: { sourcePath: params.packageBootstrap.sourcePath, byteLength: params.packageBootstrap.byteLength, expectedState: "absent-or-native-consumed", lifecycle: "native-seed-once" }, blocked: workspaceBlocked, ...workspaceBlocked ? { reason: `Workspace ${JSON.stringify(workspace)} already exists.` } : {} }); const pendingWorkspaceFiles = []; async function addWorkspaceFileInspection(fileParams) { const result = await inspectWorkspaceFileAction({ sourceRoot, source, workspace, sourcePath: fileParams.sourcePath, targetPath: fileParams.targetPath, id: fileParams.id, manifestPath: fileParams.manifestPath }); const action = result.pending?.action ?? result.action; if (!action) throw new Error("Claw workspace source inspection did not produce an action"); if (action.source) action.source = planSourcePath(fileParams.sourcePath, action.source); action.blocked ||= workspaceBlocked; if (workspaceBlocked) action.reason = `Workspace ${JSON.stringify(workspace)} already exists.`; actions.push(action); if (result.pending) pendingWorkspaceFiles.push(result.pending); if (result.blocker) blockers.push(result.blocker); } if (params.clawMarkdownBody && params.clawMarkdownBody.toString("utf8").trim().length > 0) { if (clawManifestWorkspaceConflictsWithPath(params.manifest, "SOUL.md")) { const diagnostic = blocker("claw_body_soul_conflict", "$.workspace", "CLAW.md body content and an explicit SOUL.md workspace declaration cannot both be present."); blockers.push(diagnostic); actions.push({ kind: "workspaceFile", id: "SOUL.md", action: "write", target: resolve(workspace, "SOUL.md"), source: planSource.manifestPath, sourceKind: "clawMarkdownBody", blocked: true, reason: diagnostic.message }); } else { const pending = { sourcePath: source.manifestPath, manifestPath: "$body", byteLength: params.clawMarkdownBody.byteLength, content: params.clawMarkdownBody, action: { kind: "workspaceFile", id: "SOUL.md", action: "write", target: resolve(workspace, "SOUL.md"), source: planSource.manifestPath, sourceKind: "clawMarkdownBody", details: { expectedState: "absent" }, blocked: false } }; pendingWorkspaceFiles.push(pending); actions.push(pending.action); } } for (const name of CLAW_BOOTSTRAP_FILE_NAMES) { const declaration = params.manifest.workspace.bootstrapFiles[name]; if (!declaration) continue; await addWorkspaceFileInspection({ sourcePath: declaration.source, targetPath: name, id: name, manifestPath: `$.workspace.bootstrapFiles.${name}` }); } for (const [index, file] of params.manifest.workspace.files.entries()) await addWorkspaceFileInspection({ sourcePath: file.source, targetPath: file.path, id: file.path, manifestPath: `$.workspace.files[${index}]` }); if (pendingWorkspaceFiles.reduce((total, pending) => total + pending.byteLength, 0) > 4194304) { const diagnostic = blocker("workspace_sources_too_large", "$.workspace", workspaceSourceMessage("workspace_sources_too_large", "")); blockers.push(diagnostic); for (const pending of pendingWorkspaceFiles) { pending.action.blocked = true; pending.action.reason = diagnostic.message; } } else for (const pending of pendingWorkspaceFiles) { if (pending.content) { pending.action.digest = `sha256:${createHash("sha256").update(pending.content).digest("hex")}`; continue; } try { await assertNoSymlinkParents({ rootDir: source.packageRoot, targetPath: resolve(source.packageRoot, pending.sourcePath), allowMissing: false, messagePrefix: "Workspace source" }); const read = await sourceRoot.read(pending.sourcePath, { hardlinks: "reject", maxBytes: MAX_MANAGED_FILE_BYTES, symlinks: "reject" }); pending.action.source = planSourcePath(pending.sourcePath, read.realPath); pending.action.digest = `sha256:${createHash("sha256").update(read.buffer).digest("hex")}`; } catch (error) { const code = workspaceSourceErrorCode(error); const message = workspaceSourceMessage(code, pending.sourcePath); const diagnostic = blocker(code, pending.manifestPath, message); pending.action.blocked = true; pending.action.reason = diagnostic.message; blockers.push(diagnostic); } } for (const [index, pkg] of params.manifest.packages.entries()) { const preflight = context.packagePreflight ? await context.packagePreflight(pkg, workspace) : { ok: false, code: "package_install_unavailable", message: "Package preflight is unavailable." }; const diagnostic = preflight.ok ? void 0 : blocker(preflight.code ?? "package_install_unavailable", `$.packages[${index}]`, preflight.message ?? "Package preflight failed."); if (diagnostic) blockers.push(diagnostic); if (preflight.ok && preflight.requirements) readinessRequirements.push(...preflight.requirements); actions.push({ kind: "package", id: `${pkg.kind}:${pkg.ref}`, action: preflight.ok && preflight.action === "reuse" ? "reuse" : "install", target: `${pkg.source}:${pkg.ref}@${pkg.version}`, digest: preflight.integrity, details: { ...pkg, ...preflight.integrity ? { integrity: preflight.integrity } : {}, ...preflight.installId ? { installId: preflight.installId } : {}, ...preflight.warning ? { riskWarning: preflight.warning } : {}, ...preflight.requirements ? { prerequisites: preflight.requirements } : {}, expectedState: !preflight.ok ? "unresolved" : preflight.action === "reuse" ? "present-exact" : "absent", ownerAction: preflight.action, requirementState: !preflight.ok ? "conflicting" : preflight.action === "install" ? "missing-installable" : preflight.requirements && preflight.requirements.length > 0 ? "setup-required" : "satisfied" }, blocked: !preflight.ok, ...diagnostic ? { reason: diagnostic.message } : {} }); capabilityChanges.push(capabilityChange({ kind: "package", id: `${pkg.kind}:${pkg.ref}`, path: `packages.${pkg.kind}.${pkg.ref}`, action: preflight.ok && preflight.action === "reuse" ? "reuse" : "install", reason: preflight.ok && preflight.action === "reuse" ? "The Claw requires access to an existing package capability." : "The Claw requires downloadable package content or executable code.", effect: { kind: pkg.kind, source: pkg.source, ref: pkg.ref, version: pkg.version, integrity: preflight.integrity ?? "unresolved", ...preflight.installId ? { installId: preflight.installId } : {}, ...preflight.warning ? { riskWarning: preflight.warning } : {} } })); } const extensionPlan = await planClawExtensions({ extensions: params.openClawProfile?.extensions ?? [], workspace, packagePreflight: context.packagePreflight }); const extensions = extensionPlan.extensions; const extensionCollisions = findClawExtensionPackageCollisions({ packages: params.manifest.packages, extensions: params.openClawProfile?.extensions ?? [] }); const collisionIndexes = new Set(extensionCollisions.map(({ index }) => index)); blockers.push(...extensionCollisions.map(({ diagnostic }) => diagnostic)); for (const [index, action] of extensionPlan.actions.entries()) { if (collisionIndexes.has(index)) continue; actions.push(action); } capabilityChanges.push(...extensionPlan.capabilityChanges); readinessRequirements.push(...extensionPlan.requirements); blockers.push(...extensionPlan.blockers); const existingMcpServerNames = new Set(context.existingMcpServerNames ?? []); for (const [name, server] of Object.entries(params.manifest.mcpServers)) { const existingServer = context.existingMcpServers?.[name]; const exactExisting = existingServer !== void 0 && digestClawMcpServer(existingServer) === digestClawMcpServer(server); const blocked = !exactExisting && (existingMcpServerNames.has(name) || existingServer !== void 0); if (blocked) blockers.push(blocker("mcp_server_collision", `$.mcpServers.${name}`, `MCP server ${JSON.stringify(name)} already exists with different or unresolved configuration and will not be overwritten.`)); if ("env" in server) for (const value of Object.values(server.env ?? {})) readinessRequirements.push({ kind: "environment", mcpServer: name, name: value.slice(2, -1) }); if ("auth" in server && server.auth === "oauth") readinessRequirements.push({ kind: "oauth", mcpServer: name }); actions.push({ kind: "mcpServer", id: name, action: "configure", target: `mcp.servers.${name}`, details: { ...server, expectedState: exactExisting ? "present-exact" : "absent", prerequisites: readinessRequirements.filter((requirement) => requirement.kind !== "plugin-setup" && requirement.mcpServer === name) }, blocked }); capabilityChanges.push(capabilityChange({ kind: "mcpServer", id: name, path: `mcpServers.${name}`, action: "configure", reason: "The Claw declares an MCP execution or network tool surface.", effect: { ...server, ..."env" in server && server.env ? { env: Object.keys(server.env).toSorted() } : {} } })); } for (const job of params.manifest.cronJobs) { actions.push({ kind: "cronJob", id: job.id, action: "schedule", target: `cron:${job.id}:agent=${finalId}`, details: { ...job, agentId: finalId, expectedState: "absent", ...job.delivery?.channel === "last" ? { deliveryResolution: "local-channel-state:last" } : {} }, blocked: false }); capabilityChanges.push(capabilityChange({ kind: "cronJob", id: job.id, path: `cronJobs.${job.id}`, action: "schedule", reason: "The Claw declares recurring scheduled work.", effect: { ...job, agentId: finalId } })); } capabilityChanges.sort((left, right) => `${left.kind}:${left.id}:${left.path}`.localeCompare(`${right.kind}:${right.id}:${right.path}`)); const planIntegrity = `sha256:${createHash("sha256").update(stableStringify({ manifestSchemaVersion: params.manifest.schemaVersion, clawIntegrity: source.integrity, finalId, workspace, actions, capabilityChanges, blockers, extensions })).digest("hex")}`; return { schemaVersion: CLAW_ADD_PLAN_SCHEMA_VERSION, manifestSchemaVersion: params.manifest.schemaVersion, stability: CLAW_OUTPUT_STABILITY, dryRun: true, mutationAllowed: false, planIntegrity, claw: planSource, agent: { requestedId: params.manifest.agent.id, finalId, workspace, config: agentConfig }, summary: { totalActions: actions.length, agentActions: actions.filter((action) => action.kind === "agent").length, workspaceActions: actions.filter((action) => action.kind === "workspace" || action.kind === "bootstrap" || action.kind === "workspaceFile").length, packageActions: actions.filter((action) => action.kind === "package").length, mcpServerActions: actions.filter((action) => action.kind === "mcpServer").length, cronJobActions: actions.filter((action) => action.kind === "cronJob").length, blockedActions: actions.filter((action) => action.blocked).length, capabilityEscalations: capabilityChanges.length }, actions, capabilityChanges, readiness: { ready: readinessRequirements.length === 0, requirements: readinessRequirements }, extensions, blockers, diagnostics: params.diagnostics ?? [] }; } //#endregion //#region src/cli/claws-cli-output.ts function emitClawFailure(runtime, json, message, payload) { if (json) writeRuntimeJson(runtime, payload); else runtime.error(message); runtime.exit(1); } function formatClawDiagnostics(diagnostics) { return diagnostics.map((diagnostic) => `${diagnostic.level.toUpperCase()} ${diagnostic.code} ${diagnostic.path}: ${diagnostic.message}`).join("\n"); } function logClawExperimentalWarning(runtime) { runtime.log("Experimental: Claws contracts may change while RFC 0016 is under review."); } function logClawUpdatePlanSummary(plan, runtime) { runtime.log(`Agent: ${plan.agentId}`); runtime.log(`Update actions: ${plan.summary.totalActions}`); runtime.log(`Add: ${plan.summary.added}; change: ${plan.summary.changed}; remove: ${plan.summary.removed}; release: ${plan.summary.released}; unchanged: ${plan.summary.unchanged}; manual: ${plan.summary.manual}`); runtime.log(`Capability changes: ${plan.summary.capabilityChanges}; escalations requiring explicit review: ${plan.summary.capabilityEscalations}`); runtime.log(`Plan integrity: ${plan.planIntegrity}`); if (plan.summary.capabilityEscalations > 0) runtime.log("Capability consent: the exact plan-integrity token binds every ! change disclosed below."); for (const change of plan.capabilityChanges) { const current = change.current?.summary ?? "unset"; const desired = change.desired?.summary ?? "unset"; runtime.log(` ${change.requiresDistinctConsent ? "!" : "-"} ${change.path}: ${current} -> ${desired} (${change.action})`); runtime.log(redactSensitiveText(` effect: ${JSON.stringify(change.effect)}`)); } if (plan.readiness.requirements.length > 0) { runtime.log(`Setup requirements (${plan.readiness.requirements.length}):`); for (const requirement of plan.readiness.requirements) runtime.log(redactSensitiveText(` - ${JSON.stringify(requirement)}`)); } if (plan.blockers.length > 0) runtime.error(formatClawDiagnostics(plan.blockers)); } //#endregion export { buildClawAddPlan as a, planClawExtensions as c, logClawUpdatePlanSummary as i, formatClawDiagnostics as n, clawProfileExtensionPackages as o, logClawExperimentalWarning as r, findClawExtensionPackageCollisions as s, emitClawFailure as t };