mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:30:42 +00:00
fix: scope web provider ownership to plugin index
This commit is contained in:
@@ -66,6 +66,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Providers/Z.AI: map OpenClaw thinking controls to Z.AI's `thinking` payload and add opt-in preserved thinking replay via `params.preserveThinking`, so GLM 5.x can keep prior `reasoning_content` when requested. Fixes #58680. Thanks @xuanmingguo.
|
||||
- Plugins/registry: resolve web provider ownership from the installed plugin index instead of broad manifest scans on secret, tool, and pricing paths. Thanks @shakkernerd.
|
||||
- TTS: strip model-emitted TTS directives from streamed block text before channel
|
||||
delivery, including directives split across adjacent blocks, while preserving
|
||||
the accumulated raw reply for final-mode synthesis. Fixes #38937.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { resolveManifestContractOwnerPluginId } from "../../plugins/manifest-registry.js";
|
||||
import { resolveManifestContractOwnerPluginId } from "../../plugins/plugin-registry.js";
|
||||
import type { RuntimeWebSearchMetadata } from "../../secrets/runtime-web-tools.types.js";
|
||||
import {
|
||||
resolveWebSearchDefinition,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { callGateway } from "../gateway/call.js";
|
||||
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../gateway/protocol/client-info.js";
|
||||
import { validateSecretsResolveResult } from "../gateway/protocol/index.js";
|
||||
import { formatErrorMessage } from "../infra/errors.js";
|
||||
import { resolveManifestContractOwnerPluginId } from "../plugins/manifest-registry.js";
|
||||
import { resolveManifestContractOwnerPluginId } from "../plugins/plugin-registry.js";
|
||||
import {
|
||||
analyzeCommandSecretAssignmentsFromSnapshot,
|
||||
type UnresolvedCommandSecretAssignment,
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import { resolvePluginWebSearchConfig } from "../config/plugin-web-search-config.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { resolveManifestContractPluginIds } from "../plugins/manifest-registry.js";
|
||||
import { resolveManifestContractPluginIds } from "../plugins/plugin-registry.js";
|
||||
import { normalizeProviderModelIdWithPlugin } from "../plugins/provider-runtime.js";
|
||||
import { normalizeOptionalString, resolvePrimaryStringValue } from "../shared/string-coerce.js";
|
||||
import {
|
||||
|
||||
@@ -68,7 +68,7 @@ function resolvePluginSourcePath(sourcePath: string): string {
|
||||
return sourcePath;
|
||||
}
|
||||
|
||||
type PluginManifestContractListKey =
|
||||
export type PluginManifestContractListKey =
|
||||
| "speechProviders"
|
||||
| "externalAuthProviders"
|
||||
| "mediaUnderstandingProviders"
|
||||
|
||||
@@ -24,6 +24,9 @@ import {
|
||||
refreshPluginRegistry,
|
||||
resolveChannelOwners,
|
||||
resolveCliBackendOwners,
|
||||
resolveManifestContractOwnerPluginId,
|
||||
resolveManifestContractPluginIds,
|
||||
resolveManifestContractPluginIdsByCompatibilityRuntimePath,
|
||||
resolvePluginContributionOwners,
|
||||
resolveProviderOwners,
|
||||
resolveSetupProviderOwners,
|
||||
@@ -85,6 +88,10 @@ function createCandidate(rootDir: string): PluginCandidate {
|
||||
commandAliases: [{ name: "demo-command" }],
|
||||
contracts: {
|
||||
tools: ["demo-tool"],
|
||||
webSearchProviders: ["demo-search"],
|
||||
},
|
||||
configContracts: {
|
||||
compatibilityRuntimePaths: ["tools.web.search.demo-search.apiKey"],
|
||||
},
|
||||
}),
|
||||
"utf8",
|
||||
@@ -159,6 +166,23 @@ describe("plugin registry facade", () => {
|
||||
}),
|
||||
).toEqual(["demo"]);
|
||||
expect(resolveSetupProviderOwners({ index, setupProviderId: "demo-setup" })).toEqual(["demo"]);
|
||||
expect(resolveManifestContractPluginIds({ index, contract: "webSearchProviders" })).toEqual([
|
||||
"demo",
|
||||
]);
|
||||
expect(
|
||||
resolveManifestContractOwnerPluginId({
|
||||
index,
|
||||
contract: "webSearchProviders",
|
||||
value: "demo-search",
|
||||
}),
|
||||
).toBe("demo");
|
||||
expect(
|
||||
resolveManifestContractPluginIdsByCompatibilityRuntimePath({
|
||||
index,
|
||||
contract: "webSearchProviders",
|
||||
path: "tools.web.search.demo-search.apiKey",
|
||||
}),
|
||||
).toEqual(["demo"]);
|
||||
});
|
||||
|
||||
it("keeps disabled records inspectable while excluding owners by default", () => {
|
||||
|
||||
@@ -24,7 +24,12 @@ import {
|
||||
type RefreshInstalledPluginIndexParams,
|
||||
} from "./installed-plugin-index.js";
|
||||
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
|
||||
import type { PluginManifestRecord, PluginManifestRegistry } from "./manifest-registry.js";
|
||||
import type {
|
||||
PluginManifestContractListKey,
|
||||
PluginManifestRecord,
|
||||
PluginManifestRegistry,
|
||||
} from "./manifest-registry.js";
|
||||
import type { PluginOrigin } from "./plugin-origin.types.js";
|
||||
|
||||
export type PluginRegistrySnapshot = InstalledPluginIndex;
|
||||
export type PluginRegistryRecord = InstalledPluginIndexRecord;
|
||||
@@ -107,6 +112,25 @@ export type ResolveSetupProviderOwnersParams = PluginRegistryContributionOptions
|
||||
setupProviderId: string;
|
||||
};
|
||||
|
||||
export type ResolveManifestContractPluginIdsParams = LoadPluginRegistryParams & {
|
||||
contract: PluginManifestContractListKey;
|
||||
origin?: PluginOrigin;
|
||||
onlyPluginIds?: readonly string[];
|
||||
};
|
||||
|
||||
export type ResolveManifestContractOwnerPluginIdParams = LoadPluginRegistryParams & {
|
||||
contract: PluginManifestContractListKey;
|
||||
value: string | undefined;
|
||||
origin?: PluginOrigin;
|
||||
};
|
||||
|
||||
export type ResolveManifestContractPluginIdsByCompatibilityRuntimePathParams =
|
||||
LoadPluginRegistryParams & {
|
||||
contract: PluginManifestContractListKey;
|
||||
path: string | undefined;
|
||||
origin?: PluginOrigin;
|
||||
};
|
||||
|
||||
function normalizeContributionId(value: string): string {
|
||||
return value.trim();
|
||||
}
|
||||
@@ -139,6 +163,25 @@ function collectContractKeys(plugin: PluginManifestRecord): readonly string[] {
|
||||
);
|
||||
}
|
||||
|
||||
function listManifestContractValues(
|
||||
plugin: PluginManifestRecord,
|
||||
contract: PluginManifestContractListKey,
|
||||
): readonly string[] {
|
||||
return plugin.contracts?.[contract] ?? [];
|
||||
}
|
||||
|
||||
function loadManifestContractRegistry(
|
||||
params: LoadPluginRegistryParams & {
|
||||
onlyPluginIds?: readonly string[];
|
||||
},
|
||||
): PluginManifestRegistry {
|
||||
return loadPluginManifestRegistryForPluginRegistry({
|
||||
...params,
|
||||
pluginIds: params.onlyPluginIds,
|
||||
includeDisabled: true,
|
||||
});
|
||||
}
|
||||
|
||||
function listManifestContributionIds(
|
||||
plugin: PluginManifestRecord,
|
||||
contribution: PluginRegistryContributionKey,
|
||||
@@ -440,6 +483,53 @@ export function resolveSetupProviderOwners(
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveManifestContractPluginIds(
|
||||
params: ResolveManifestContractPluginIdsParams,
|
||||
): string[] {
|
||||
return loadManifestContractRegistry(params)
|
||||
.plugins.filter(
|
||||
(plugin) =>
|
||||
(!params.origin || plugin.origin === params.origin) &&
|
||||
listManifestContractValues(plugin, params.contract).length > 0,
|
||||
)
|
||||
.map((plugin) => plugin.id)
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
export function resolveManifestContractPluginIdsByCompatibilityRuntimePath(
|
||||
params: ResolveManifestContractPluginIdsByCompatibilityRuntimePathParams,
|
||||
): string[] {
|
||||
const normalizedPath = params.path?.trim();
|
||||
if (!normalizedPath) {
|
||||
return [];
|
||||
}
|
||||
return loadManifestContractRegistry(params)
|
||||
.plugins.filter(
|
||||
(plugin) =>
|
||||
(!params.origin || plugin.origin === params.origin) &&
|
||||
listManifestContractValues(plugin, params.contract).length > 0 &&
|
||||
(plugin.configContracts?.compatibilityRuntimePaths ?? []).includes(normalizedPath),
|
||||
)
|
||||
.map((plugin) => plugin.id)
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
export function resolveManifestContractOwnerPluginId(
|
||||
params: ResolveManifestContractOwnerPluginIdParams,
|
||||
): string | undefined {
|
||||
const normalizedValue = normalizeContributionId(params.value ?? "").toLowerCase();
|
||||
if (!normalizedValue) {
|
||||
return undefined;
|
||||
}
|
||||
return loadManifestContractRegistry(params).plugins.find(
|
||||
(plugin) =>
|
||||
(!params.origin || plugin.origin === params.origin) &&
|
||||
listManifestContractValues(plugin, params.contract).some(
|
||||
(candidate) => normalizeContributionId(candidate).toLowerCase() === normalizedValue,
|
||||
),
|
||||
)?.id;
|
||||
}
|
||||
|
||||
export function inspectPluginRegistry(
|
||||
params: LoadInstalledPluginIndexParams & InstalledPluginIndexStoreOptions = {},
|
||||
): Promise<PluginRegistryInspection> {
|
||||
|
||||
@@ -2,4 +2,4 @@ export {
|
||||
resolveManifestContractOwnerPluginId,
|
||||
resolveManifestContractPluginIds,
|
||||
resolveManifestContractPluginIdsByCompatibilityRuntimePath,
|
||||
} from "../plugins/manifest-registry.js";
|
||||
} from "../plugins/plugin-registry.js";
|
||||
|
||||
Reference in New Issue
Block a user