fix(coding): remediate ML-07 plugin authority

This commit is contained in:
2026-08-27 20:40:58 +08:00
parent 9407c67df2
commit cf13aa7eba
29 changed files with 1008 additions and 214 deletions

View File

@@ -1,3 +1,4 @@
import { readFileSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import {
@@ -7,6 +8,7 @@ import {
BUNDLED_CODING_PLUGIN_SETTINGS_SURFACES,
CODE_OWNED_PLUGIN_PERMISSION_IDS,
DATA_SERVICE_CAPABILITY_IDS,
DATA_SERVICE_OPERATION_DEFINITIONS,
DATA_SERVICE_PLUGIN_ID,
DATA_SERVICE_TOOL_NAMES,
type AgentPluginsRootManifest,
@@ -563,6 +565,9 @@ export function parseCodingPluginManifest(
requiresBackend,
skills,
tools,
operations: pluginId === DATA_SERVICE_PLUGIN_ID
? DATA_SERVICE_OPERATION_DEFINITIONS
: tools.map(({ capabilityId, operation, name }) => ({ capabilityId, operation, toolName: name })),
surfaces: normalizedSurfaces,
});
validateDataServiceDefinition(definition, capabilityManifestPath);
@@ -613,6 +618,42 @@ export async function loadCodingPluginDefinition(packageRoot: string): Promise<C
return definition;
}
export function loadCodingPluginDefinitionSync(packageRoot: string): CodingPluginDefinition {
const root = path.resolve(packageRoot);
const pluginManifestPath = path.join(root, PACKAGE_MANIFEST_FILE);
const rootValue = readJson(readFileSync(pluginManifestPath, 'utf8'), pluginManifestPath);
const rootManifest = parseAgentPluginsRootManifest(rootValue, pluginManifestPath);
const capabilityManifest = normalizeCandidatePath(
root,
root,
rootManifest.extensions['com.makelore'].capabilityManifest,
pluginManifestPath,
'extensions.com.makelore.capabilityManifest',
);
if (capabilityManifest !== CAPABILITY_MANIFEST_RELATIVE_PATH) {
fail(pluginManifestPath, 'extensions.com.makelore.capabilityManifest', `must point to ${CAPABILITY_MANIFEST_RELATIVE_PATH}`);
}
const capabilityManifestPath = path.join(root, capabilityManifest);
const capabilityValue = readJson(readFileSync(capabilityManifestPath, 'utf8'), capabilityManifestPath);
const definition = parseCodingPluginManifest(rootValue, capabilityValue, {
packageRoot: root,
rootManifestPath: pluginManifestPath,
capabilityManifestPath,
});
for (const skill of definition.skills) {
try {
readFileSync(path.join(root, skill.entryPath), 'utf8');
} catch (error) {
fail(
capabilityManifestPath,
`skills.${skill.id}.entry`,
`Skill entry could not be read: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
return definition;
}
export function resolveBundledCodingPluginRootPaths(resourcesRoot: string): string[] {
const root = path.resolve(resourcesRoot);
return BUNDLED_CODING_PLUGIN_ROOTS.map((relativeRoot) => path.join(root, relativeRoot));
@@ -620,22 +661,7 @@ export function resolveBundledCodingPluginRootPaths(resourcesRoot: string): stri
export const resolveBundledPluginRoots = resolveBundledCodingPluginRootPaths;
export async function loadBundledCodingPluginDefinitions(
resourcesRoot: string,
): Promise<CodingPluginDefinition[]> {
const roots = resolveBundledCodingPluginRootPaths(resourcesRoot);
const definitions = await Promise.all(roots.map(async (root, index) => {
const definition = await loadCodingPluginDefinition(root);
const expectedRoot = BUNDLED_CODING_PLUGIN_ROOTS[index];
if (definition.id !== `makelore.${expectedRoot}`) {
throw new CodingPluginManifestError(
path.join(root, PACKAGE_MANIFEST_FILE),
'name',
`fixed bundle root ${expectedRoot} contains ${definition.id}`,
);
}
return definition;
}));
function validateBundledDefinitions(definitions: readonly CodingPluginDefinition[]): void {
const pluginIds = new Set<string>();
const capabilityIds = new Set<string>();
const skillIds = new Set<string>();
@@ -657,7 +683,46 @@ export async function loadBundledCodingPluginDefinitions(
capabilityIds.add(tool.capabilityId);
}
}
return definitions;
}
export async function loadBundledCodingPluginDefinitions(
resourcesRoot: string,
): Promise<readonly CodingPluginDefinition[]> {
const roots = resolveBundledCodingPluginRootPaths(resourcesRoot);
const definitions = await Promise.all(roots.map(async (root, index) => {
const definition = await loadCodingPluginDefinition(root);
const expectedRoot = BUNDLED_CODING_PLUGIN_ROOTS[index];
if (definition.id !== `makelore.${expectedRoot}`) {
throw new CodingPluginManifestError(
path.join(root, PACKAGE_MANIFEST_FILE),
'name',
`fixed bundle root ${expectedRoot} contains ${definition.id}`,
);
}
return definition;
}));
validateBundledDefinitions(definitions);
return Object.freeze(definitions);
}
export function loadBundledCodingPluginDefinitionsSync(
resourcesRoot: string,
): readonly CodingPluginDefinition[] {
const roots = resolveBundledCodingPluginRootPaths(resourcesRoot);
const definitions = roots.map((root, index) => {
const definition = loadCodingPluginDefinitionSync(root);
const expectedRoot = BUNDLED_CODING_PLUGIN_ROOTS[index];
if (definition.id !== `makelore.${expectedRoot}`) {
throw new CodingPluginManifestError(
path.join(root, PACKAGE_MANIFEST_FILE),
'name',
`fixed bundle root ${expectedRoot} contains ${definition.id}`,
);
}
return definition;
});
validateBundledDefinitions(definitions);
return Object.freeze(definitions);
}
export const parseBundledCodingPlugins = loadBundledCodingPluginDefinitions;

View File

@@ -2,7 +2,6 @@ import path from 'node:path';
import { atomicWriteJson, readJsonFile, type JsonFileWriter } from '../coding-projects/atomic-json';
import { readCodingProjectConfigV2 } from '../coding-projects/project-config';
import {
BUNDLED_CODING_PLUGIN_DEFINITIONS,
DATA_SERVICE_PLUGIN_ID,
} from '../../shared/coding-plugins';
@@ -98,7 +97,7 @@ function knownPluginIdSet(
): Set<string> {
const source = typeof value === 'function'
? value()
: value ?? BUNDLED_CODING_PLUGIN_DEFINITIONS.map(({ id }) => id);
: value ?? [DATA_SERVICE_PLUGIN_ID];
return new Set(source.map((id) => id.trim()).filter(Boolean));
}
@@ -193,6 +192,7 @@ interface ReadSelectionResult {
export class ProjectPluginService {
private readonly mutationTails = new Map<string, Promise<unknown>>();
private readonly managedInputRevisions = new Map<string, number>();
private readonly legacySelections = new Map<string, readonly string[]>();
constructor(private readonly options: ProjectPluginServiceOptions = {}) {}
@@ -211,6 +211,7 @@ export class ProjectPluginService {
const result = await this.readFile(filePath);
const knownIds = knownPluginIdSet(this.options.knownPluginIds);
if (result.file) {
this.legacySelections.delete(project);
const enabledPluginIds = result.file.enabledPluginIds;
return {
projectPath: project,
@@ -225,7 +226,11 @@ export class ProjectPluginService {
};
}
const legacyProjectedPluginIds = await this.legacyProjectedPluginIds(project);
let legacyProjectedPluginIds = this.legacySelections.get(project);
if (!legacyProjectedPluginIds) {
legacyProjectedPluginIds = Object.freeze(await this.legacyProjectedPluginIds(project));
this.legacySelections.set(project, legacyProjectedPluginIds);
}
const enabledPluginIds = [...legacyProjectedPluginIds];
return {
projectPath: project,

View File

@@ -1,5 +1,4 @@
import {
BUNDLED_CODING_PLUGIN_DEFINITIONS,
type CodingPluginDefinition,
type CodingPluginToolDefinition,
type PluginBillingMode,
@@ -120,7 +119,7 @@ export interface CodingCapabilityRegistryOptions {
getEnabledPluginIds(projectPath: string): Promise<readonly string[]>;
};
adapters: readonly CodingPluginAdapter[];
definitions?: readonly CodingPluginDefinition[];
definitions: readonly CodingPluginDefinition[];
getDurableProjectId?: (projectPath: string, localProjectId: string) => Promise<string> | string;
}
@@ -340,7 +339,7 @@ export class CodingCapabilityRegistryImpl implements CodingCapabilityRegistryPor
private readonly adaptersByPluginId: ReadonlyMap<string, CodingPluginAdapter>;
constructor(private readonly options: CodingCapabilityRegistryOptions) {
const definitions = (options.definitions ?? BUNDLED_CODING_PLUGIN_DEFINITIONS).filter(definitionValid);
const definitions = options.definitions.filter(definitionValid);
this.definitions = Object.freeze([...definitions]);
const tools = new Map<string, { definition: CodingPluginDefinition; tool: CodingPluginToolDefinition }>();
for (const definition of this.definitions) {