mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 09:41:11 +00:00
fix(cycles): split embedded runner and setup leaf types
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import type { ReasoningLevel, ThinkLevel } from "../../auto-reply/thinking.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import type { enqueueCommand } from "../../process/command-queue.js";
|
||||
import type { ExecElevatedDefaults } from "../bash-tools.js";
|
||||
import type { CommandQueueEnqueueFn } from "../../process/command-queue.types.js";
|
||||
import type { ExecElevatedDefaults } from "../bash-tools.exec-types.js";
|
||||
import type { SkillSnapshot } from "../skills.js";
|
||||
|
||||
export type CompactEmbeddedPiSessionParams = {
|
||||
@@ -50,7 +50,7 @@ export type CompactEmbeddedPiSessionParams = {
|
||||
attempt?: number;
|
||||
maxAttempts?: number;
|
||||
lane?: string;
|
||||
enqueue?: typeof enqueueCommand;
|
||||
enqueue?: CommandQueueEnqueueFn;
|
||||
extraSystemPrompt?: string;
|
||||
ownerNumbers?: string[];
|
||||
abortSignal?: AbortSignal;
|
||||
|
||||
@@ -4,9 +4,9 @@ import type { ReasoningLevel, ThinkLevel, VerboseLevel } from "../../../auto-rep
|
||||
import type { ReplyPayload } from "../../../auto-reply/types.js";
|
||||
import type { OpenClawConfig } from "../../../config/types.openclaw.js";
|
||||
import type { PromptImageOrderEntry } from "../../../media/prompt-image-order.js";
|
||||
import type { enqueueCommand } from "../../../process/command-queue.js";
|
||||
import type { CommandQueueEnqueueFn } from "../../../process/command-queue.types.js";
|
||||
import type { InputProvenance } from "../../../sessions/input-provenance.js";
|
||||
import type { ExecElevatedDefaults, ExecToolDefaults } from "../../bash-tools.js";
|
||||
import type { ExecElevatedDefaults, ExecToolDefaults } from "../../bash-tools.exec-types.js";
|
||||
import type { AgentStreamParams, ClientToolDefinition } from "../../command/shared-types.js";
|
||||
import type { AgentInternalEvent } from "../../internal-events.js";
|
||||
import type { BlockReplyPayload } from "../../pi-embedded-payloads.js";
|
||||
@@ -113,7 +113,7 @@ export type RunEmbeddedPiAgentParams = {
|
||||
onToolResult?: (payload: ReplyPayload) => void | Promise<void>;
|
||||
onAgentEvent?: (evt: { stream: string; data: Record<string, unknown> }) => void;
|
||||
lane?: string;
|
||||
enqueue?: typeof enqueueCommand;
|
||||
enqueue?: CommandQueueEnqueueFn;
|
||||
extraSystemPrompt?: string;
|
||||
internalEvents?: AgentInternalEvent[];
|
||||
inputProvenance?: InputProvenance;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { z, type ZodType } from "zod";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import { getBundledChannelPlugin } from "./bundled.js";
|
||||
import { getChannelPlugin } from "./registry.js";
|
||||
import {
|
||||
resolveSingleAccountKeysToMove,
|
||||
resolveSingleAccountPromotionTarget,
|
||||
} from "./setup-promotion-helpers.js";
|
||||
import type { ChannelSetupAdapter } from "./types.adapters.js";
|
||||
import type { ChannelSetupInput } from "./types.core.js";
|
||||
|
||||
@@ -377,136 +378,6 @@ type ChannelSectionRecord = Record<string, unknown> & {
|
||||
accounts?: Record<string, Record<string, unknown>>;
|
||||
};
|
||||
|
||||
const COMMON_SINGLE_ACCOUNT_KEYS_TO_MOVE = new Set([
|
||||
"name",
|
||||
"token",
|
||||
"tokenFile",
|
||||
"botToken",
|
||||
"appToken",
|
||||
"account",
|
||||
"signalNumber",
|
||||
"authDir",
|
||||
"cliPath",
|
||||
"dbPath",
|
||||
"httpUrl",
|
||||
"httpHost",
|
||||
"httpPort",
|
||||
"webhookPath",
|
||||
"webhookUrl",
|
||||
"webhookSecret",
|
||||
"service",
|
||||
"region",
|
||||
"homeserver",
|
||||
"userId",
|
||||
"accessToken",
|
||||
"password",
|
||||
"deviceName",
|
||||
"url",
|
||||
"code",
|
||||
"dmPolicy",
|
||||
"allowFrom",
|
||||
"groupPolicy",
|
||||
"groupAllowFrom",
|
||||
"defaultTo",
|
||||
]);
|
||||
|
||||
const BUNDLED_SINGLE_ACCOUNT_PROMOTION_FALLBACKS: Record<string, readonly string[]> = {
|
||||
// Some setup/migration paths run before the channel setup surface has been loaded.
|
||||
telegram: ["streaming"],
|
||||
};
|
||||
|
||||
const BUNDLED_NAMED_ACCOUNT_PROMOTION_FALLBACKS: Record<string, readonly string[]> = {
|
||||
// Keep top-level Telegram policy fallback intact when only auth needs seeding.
|
||||
telegram: ["botToken", "tokenFile"],
|
||||
};
|
||||
|
||||
type ChannelSetupPromotionSurface = {
|
||||
singleAccountKeysToMove?: readonly string[];
|
||||
namedAccountPromotionKeys?: readonly string[];
|
||||
resolveSingleAccountPromotionTarget?: (params: {
|
||||
channel: ChannelSectionBase;
|
||||
}) => string | undefined;
|
||||
};
|
||||
|
||||
function getChannelSetupPromotionSurface(channelKey: string): ChannelSetupPromotionSurface | null {
|
||||
const setup = getChannelPlugin(channelKey)?.setup ?? getBundledChannelPlugin(channelKey)?.setup;
|
||||
if (!setup || typeof setup !== "object") {
|
||||
return null;
|
||||
}
|
||||
return setup as ChannelSetupPromotionSurface;
|
||||
}
|
||||
|
||||
export function shouldMoveSingleAccountChannelKey(params: {
|
||||
channelKey: string;
|
||||
key: string;
|
||||
}): boolean {
|
||||
if (COMMON_SINGLE_ACCOUNT_KEYS_TO_MOVE.has(params.key)) {
|
||||
return true;
|
||||
}
|
||||
const contractKeys = getChannelSetupPromotionSurface(params.channelKey)?.singleAccountKeysToMove;
|
||||
if (contractKeys?.includes(params.key)) {
|
||||
return true;
|
||||
}
|
||||
const fallbackKeys = BUNDLED_SINGLE_ACCOUNT_PROMOTION_FALLBACKS[params.channelKey];
|
||||
if (fallbackKeys?.includes(params.key)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function resolveSingleAccountKeysToMove(params: {
|
||||
channelKey: string;
|
||||
channel: Record<string, unknown>;
|
||||
}): string[] {
|
||||
const hasNamedAccounts =
|
||||
Object.keys((params.channel.accounts as Record<string, unknown>) ?? {}).filter(Boolean).length >
|
||||
0;
|
||||
const namedAccountPromotionKeys =
|
||||
getChannelSetupPromotionSurface(params.channelKey)?.namedAccountPromotionKeys ??
|
||||
BUNDLED_NAMED_ACCOUNT_PROMOTION_FALLBACKS[params.channelKey];
|
||||
return Object.entries(params.channel)
|
||||
.filter(([key, value]) => {
|
||||
if (key === "accounts" || key === "enabled" || value === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (!shouldMoveSingleAccountChannelKey({ channelKey: params.channelKey, key })) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
hasNamedAccounts &&
|
||||
namedAccountPromotionKeys &&
|
||||
!namedAccountPromotionKeys.includes(key)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(([key]) => key);
|
||||
}
|
||||
|
||||
export function resolveSingleAccountPromotionTarget(params: {
|
||||
channelKey: string;
|
||||
channel: ChannelSectionBase;
|
||||
}): string {
|
||||
const accounts = params.channel.accounts ?? {};
|
||||
const resolveExistingAccountId = (targetAccountId: string): string => {
|
||||
const normalizedTargetAccountId = normalizeAccountId(targetAccountId);
|
||||
const matchedAccountId = Object.keys(accounts).find(
|
||||
(accountId) => normalizeAccountId(accountId) === normalizedTargetAccountId,
|
||||
);
|
||||
return matchedAccountId ?? normalizedTargetAccountId;
|
||||
};
|
||||
const surface = getChannelSetupPromotionSurface(params.channelKey);
|
||||
const resolved = surface?.resolveSingleAccountPromotionTarget?.({
|
||||
channel: params.channel,
|
||||
});
|
||||
const normalizedResolved = normalizeOptionalString(resolved);
|
||||
if (normalizedResolved) {
|
||||
return resolveExistingAccountId(normalizedResolved);
|
||||
}
|
||||
return resolveExistingAccountId(DEFAULT_ACCOUNT_ID);
|
||||
}
|
||||
|
||||
function cloneIfObject<T>(value: T): T {
|
||||
if (value && typeof value === "object") {
|
||||
return structuredClone(value);
|
||||
|
||||
139
src/channels/plugins/setup-promotion-helpers.ts
Normal file
139
src/channels/plugins/setup-promotion-helpers.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import { getBundledChannelPlugin } from "./bundled.js";
|
||||
import { getChannelPlugin } from "./registry.js";
|
||||
|
||||
type ChannelSectionBase = {
|
||||
defaultAccount?: string;
|
||||
accounts?: Record<string, Record<string, unknown>>;
|
||||
};
|
||||
|
||||
const COMMON_SINGLE_ACCOUNT_KEYS_TO_MOVE = new Set([
|
||||
"name",
|
||||
"token",
|
||||
"tokenFile",
|
||||
"botToken",
|
||||
"appToken",
|
||||
"account",
|
||||
"signalNumber",
|
||||
"authDir",
|
||||
"cliPath",
|
||||
"dbPath",
|
||||
"httpUrl",
|
||||
"httpHost",
|
||||
"httpPort",
|
||||
"webhookPath",
|
||||
"webhookUrl",
|
||||
"webhookSecret",
|
||||
"service",
|
||||
"region",
|
||||
"homeserver",
|
||||
"userId",
|
||||
"accessToken",
|
||||
"password",
|
||||
"deviceName",
|
||||
"url",
|
||||
"code",
|
||||
"dmPolicy",
|
||||
"allowFrom",
|
||||
"groupPolicy",
|
||||
"groupAllowFrom",
|
||||
"defaultTo",
|
||||
]);
|
||||
|
||||
const BUNDLED_SINGLE_ACCOUNT_PROMOTION_FALLBACKS: Record<string, readonly string[]> = {
|
||||
// Some setup/migration paths run before the channel setup surface has been loaded.
|
||||
telegram: ["streaming"],
|
||||
};
|
||||
|
||||
const BUNDLED_NAMED_ACCOUNT_PROMOTION_FALLBACKS: Record<string, readonly string[]> = {
|
||||
// Keep top-level Telegram policy fallback intact when only auth needs seeding.
|
||||
telegram: ["botToken", "tokenFile"],
|
||||
};
|
||||
|
||||
type ChannelSetupPromotionSurface = {
|
||||
singleAccountKeysToMove?: readonly string[];
|
||||
namedAccountPromotionKeys?: readonly string[];
|
||||
resolveSingleAccountPromotionTarget?: (params: {
|
||||
channel: ChannelSectionBase;
|
||||
}) => string | undefined;
|
||||
};
|
||||
|
||||
function getChannelSetupPromotionSurface(channelKey: string): ChannelSetupPromotionSurface | null {
|
||||
const setup = getChannelPlugin(channelKey)?.setup ?? getBundledChannelPlugin(channelKey)?.setup;
|
||||
if (!setup || typeof setup !== "object") {
|
||||
return null;
|
||||
}
|
||||
return setup as ChannelSetupPromotionSurface;
|
||||
}
|
||||
|
||||
export function shouldMoveSingleAccountChannelKey(params: {
|
||||
channelKey: string;
|
||||
key: string;
|
||||
}): boolean {
|
||||
if (COMMON_SINGLE_ACCOUNT_KEYS_TO_MOVE.has(params.key)) {
|
||||
return true;
|
||||
}
|
||||
const contractKeys = getChannelSetupPromotionSurface(params.channelKey)?.singleAccountKeysToMove;
|
||||
if (contractKeys?.includes(params.key)) {
|
||||
return true;
|
||||
}
|
||||
const fallbackKeys = BUNDLED_SINGLE_ACCOUNT_PROMOTION_FALLBACKS[params.channelKey];
|
||||
if (fallbackKeys?.includes(params.key)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function resolveSingleAccountKeysToMove(params: {
|
||||
channelKey: string;
|
||||
channel: Record<string, unknown>;
|
||||
}): string[] {
|
||||
const hasNamedAccounts =
|
||||
Object.keys((params.channel.accounts as Record<string, unknown>) ?? {}).filter(Boolean).length >
|
||||
0;
|
||||
const namedAccountPromotionKeys =
|
||||
getChannelSetupPromotionSurface(params.channelKey)?.namedAccountPromotionKeys ??
|
||||
BUNDLED_NAMED_ACCOUNT_PROMOTION_FALLBACKS[params.channelKey];
|
||||
return Object.entries(params.channel)
|
||||
.filter(([key, value]) => {
|
||||
if (key === "accounts" || key === "enabled" || value === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (!shouldMoveSingleAccountChannelKey({ channelKey: params.channelKey, key })) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
hasNamedAccounts &&
|
||||
namedAccountPromotionKeys &&
|
||||
!namedAccountPromotionKeys.includes(key)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(([key]) => key);
|
||||
}
|
||||
|
||||
export function resolveSingleAccountPromotionTarget(params: {
|
||||
channelKey: string;
|
||||
channel: ChannelSectionBase;
|
||||
}): string {
|
||||
const accounts = params.channel.accounts ?? {};
|
||||
const resolveExistingAccountId = (targetAccountId: string): string => {
|
||||
const normalizedTargetAccountId = normalizeAccountId(targetAccountId);
|
||||
const matchedAccountId = Object.keys(accounts).find(
|
||||
(accountId) => normalizeAccountId(accountId) === normalizedTargetAccountId,
|
||||
);
|
||||
return matchedAccountId ?? normalizedTargetAccountId;
|
||||
};
|
||||
const surface = getChannelSetupPromotionSurface(params.channelKey);
|
||||
const resolved = surface?.resolveSingleAccountPromotionTarget?.({
|
||||
channel: params.channel,
|
||||
});
|
||||
const normalizedResolved = normalizeOptionalString(resolved);
|
||||
if (normalizedResolved) {
|
||||
return resolveExistingAccountId(normalizedResolved);
|
||||
}
|
||||
return resolveExistingAccountId(DEFAULT_ACCOUNT_ID);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { normalizeProviderId } from "../../../agents/model-selection-normalize.js";
|
||||
import { resolveSingleAccountKeysToMove } from "../../../channels/plugins/setup-helpers.js";
|
||||
import { resolveSingleAccountKeysToMove } from "../../../channels/plugins/setup-promotion-helpers.js";
|
||||
import { resolveNormalizedProviderModelMaxTokens } from "../../../config/defaults.js";
|
||||
import type { OpenClawConfig } from "../../../config/types.openclaw.js";
|
||||
import { DEFAULT_GOOGLE_API_BASE_URL } from "../../../infra/google-api-base-url.js";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getActivePluginRegistry } from "../plugins/runtime.js";
|
||||
import { getPluginRegistryState } from "../plugins/runtime-state.js";
|
||||
import { resolveReservedGatewayMethodScope } from "../shared/gateway-method-policy.js";
|
||||
import {
|
||||
ADMIN_SCOPE,
|
||||
@@ -185,7 +185,7 @@ function resolveScopedMethod(method: string): OperatorScope | undefined {
|
||||
if (reservedScope) {
|
||||
return reservedScope;
|
||||
}
|
||||
const pluginScope = getActivePluginRegistry()?.gatewayMethodScopes?.[method];
|
||||
const pluginScope = getPluginRegistryState()?.activeRegistry?.gatewayMethodScopes?.[method];
|
||||
if (pluginScope) {
|
||||
return pluginScope;
|
||||
}
|
||||
|
||||
7
src/process/command-queue.types.ts
Normal file
7
src/process/command-queue.types.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export type CommandQueueEnqueueFn = <T>(
|
||||
task: () => Promise<T>,
|
||||
opts?: {
|
||||
warnAfterMs?: number;
|
||||
onWait?: (waitMs: number, queuedAhead: number) => void;
|
||||
},
|
||||
) => Promise<T>;
|
||||
Reference in New Issue
Block a user