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

@@ -20,7 +20,6 @@ import type { PiProductTools } from '../coding-runtime/pi/product-tools';
import type { DataServiceOperations } from '../services/data-service-client';
import type { PreviewDataSessionManager } from '../services/preview-data-session';
import {
BUNDLED_CODING_PLUGIN_DEFINITIONS,
type CodingPluginDefinition,
type PluginBillingMode,
} from '../../shared/coding-plugins';
@@ -97,6 +96,8 @@ export interface CodingPluginProjectProjection {
items: Array<{
id: string;
version: string;
contractVersion: number;
requiresBackend: boolean;
displayName: string;
description: string;
enabled: boolean;
@@ -105,7 +106,17 @@ export interface CodingPluginProjectProjection {
skills: Array<{ id: string; assignedAgentIds: string[] }>;
capabilities: Array<{
id: string;
operations: Array<{ id: string; billing: PublicPluginBilling }>;
operations: Array<{
id: string;
billing: PublicPluginBilling;
tool: {
name: string;
label: string;
description: string;
mutation: 'read' | 'write' | 'destructive';
permissions: string[];
} | null;
}>;
}>;
settingsSurface: string | null;
}>;
@@ -137,7 +148,7 @@ export interface CreateCodingProjectPluginServiceOptions {
projectPlugins: Pick<ProjectPluginService, 'getEnabledPluginIds' | 'setEnabled'>;
policyClient: Pick<PluginPolicyClient, 'refresh' | 'getState'>;
adapters: readonly CodingPluginAdapter[];
definitions?: readonly CodingPluginDefinition[];
definitions: readonly CodingPluginDefinition[];
}
function policyOperation(
@@ -245,7 +256,7 @@ function effectiveState(input: {
export function createCodingProjectPluginService(
options: CreateCodingProjectPluginServiceOptions,
): CodingProjectPluginService {
const definitions = options.definitions ?? BUNDLED_CODING_PLUGIN_DEFINITIONS;
const definitions = options.definitions;
const adapters = new Map(options.adapters.map((adapter) => [adapter.pluginId, adapter]));
async function project(localProjectId: string) {
@@ -278,13 +289,27 @@ export function createCodingProjectPluginService(
const policyAvailable = Boolean(
pluginPolicy?.supported_contract_versions.includes(definition.contractVersion),
);
const capabilityIds = [...new Set(definition.tools.map(({ capabilityId }) => capabilityId))];
const capabilityIds = [...new Set(definition.operations.map(({ capabilityId }) => capabilityId))];
const capabilities = capabilityIds.flatMap((capabilityId) => {
const operations = definition.tools
.filter((tool) => tool.capabilityId === capabilityId)
.flatMap((tool) => {
const joined = policyOperation(policy, definition, capabilityId, tool.operation);
return joined ? [{ id: tool.operation, billing: publicBilling(joined.billing, policy) }] : [];
const operations = definition.operations
.filter((candidate) => candidate.capabilityId === capabilityId)
.flatMap((candidate) => {
const joined = policyOperation(policy, definition, capabilityId, candidate.operation);
if (!joined) return [];
const tool = candidate.toolName
? definition.tools.find(({ name }) => name === candidate.toolName)
: undefined;
return [{
id: candidate.operation,
billing: publicBilling(joined.billing, policy),
tool: tool ? {
name: tool.name,
label: tool.label,
description: tool.description,
mutation: tool.mutation,
permissions: [...tool.permissions],
} : null,
}];
});
return operations.length > 0 ? [{ id: capabilityId, operations }] : [];
});
@@ -292,6 +317,8 @@ export function createCodingProjectPluginService(
return {
id: definition.id,
version: definition.version,
contractVersion: definition.contractVersion,
requiresBackend: definition.requiresBackend,
displayName: definition.displayName,
description: definition.description,
enabled,
@@ -349,6 +376,7 @@ export interface CodingProductHostOptions {
projects: Pick<CodingProjectService, 'getActiveProject' | 'findActiveConversation'>;
productTools: PiProductTools;
files?: CodingProjectFileService;
getEnabledPluginIds?(projectPath: string): Promise<readonly string[]>;
listPiCommands?(conversationId: string): Promise<unknown>;
}
@@ -455,16 +483,23 @@ export function createCodingProductHost(options: CodingProductHostOptions): Codi
},
async listSkills(agentId) {
const project = await activeProject();
const enabledPluginIds = options.getEnabledPluginIds
? await options.getEnabledPluginIds(project.path)
: [];
return await options.productTools.listSkills(
await selectedSkillIds(project.path, agentId),
enabledPluginIds,
);
},
async listCommands(conversationId) {
const context = await conversationContext(conversationId);
const enabledPluginIds = options.getEnabledPluginIds
? await options.getEnabledPluginIds(context.project.path)
: [];
const piCommands = options.listPiCommands
? normalizePiCommands(await options.listPiCommands(conversationId))
: [];
return await options.productTools.listCommands(context.skillIds, piCommands);
return await options.productTools.listCommands(context.skillIds, piCommands, enabledPluginIds);
},
async getChanges(conversationId) {
await conversationContext(conversationId);