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

@@ -14,20 +14,11 @@ import type {
ProductPiCommandInput,
} from '../../../shared/coding-product-tools';
import {
DATA_SERVICE_PI_TOOL_NAMES,
type DataServicePiToolName,
} from '../../../shared/data-service';
type ProductCodingPluginSkillSource,
} from '../../coding-projects/skill-registry';
import {
DATA_SERVICE_PLUGIN_DEFINITION,
} from '../../../shared/coding-plugins';
import { createDataServicePluginAdapter } from '../../coding-plugins/adapters/data-service';
import {
buildCapabilityToolResult,
type AdapterInvocationResult,
type CodingCapabilityRegistry,
type TrustedCodingCapabilityContext,
} from '../../coding-plugins/registry';
import type { DataServiceOperations } from '../../services/data-service-client';
import type { KnownToolDetails, RuntimeContextDetailsV1 } from '../contracts';
import { PiAgentBrowserTool } from './extensions/agent-browser';
import { reportChangedFiles } from './extensions/changed-file';
@@ -40,10 +31,7 @@ export type PiProductToolName =
| 'game_asset_review'
| 'task_state'
| 'changed_file'
| 'runtime_context'
| DataServicePiToolName;
export { DATA_SERVICE_PI_TOOL_NAMES };
| 'runtime_context';
const PI_PRODUCT_TOOL_NAMES = new Set<string>([
'agent_browser',
@@ -52,7 +40,6 @@ const PI_PRODUCT_TOOL_NAMES = new Set<string>([
'task_state',
'changed_file',
'runtime_context',
...DATA_SERVICE_PI_TOOL_NAMES,
]);
export function isPiProductToolName(value: unknown): value is PiProductToolName {
@@ -78,7 +65,7 @@ export interface PiProductToolsOptions {
attachments: CodingAttachmentStore;
bundledSkillsDir: string;
changeTracker?: ConversationChangeTracker;
dataService?: DataServiceOperations;
pluginSkillSources?: readonly ProductCodingPluginSkillSource[];
capabilityRegistry?: CodingCapabilityRegistry;
}
@@ -86,20 +73,12 @@ export class PiProductTools {
readonly changeTracker: ConversationChangeTracker;
private readonly browser: PiAgentBrowserTool;
private readonly gameAssets = new PiGameAssetTools();
private dataServiceAdapter: ReturnType<typeof createDataServicePluginAdapter> | undefined;
private capabilityRegistry: CodingCapabilityRegistry | undefined;
constructor(private readonly options: PiProductToolsOptions) {
this.changeTracker = options.changeTracker ?? new ConversationChangeTracker();
this.browser = new PiAgentBrowserTool(options.browser, options.attachments);
this.capabilityRegistry = options.capabilityRegistry;
this.dataServiceAdapter = options.dataService
? createDataServicePluginAdapter(options.dataService)
: undefined;
}
configureDataService(dataService: DataServiceOperations): void {
this.dataServiceAdapter = createDataServicePluginAdapter(dataService);
}
configureCapabilityRegistry(registry: CodingCapabilityRegistry): void {
@@ -118,15 +97,27 @@ export class PiProductTools {
return this.changeTracker.getSnapshot(conversationId);
}
listSkills(skillIds: readonly string[]): Promise<ProductCodingSkill[]> {
return listProductCodingSkills(this.options.bundledSkillsDir, skillIds);
listSkills(
skillIds: readonly string[],
availablePluginSkillIds: readonly string[] = [],
): Promise<ProductCodingSkill[]> {
const available = new Set(availablePluginSkillIds);
const sources = (this.options.pluginSkillSources ?? []).map((source) => ({
...source,
available: available.has(source.pluginId ?? source.id) || available.has(source.id),
}));
return listProductCodingSkills(this.options.bundledSkillsDir, skillIds, sources);
}
async listCommands(
skillIds: readonly string[],
piCommands: readonly ProductPiCommandInput[] = [],
availablePluginSkillIds: readonly string[] = [],
): Promise<ProductCodingCommand[]> {
return buildProductCodingCommandCatalog(await this.listSkills(skillIds), piCommands);
return buildProductCodingCommandCatalog(
await this.listSkills(skillIds, availablePluginSkillIds),
piCommands,
);
}
async markBash(conversationId: string, runId: string): Promise<void> {
@@ -164,32 +155,8 @@ export class PiProductTools {
value: input,
});
}
if (DATA_SERVICE_PI_TOOL_NAMES.includes(toolName as DataServicePiToolName)) {
const adapter = this.dataServiceAdapter;
const definition = DATA_SERVICE_PLUGIN_DEFINITION.tools.find(({ name }) => name === toolName);
if (!adapter || !definition) throw new Error('Data Service tools are unavailable');
const trustedContext: TrustedCodingCapabilityContext = {
conversationId: context.conversationId,
runId: context.runId,
resourceId: context.resourceId,
requestId: `pi:${context.runId}:${context.resourceId}`,
localProjectId: context.projectId,
projectPath: context.projectPath,
durableProjectId: context.projectId,
workerRole: 'parent',
effectiveSkillIds: [...context.skillIds],
};
const result: AdapterInvocationResult = await adapter.invoke(trustedContext, definition, input);
return buildCapabilityToolResult(
DATA_SERVICE_PLUGIN_DEFINITION,
definition,
context,
result,
{ mode: 'included', status: 'included' },
);
}
if (toolName !== 'runtime_context') throw new Error('Product tool is unavailable');
const skills = await this.listSkills(context.skillIds);
const skills = await this.listSkills(context.skillIds, context.skillIds);
const details: RuntimeContextDetailsV1 = {
schema: 'runtime-context.v1',
skills,