UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

170 lines (169 loc) 8.62 kB
import { i as openOpenClawStateDatabase } from "./openclaw-state-db-BRTnL-D8.js"; import { g as registerAgentRunDelegatedAuthorityClosedHandler } from "./agent-run-registry-CKYKdfNd.js"; import { s as isOperatorUiClient } from "./message-channel-BQrhwUEA.js"; import { o as selectResolvedUserProfileById } from "./user-profiles-internal-D0HRUN8X.js"; import { i as getAdmittedRunDelegatedAuthority } from "./admitted-run-context-CHY5SdVF.js"; import { r as getGatewayToolCallerIdentity } from "./gateway-caller-context-BxVUFvkR.js"; import { M as SkillLibraryError, a as seedSkillLibrarySelection, h as resolveSkillLibraryActor, j as validateSkillLibraryPath } from "./selection-NYtFFG08.js"; import { a as mutateSkillLibrary, c as saveSkillLibrary, i as listSkillLibrary, n as libraryAuthority, o as readSkillLibrary, s as resolveSkillLibraryPresentation, t as activateLibrarySelection } from "./skills-library-BzfO7efV.js"; //#region src/skills/library/authoring.ts /** Model edits are named changes; unread binary resources retain their exact bytes and modes. */ function mergeSkillLibrarySupportFiles(current, upserts = [], deletes = []) { const changed = /* @__PURE__ */ new Set(); for (const filePath of [...upserts.map((file) => file.path), ...deletes]) { validateSkillLibraryPath(filePath); const folded = filePath.toLowerCase(); if (folded === "skill.md" || changed.has(folded)) throw new SkillLibraryError("INVALID_BUNDLE", "Support edits cannot include SKILL.md, duplicate paths, or conflicting upserts and deletes. Use proposal_content for SKILL.md."); changed.add(folded); } const files = new Map(current.map((file) => [file.path, file])); for (const filePath of deletes) if (!files.delete(filePath)) throw new SkillLibraryError("INVALID_BUNDLE", `Support file not found: ${filePath}`); for (const file of upserts) files.set(file.path, { ...file, executable: file.executable ?? files.get(file.path)?.executable }); return [...files.values()]; } //#endregion //#region src/gateway/skill-library-authoring.ts const active = /* @__PURE__ */ new Map(); /** A mixed-person steer revokes mutation authority without changing the session's committed pins. */ function invalidateSkillAuthoringForOtherRequester(sessionKey, profileId) { for (const grant of active.get(sessionKey) ?? []) if (grant.profileId !== profileId) { const db = openOpenClawStateDatabase().db; if (!profileId || selectResolvedUserProfileById(db, grant.profileId)?.id !== selectResolvedUserProfileById(db, profileId)?.id) grant.revoke(); } } /** Only ordinary attributed human ingress may mint a namespace; actions remain normal tool policy. */ function prepareGatewaySkillAuthoring(options, sessionKey, isHumanTurn) { const client = options.client; if (!isHumanTurn || !client?.authenticatedUserProfile || client.internal?.syntheticClient || client.internal?.agentRuntimeIdentity || client.internal?.senderAttribution || client.internal?.pluginRuntimeOwnerId || client.internal?.approvalRuntime || client.internal?.cronRunContinuation || client.internal?.delegatedToolPolicyHandoffId) return; const authority = libraryAuthority(options); const library = resolveSkillLibraryPresentation(authority); if (!library.profileId || library.defaultTarget === "unavailable") return; const profileId = library.profileId; invalidateSkillAuthoringForOtherRequester(sessionKey, profileId); let bound; let revoked = false; let owner; const assertCurrent = () => { authority.assertCurrent(); const caller = getGatewayToolCallerIdentity(); if (revoked || !bound || !owner || getAdmittedRunDelegatedAuthority(bound) !== owner || caller?.operationalRunInstance !== bound.operationalRunInstance || !caller.receiptAuthority || caller.receiptAuthority() === false) throw new SkillLibraryError("AUTHORITY_EXPIRED", "Personal authoring authority expired or this turn has mixed requesters. Send a fresh attributed message requesting the change."); }; const currentAuthority = { ...authority, assertCurrent, namespace: "personal" }; return { target: "personal", defaultTarget: library.defaultTarget, multipleProfiles: library.multipleProfiles, bind(context) { if (bound) { if (context !== bound) throw new SkillLibraryError("AUTHORITY_EXPIRED", "Personal authoring cannot move to a replacement run. Send a fresh message."); return; } bound = context; owner = getAdmittedRunDelegatedAuthority(context); if (!owner) throw new SkillLibraryError("AUTHORITY_EXPIRED", "Personal authoring run was not admitted."); const grant = { profileId, revoke: () => { revoked = true; } }; const grants = active.get(sessionKey) ?? /* @__PURE__ */ new Set(); grants.add(grant); active.set(sessionKey, grants); const stop = registerAgentRunDelegatedAuthorityClosedHandler((closed) => { if (closed !== owner) return; revoked = true; grants.delete(grant); if (!grants.size) active.delete(sessionKey); stop(); }); }, assertWorkspaceCurrent() { assertCurrent(); if (!resolveSkillLibraryActor(openOpenClawStateDatabase().db, authority).admin) throw new SkillLibraryError("FORBIDDEN", "Workspace authoring requires current administrator authority."); }, async invoke(input) { assertCurrent(); if (input.action === "list") return listSkillLibrary(currentAuthority); if (input.action === "read") { if (!input.skillId) throw new SkillLibraryError("INVALID_BUNDLE", "Choose skill_id from list."); return readSkillLibrary(currentAuthority, input.skillId, input.revision); } if (input.action === "activate") { if (!input.skillId) throw new SkillLibraryError("INVALID_BUNDLE", "Choose skill_id from list."); return activateLibrarySelection({ ...options, sessionMutationCommitGuard: assertCurrent }, { sessionKey, action: "attach", skillId: input.skillId, revision: input.revision }); } if (input.action !== "create" && (!input.skillId || !input.expectedRevision)) throw new SkillLibraryError("INVALID_BUNDLE", "Read the skill first; supply skill_id and expected_revision from that read."); if (input.action === "create" || input.action === "update") { const current = input.action === "update" ? await readSkillLibrary(currentAuthority, input.skillId, input.expectedRevision) : void 0; const slug = input.slug ?? current?.entry.slug; const content = input.content ?? current?.content; if (!slug || !content) throw new SkillLibraryError("INVALID_BUNDLE", "Creating a skill requires name (the human slug) and complete proposal_content."); return saveSkillLibrary(currentAuthority, { slug, content, files: mergeSkillLibrarySupportFiles(current?.files ?? [], input.files, input.deleteFiles), skillId: input.action === "update" ? input.skillId : void 0, expectedRevision: input.action === "create" ? null : input.expectedRevision }); } return mutateSkillLibrary(currentAuthority, { action: input.action, skillId: input.skillId, expectedRevision: input.expectedRevision, revision: input.revision }); } }; } //#endregion //#region src/gateway/session-input-participant.ts /** Only the live authenticated profile establishes a person; client metadata stays unresolved. */ function resolveGatewayInputParticipant(client, provenance) { if (!client || client.internal?.syntheticClient || provenance && provenance.kind !== "external_user") return; const profileId = client.authenticatedUserProfile?.profileId; if (profileId) return { type: "profile", id: profileId }; const clientInfo = client.connect?.client; return !clientInfo || isOperatorUiClient(clientInfo) ? void 0 : { type: "observation", pluginId: null, accountId: null, senderKind: "unknown", id: clientInfo.id }; } //#endregion //#region src/gateway/skill-library-session.ts /** Selection is prepared from this request's real principal, never reconstructed from provenance. */ function prepareSkillLibrarySessionCreation(client, cfg, creation) { if (!client?.authenticatedUserProfile || client.internal?.syntheticClient || creation.via === "spawn") return creation; return { ...creation, skillLibrarySelections: seedSkillLibrarySelection({ profileId: client.authenticatedUserProfile.profileId, scopes: client.connect.scopes ?? [], getConfig: typeof cfg === "function" ? cfg : () => cfg, assertCurrent: () => {} }) }; } //#endregion export { prepareGatewaySkillAuthoring as i, resolveGatewayInputParticipant as n, invalidateSkillAuthoringForOtherRequester as r, prepareSkillLibrarySessionCreation as t };