fix(coding): remediate ML-07 plugin authority
This commit is contained in:
@@ -42,6 +42,10 @@ import {
|
||||
} from '../coding-plugins/registry';
|
||||
import { createDataServicePluginAdapter } from '../coding-plugins/adapters/data-service';
|
||||
import { PluginPolicyClient } from '../services/plugin-policy-client';
|
||||
import {
|
||||
loadBundledCodingPluginDefinitionsSync,
|
||||
resolveBundledCodingPluginRootPaths,
|
||||
} from '../coding-plugins/manifest';
|
||||
import {
|
||||
createCodingProjectPluginService,
|
||||
type CodingProjectPluginService,
|
||||
@@ -88,6 +92,20 @@ export function createCodingComposition(
|
||||
options: CreateCodingCompositionOptions,
|
||||
): CodingProductComposition {
|
||||
const getLocalProxyCredential = options.getLocalProxyCredential;
|
||||
const bundledPluginsDir = path.join(
|
||||
path.dirname(path.resolve(options.paths.bundledSkillsDir)),
|
||||
'coding-plugins',
|
||||
);
|
||||
const pluginDefinitions = loadBundledCodingPluginDefinitionsSync(bundledPluginsDir);
|
||||
const pluginRoots = resolveBundledCodingPluginRootPaths(bundledPluginsDir);
|
||||
const pluginSkillSources = pluginDefinitions.flatMap((definition, index) => (
|
||||
definition.skills.map((skill) => ({
|
||||
id: skill.id,
|
||||
pluginId: definition.id,
|
||||
directory: path.join(pluginRoots[index] as string, path.dirname(skill.entryPath)),
|
||||
entryPath: path.basename(skill.entryPath),
|
||||
}))
|
||||
));
|
||||
const projectStore = options.projectStore ?? createCodingProjectStore(options.storage);
|
||||
const attachments = new CodingAttachmentStore(
|
||||
path.join(options.paths.userDataDir, 'coding-runtime', 'attachments'),
|
||||
@@ -96,6 +114,7 @@ export function createCodingComposition(
|
||||
browser: options.browser,
|
||||
attachments,
|
||||
bundledSkillsDir: options.paths.bundledSkillsDir,
|
||||
pluginSkillSources,
|
||||
});
|
||||
const extensionHost = new PiManagedExtensionHost();
|
||||
extensionHost.configureProductTools(productTools);
|
||||
@@ -156,6 +175,7 @@ export function createCodingComposition(
|
||||
const dataServiceAdapter = createDataServicePluginAdapter(dataService);
|
||||
const policyClient = new PluginPolicyClient();
|
||||
const projectPlugins = createProjectPluginService({
|
||||
knownPluginIds: pluginDefinitions.map(({ id }) => id),
|
||||
onManagedInputsChanged: async ({ projectPath }) => {
|
||||
runtime?.markResourcesStale();
|
||||
const conversations = await conversationStoreForProject(projectPath).read()
|
||||
@@ -172,6 +192,7 @@ export function createCodingComposition(
|
||||
policyClient,
|
||||
projectPlugins,
|
||||
adapters: [dataServiceAdapter],
|
||||
definitions: pluginDefinitions,
|
||||
getDurableProjectId: async (projectPath, localProjectId) => {
|
||||
const active = await projects.requireActiveRealProjectWithIdentity(projectPath);
|
||||
if (active.project.id !== localProjectId) {
|
||||
@@ -195,6 +216,7 @@ export function createCodingComposition(
|
||||
projectPlugins,
|
||||
policyClient,
|
||||
adapters: [dataServiceAdapter],
|
||||
definitions: pluginDefinitions,
|
||||
});
|
||||
const workerPool = new PiWorkerPool({
|
||||
processBudget,
|
||||
@@ -269,6 +291,7 @@ export function createCodingComposition(
|
||||
const host = createCodingProductHost({
|
||||
projects,
|
||||
productTools,
|
||||
getEnabledPluginIds: (projectPath) => projectPlugins.getEnabledPluginIds(projectPath),
|
||||
listPiCommands: (conversationId) => conversations.listLiveCommands(conversationId),
|
||||
});
|
||||
previewDataSession = createPreviewDataSessionManager({ projects });
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user