File size: 1,657 Bytes
4c76b0d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import type { PluginManifestRecord } from "../manifest-registry.js";
import type { PluginToolRegistration } from "../registry-types.js";
const HOST_RESTRICTED_CONVERSATION_READ_TOOLS = new Set(["feishu:feishu_chat"]);
function normalizeContractName(value: string): string {
return value.trim().toLowerCase();
}
export function isHostRestrictedConversationReadTool(params: {
pluginId: string;
toolName: string;
}): boolean {
return HOST_RESTRICTED_CONVERSATION_READ_TOOLS.has(
`${normalizeContractName(params.pluginId)}:${normalizeContractName(params.toolName)}`,
);
}
export function registrationIncludesHostRestrictedConversationReadTool(
entry: PluginToolRegistration,
): boolean {
// The gate must reflect the tools this registration actually produces
// (`names`), not the full manifest contract (`declaredNames`). A non-bundled
// Feishu registration that produces feishu_doc still declares feishu_chat in
// its contract; checking declaredNames would collaterally drop feishu_doc.
// Fall back to declaredNames only when the registration has no produced names
// yet, preserving the fail-closed contract for unnamed registrations.
const producibleNames = entry.names.length > 0 ? entry.names : (entry.declaredNames ?? []);
return producibleNames.some((toolName) =>
isHostRestrictedConversationReadTool({ pluginId: entry.pluginId, toolName }),
);
}
export function isBundledConversationReadToolRegistration(params: {
entry: PluginToolRegistration;
manifestPlugin: PluginManifestRecord | undefined;
}): boolean {
return params.entry.origin === "bundled" && params.manifestPlugin?.origin === "bundled";
}
|