fix(plugins): preserve frozen hosted workers on stale library

This commit is contained in:
2026-09-01 22:26:20 +08:00
parent 697c68974d
commit 40e1912f2f
5 changed files with 217 additions and 3 deletions

View File

@@ -251,6 +251,23 @@ export class EffectivePluginResolver {
constructor(private readonly options: EffectivePluginResolverOptions) {}
async resolve(input: EffectivePluginResolverInput): Promise<EffectivePluginSnapshot> {
return this.resolveSnapshot(input, false);
}
/**
* Revalidate an already frozen worker before invoking one of its tools.
* A stale Library snapshot is still the last verified account projection;
* the hosted adapter resolves a fresh server Admission for the exact frozen
* Release before dispatch. New workers continue to require a current Library.
*/
async resolveForInvocation(input: EffectivePluginResolverInput): Promise<EffectivePluginSnapshot> {
return this.resolveSnapshot(input, true);
}
private async resolveSnapshot(
input: EffectivePluginResolverInput,
allowStaleHostedLibrary: boolean,
): Promise<EffectivePluginSnapshot> {
const definitions = await this.definitionRecords();
const blockedMarketplacePlugins = marketplaceSkillConflicts(definitions);
const assigned = normalizeIds(input.assignedSkillIds);
@@ -342,7 +359,9 @@ export class EffectivePluginResolver {
unavailableReasons.push(unavailable(definition.id, 'account_required', 'Marketplace account is required'));
continue;
}
if (!library || (library.stale && definition.runtimeKind !== 'skill_only')) {
if (!library || (library.stale
&& definition.runtimeKind !== 'skill_only'
&& !allowStaleHostedLibrary)) {
unavailableReasons.push(unavailable(definition.id, 'library_unavailable', 'Marketplace Library is unavailable'));
continue;
}

View File

@@ -560,7 +560,7 @@ export class CodingCapabilityRegistryImpl implements CodingCapabilityRegistryPor
&& candidate.capabilityId === tool.capabilityId
&& candidate.operation === tool.operation
));
const currentSnapshot = await this.options.effectiveResolver.resolve({
const currentSnapshot = await this.options.effectiveResolver.resolveForInvocation({
projectId: input.context.projectId,
projectPath: input.context.projectPath,
assignedSkillIds: input.context.effectiveSnapshot.effectiveSkillIds,