mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 12:23:59 +00:00
* fix(agent): honor recipient session routing Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com> Co-authored-by: pingfanfan <pingfan.work@gmail.com> * fix(agent): preserve canonical recipient routes * fix(agent): require exact recipient routes Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com> Co-authored-by: pingfanfan <pingfan.work@gmail.com> * fix(agent): harden recipient route resolution * fix(imessage): require canonical recipient handles * fix(agent): refine provider recipient exactness * fix(agent): bound direct alias session routing * fix(signal): preserve direct alias route type * fix(agent): honor binding-scoped recipient sessions * fix(routing): honor configured main session aliases * fix(clickclack): align account-owned session routes * fix(twitch): preserve canonical recipient sessions * fix(routing): isolate stable outbound identities * chore: defer recipient session changelog * fix(sms): use dedicated channel SDK facade --------- Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com> Co-authored-by: pingfanfan <pingfan.work@gmail.com>
164 lines
5.3 KiB
TypeScript
164 lines
5.3 KiB
TypeScript
/** Resolves agent runtime config, including SecretRef materialization for agent command use. */
|
|
import {
|
|
getAgentRuntimeCommandSecretTargetIds,
|
|
getScopedChannelsCommandSecretTargets,
|
|
} from "../cli/command-secret-targets.js";
|
|
import { getRuntimeConfig, readConfigFileSnapshotForWrite } from "../config/io.js";
|
|
import { setRuntimeConfigSnapshot } from "../config/runtime-snapshot.js";
|
|
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
|
import { isSecretRef } from "../config/types.secrets.js";
|
|
import type { RuntimeEnv } from "../runtime.js";
|
|
import { discoverConfigSecretTargetsByIds } from "../secrets/target-registry.js";
|
|
|
|
/** Loads runtime/source config and resolves command SecretRefs when the agent path needs them. */
|
|
export async function resolveAgentRuntimeConfig(
|
|
runtime: RuntimeEnv,
|
|
params?: {
|
|
runtimeTargetsChannelSecrets?: boolean;
|
|
runtimeChannelSecretScope?: { channel: string; accountId?: string };
|
|
},
|
|
): Promise<{
|
|
loadedRaw: OpenClawConfig;
|
|
sourceConfig: OpenClawConfig;
|
|
cfg: OpenClawConfig;
|
|
}> {
|
|
const loadedRaw = getRuntimeConfig();
|
|
const includeChannelTargets = params?.runtimeTargetsChannelSecrets === true;
|
|
const channelSecretScope = params?.runtimeChannelSecretScope;
|
|
const hasRuntimeSecretRefs = hasAgentRuntimeSecretRefs({
|
|
config: loadedRaw,
|
|
includeChannelTargets,
|
|
channel: channelSecretScope?.channel,
|
|
});
|
|
const sourceConfig = await (async () => {
|
|
try {
|
|
const { snapshot } = await readConfigFileSnapshotForWrite();
|
|
if (snapshot.valid) {
|
|
return snapshot.resolved;
|
|
}
|
|
} catch {
|
|
// Fall back to runtime-loaded config when source snapshot is unavailable.
|
|
}
|
|
return loadedRaw;
|
|
})();
|
|
const cfg = hasRuntimeSecretRefs
|
|
? await (async () => {
|
|
const runtimeSecretTargets = resolveAgentRuntimeSecretTargets({
|
|
config: loadedRaw,
|
|
includeChannelTargets,
|
|
channelSecretScope,
|
|
});
|
|
return (
|
|
await (
|
|
await import("../cli/command-config-resolution.runtime.js")
|
|
).resolveCommandConfigWithSecrets({
|
|
config: loadedRaw,
|
|
commandName: "agent",
|
|
targetIds: runtimeSecretTargets.targetIds,
|
|
...(runtimeSecretTargets.allowedPaths
|
|
? { allowedPaths: runtimeSecretTargets.allowedPaths }
|
|
: {}),
|
|
runtime,
|
|
})
|
|
).resolvedConfig;
|
|
})()
|
|
: loadedRaw;
|
|
setRuntimeConfigSnapshot(cfg, sourceConfig);
|
|
return { loadedRaw, sourceConfig, cfg };
|
|
}
|
|
|
|
function hasNestedSecretRef(value: unknown): boolean {
|
|
if (isSecretRef(value)) {
|
|
return true;
|
|
}
|
|
if (Array.isArray(value)) {
|
|
return value.some((entry) => hasNestedSecretRef(entry));
|
|
}
|
|
if (!value || typeof value !== "object") {
|
|
return false;
|
|
}
|
|
return Object.values(value).some((entry) => hasNestedSecretRef(entry));
|
|
}
|
|
|
|
function hasAgentRuntimeSecretRefs(params: {
|
|
config: OpenClawConfig;
|
|
includeChannelTargets: boolean;
|
|
channel?: string;
|
|
}): boolean {
|
|
const { config } = params;
|
|
if (hasNestedSecretRef(config.models?.providers)) {
|
|
return true;
|
|
}
|
|
if (hasNestedSecretRef(config.agents?.defaults?.memorySearch?.remote?.apiKey)) {
|
|
return true;
|
|
}
|
|
if (
|
|
Array.isArray(config.agents?.list) &&
|
|
config.agents.list.some((agent) => hasNestedSecretRef(agent?.memorySearch?.remote?.apiKey))
|
|
) {
|
|
return true;
|
|
}
|
|
if (hasNestedSecretRef(config.messages?.tts?.providers)) {
|
|
return true;
|
|
}
|
|
if (hasNestedSecretRef(config.skills?.entries)) {
|
|
return true;
|
|
}
|
|
if (hasNestedSecretRef(config.tools?.web?.search)) {
|
|
return true;
|
|
}
|
|
if (
|
|
config.plugins?.entries &&
|
|
Object.values(config.plugins.entries).some((entry) =>
|
|
hasNestedSecretRef({
|
|
webSearch: entry?.config?.webSearch,
|
|
webFetch: entry?.config?.webFetch,
|
|
}),
|
|
)
|
|
) {
|
|
return true;
|
|
}
|
|
if (params.includeChannelTargets) {
|
|
return hasNestedSecretRef(config.channels);
|
|
}
|
|
if (!params.channel) {
|
|
return false;
|
|
}
|
|
return hasNestedSecretRef(
|
|
(config.channels as Record<string, unknown> | undefined)?.[params.channel],
|
|
);
|
|
}
|
|
|
|
function resolveAgentRuntimeSecretTargets(params: {
|
|
config: OpenClawConfig;
|
|
includeChannelTargets: boolean;
|
|
channelSecretScope?: { channel: string; accountId?: string };
|
|
}): { targetIds: Set<string>; allowedPaths?: Set<string> } {
|
|
const baseTargetIds = getAgentRuntimeCommandSecretTargetIds({
|
|
includeChannelTargets: params.includeChannelTargets,
|
|
});
|
|
if (params.includeChannelTargets || !params.channelSecretScope) {
|
|
return { targetIds: baseTargetIds };
|
|
}
|
|
const channelTargets = getScopedChannelsCommandSecretTargets({
|
|
config: params.config,
|
|
channel: params.channelSecretScope.channel,
|
|
accountId: params.channelSecretScope.accountId,
|
|
defaultAccountWhenMissing: true,
|
|
});
|
|
const targetIds = new Set(baseTargetIds);
|
|
for (const targetId of channelTargets.targetIds) {
|
|
targetIds.add(targetId);
|
|
}
|
|
if (!channelTargets.allowedPaths) {
|
|
return { targetIds };
|
|
}
|
|
|
|
// Account scoping must not exclude the agent's model/tool secrets from the same resolution.
|
|
const allowedPaths = new Set(channelTargets.allowedPaths);
|
|
for (const target of discoverConfigSecretTargetsByIds(params.config, baseTargetIds)) {
|
|
allowedPaths.add(target.path);
|
|
}
|
|
return { targetIds, allowedPaths };
|
|
}
|