fix(skills): make marketplace installs available to API keys (#10854)

Dashboard-installed SkillsMP and skills.sh skills now store under the canonical global skill scope and merge into every API-key-scoped lookup, so marketplace installs actually reach API keys instead of staying invisible outside the installing session. Tenant-owned skill overrides stay isolated; existing skillsmp/skillssh rows are recognized without a migration, with canonical rows preferred on identity overlap. Closes #9716.

Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 9 files):
- 94/94 skills-*.test.ts tests pass, including the tenant-isolation regression coverage in skills-injection.test.ts (global skills reach a different API key without leaking another tenant's skills).
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.

Co-authored-by: kriptoburak <kriptoburak@users.noreply.github.com>
This commit is contained in:
Burak Bayır
2026-08-21 06:13:31 +03:00
committed by GitHub
parent fa0cd5af1c
commit 16fc433a4f
9 changed files with 225 additions and 24 deletions

View File

@@ -1,6 +1,6 @@
import { NextResponse } from "next/server";
import { z } from "zod";
import { skillRegistry } from "@/lib/skills/registry";
import { GLOBAL_SKILL_OWNER_ID, skillRegistry } from "@/lib/skills/registry";
import { validateBody, isValidationFailure } from "@/shared/validation/helpers";
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error";
@@ -39,7 +39,7 @@ export async function POST(request: Request) {
description,
schema: { input: schema.input, output: schema.output },
handler: handlerCode,
apiKeyId: apiKeyId || "system",
apiKeyId: apiKeyId || GLOBAL_SKILL_OWNER_ID,
enabled: true,
});

View File

@@ -1,7 +1,7 @@
import { NextResponse } from "next/server";
import { z } from "zod";
import { validateBody, isValidationFailure } from "@/shared/validation/helpers";
import { skillRegistry } from "@/lib/skills/registry";
import { GLOBAL_SKILL_OWNER_ID, skillRegistry } from "@/lib/skills/registry";
import { getSkillsProviderSetting } from "@/lib/skills/providerSettings";
import { isAuthenticated } from "@/shared/utils/apiAuth";
@@ -44,7 +44,7 @@ export async function POST(request: Request) {
description,
schema: { input: { content: "string" }, output: { result: "string" } },
handler: `// Installed from SkillsMP\n// SKILL.md content:\n${skillMdContent}`,
apiKeyId: provider,
apiKeyId: GLOBAL_SKILL_OWNER_ID,
enabled: true,
mode: "auto",
sourceProvider: "skillsmp",

View File

@@ -1,7 +1,7 @@
import { NextResponse } from "next/server";
import { z } from "zod";
import { validateBody, isValidationFailure } from "@/shared/validation/helpers";
import { skillRegistry } from "@/lib/skills/registry";
import { GLOBAL_SKILL_OWNER_ID, skillRegistry } from "@/lib/skills/registry";
import { isAuthenticated } from "@/shared/utils/apiAuth";
import { fetchSkillMd } from "@/lib/skills/skillssh";
import { getSkillsProviderSetting } from "@/lib/skills/providerSettings";
@@ -46,7 +46,7 @@ export async function POST(request: Request) {
description,
schema: { input: { content: "string" }, output: { result: "string" } },
handler: `// Installed from skills.sh\n// Source: ${source}/${skillId}\n// SKILL.md content:\n${skillMdContent}`,
apiKeyId: provider,
apiKeyId: GLOBAL_SKILL_OWNER_ID,
enabled: true,
mode: "auto",
sourceProvider: "skillssh",