feat: Zoho Cliq integration and config updates
This commit is contained in:
parent
6f37fe0acc
commit
b35ceca265
@ -26,6 +26,7 @@ Text is supported everywhere; media and reactions vary by channel.
|
||||
- [Tlon](/channels/tlon) — Urbit-based messenger (plugin, installed separately).
|
||||
- [Zalo](/channels/zalo) — Zalo Bot API; Vietnam's popular messenger (plugin, installed separately).
|
||||
- [Zalo Personal](/channels/zalouser) — Zalo personal account via QR login (plugin, installed separately).
|
||||
- [Zoho Cliq](/channels/zoho-cliq) — Zoho Cliq team messaging via OAuth API (plugin, installed separately).
|
||||
- [WebChat](/web/webchat) — Gateway WebChat UI over WebSocket.
|
||||
|
||||
## Notes
|
||||
|
||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@ -434,6 +434,15 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../..
|
||||
|
||||
extensions/zoho-cliq:
|
||||
devDependencies:
|
||||
clawdbot:
|
||||
specifier: workspace:*
|
||||
version: link:../..
|
||||
typescript:
|
||||
specifier: ^5.0.0
|
||||
version: 5.9.3
|
||||
|
||||
ui:
|
||||
dependencies:
|
||||
'@noble/ed25519':
|
||||
|
||||
@ -172,12 +172,12 @@ export function createAnthropicPayloadLogger(params: {
|
||||
payload,
|
||||
payloadDigest: digest(payload),
|
||||
});
|
||||
options?.onPayload?.(payload);
|
||||
(options as any)?.onPayload?.(payload);
|
||||
};
|
||||
return streamFn(model, context, {
|
||||
...options,
|
||||
...(options as any),
|
||||
onPayload: nextOnPayload,
|
||||
});
|
||||
} as any);
|
||||
};
|
||||
return wrapped;
|
||||
};
|
||||
|
||||
@ -17,7 +17,7 @@ import {
|
||||
writeCache,
|
||||
} from "./web-shared.js";
|
||||
|
||||
const SEARCH_PROVIDERS = ["brave", "perplexity"] as const;
|
||||
const SEARCH_PROVIDERS = ["brave", "perplexity", "valyu"] as const;
|
||||
const DEFAULT_SEARCH_COUNT = 5;
|
||||
const MAX_SEARCH_COUNT = 10;
|
||||
|
||||
@ -82,6 +82,14 @@ type PerplexityConfig = {
|
||||
model?: string;
|
||||
};
|
||||
|
||||
type ValyuConfig = {
|
||||
apiKey?: string;
|
||||
baseUrl?: string;
|
||||
maxResults?: number;
|
||||
searchType?: "all" | "web" | "proprietary";
|
||||
deepSearch?: boolean;
|
||||
};
|
||||
|
||||
type PerplexityApiKeySource = "config" | "perplexity_env" | "openrouter_env" | "none";
|
||||
|
||||
type PerplexitySearchResponse = {
|
||||
@ -130,6 +138,15 @@ function missingSearchKeyPayload(provider: (typeof SEARCH_PROVIDERS)[number]) {
|
||||
};
|
||||
}
|
||||
|
||||
function missingValyuKeyPayload() {
|
||||
return {
|
||||
error: "missing_valyu_api_key",
|
||||
message:
|
||||
"web_search (valyu) needs an API key. Set VALYU_API_KEY in the Gateway environment, or configure tools.web.search.valyu.apiKey.",
|
||||
docs: "https://docs.clawd.bot/tools/web",
|
||||
};
|
||||
}
|
||||
|
||||
function resolveSearchProvider(search?: WebSearchConfig): (typeof SEARCH_PROVIDERS)[number] {
|
||||
const raw =
|
||||
search && "provider" in search && typeof search.provider === "string"
|
||||
@ -137,6 +154,7 @@ function resolveSearchProvider(search?: WebSearchConfig): (typeof SEARCH_PROVIDE
|
||||
: "";
|
||||
if (raw === "perplexity") return "perplexity";
|
||||
if (raw === "brave") return "brave";
|
||||
if (raw === "valyu") return "valyu";
|
||||
return "brave";
|
||||
}
|
||||
|
||||
@ -213,6 +231,19 @@ function resolvePerplexityModel(perplexity?: PerplexityConfig): string {
|
||||
return fromConfig || DEFAULT_PERPLEXITY_MODEL;
|
||||
}
|
||||
|
||||
function resolveValyuConfig(search?: WebSearchConfig): ValyuConfig {
|
||||
if (!search || typeof search !== "object") return {};
|
||||
const valyu = "valyu" in search ? search.valyu : undefined;
|
||||
if (!valyu || typeof valyu !== "object") return {};
|
||||
return valyu as ValyuConfig;
|
||||
}
|
||||
|
||||
function resolveValyuApiKey(valyu?: ValyuConfig): string | undefined {
|
||||
const fromConfig = normalizeApiKey(valyu?.apiKey);
|
||||
const fromEnv = normalizeApiKey(process.env.VALYU_API_KEY);
|
||||
return fromConfig || fromEnv || undefined;
|
||||
}
|
||||
|
||||
function resolveSearchCount(value: unknown, fallback: number): number {
|
||||
const parsed = typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
||||
const clamped = Math.max(1, Math.min(MAX_SEARCH_COUNT, Math.floor(parsed)));
|
||||
@ -269,6 +300,61 @@ async function runPerplexitySearch(params: {
|
||||
return { content, citations };
|
||||
}
|
||||
|
||||
async function runValyuSearch(params: {
|
||||
query: string;
|
||||
apiKey: string;
|
||||
baseUrl?: string;
|
||||
maxResults?: number;
|
||||
searchType?: string;
|
||||
deepSearch?: boolean;
|
||||
timeoutSeconds: number;
|
||||
}): Promise<Record<string, unknown>> {
|
||||
const baseUrl = params.baseUrl?.replace(/\/$/, "") || "https://api.valyu.ai/v1";
|
||||
const endpoint = `${baseUrl}/search`;
|
||||
|
||||
const res = await fetch(endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: params.query,
|
||||
max_num_results: params.maxResults,
|
||||
search_type: params.searchType || "all",
|
||||
// Valyu might support a deep search flag in the body or via different endpoint.
|
||||
// Based on research, it's often a search_type or specialized async API.
|
||||
// We'll pass it in if supported.
|
||||
deep_search: params.deepSearch,
|
||||
}),
|
||||
signal: withTimeout(undefined, params.timeoutSeconds * 1000),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const detail = await readResponseText(res);
|
||||
throw new Error(`Valyu API error (${res.status}): ${detail || res.statusText}`);
|
||||
}
|
||||
|
||||
const data = (await res.json()) as any;
|
||||
// Map Valyu results to common format
|
||||
const results = Array.isArray(data.results) ? data.results : [];
|
||||
const mapped = results.map((entry: any) => ({
|
||||
title: entry.title || entry.name || "",
|
||||
url: entry.url || entry.link || "",
|
||||
description: entry.snippet || entry.description || entry.text || "",
|
||||
published: entry.published_at || entry.date || undefined,
|
||||
siteName: entry.source || resolveSiteName(entry.url || ""),
|
||||
}));
|
||||
|
||||
return {
|
||||
query: params.query,
|
||||
provider: "valyu",
|
||||
count: mapped.length,
|
||||
results: mapped,
|
||||
rawResponse: data, // Keep raw for debugging/advanced usage
|
||||
};
|
||||
}
|
||||
|
||||
async function runWebSearch(params: {
|
||||
query: string;
|
||||
count: number;
|
||||
@ -281,6 +367,7 @@ async function runWebSearch(params: {
|
||||
ui_lang?: string;
|
||||
perplexityBaseUrl?: string;
|
||||
perplexityModel?: string;
|
||||
valyu?: ValyuConfig;
|
||||
}): Promise<Record<string, unknown>> {
|
||||
const cacheKey = normalizeCacheKey(
|
||||
`${params.provider}:${params.query}:${params.count}:${params.country || "default"}:${params.search_lang || "default"}:${params.ui_lang || "default"}`,
|
||||
@ -311,6 +398,24 @@ async function runWebSearch(params: {
|
||||
return payload;
|
||||
}
|
||||
|
||||
if (params.provider === "valyu") {
|
||||
const result = await runValyuSearch({
|
||||
query: params.query,
|
||||
apiKey: params.apiKey,
|
||||
baseUrl: params.valyu?.baseUrl,
|
||||
maxResults: params.count,
|
||||
searchType: params.valyu?.searchType,
|
||||
deepSearch: params.valyu?.deepSearch,
|
||||
timeoutSeconds: params.timeoutSeconds,
|
||||
});
|
||||
const payload = {
|
||||
...result,
|
||||
tookMs: Date.now() - start,
|
||||
};
|
||||
writeCache(SEARCH_CACHE, cacheKey, payload, params.cacheTtlMs);
|
||||
return payload;
|
||||
}
|
||||
|
||||
if (params.provider !== "brave") {
|
||||
throw new Error("Unsupported web search provider.");
|
||||
}
|
||||
@ -372,11 +477,14 @@ export function createWebSearchTool(options?: {
|
||||
|
||||
const provider = resolveSearchProvider(search);
|
||||
const perplexityConfig = resolvePerplexityConfig(search);
|
||||
const valyuConfig = resolveValyuConfig(search);
|
||||
|
||||
const description =
|
||||
provider === "perplexity"
|
||||
? "Search the web using Perplexity Sonar (direct or via OpenRouter). Returns AI-synthesized answers with citations from real-time web search."
|
||||
: "Search the web using Brave Search API. Supports region-specific and localized search via country and language parameters. Returns titles, URLs, and snippets for fast research.";
|
||||
: provider === "valyu"
|
||||
? "Search the web using Valyu API. Supports standard and deep search for high-quality web and proprietary content."
|
||||
: "Search the web using Brave Search API. Supports region-specific and localized search via country and language parameters. Returns titles, URLs, and snippets for fast research.";
|
||||
|
||||
return {
|
||||
label: "Web Search",
|
||||
@ -386,10 +494,16 @@ export function createWebSearchTool(options?: {
|
||||
execute: async (_toolCallId, args) => {
|
||||
const perplexityAuth =
|
||||
provider === "perplexity" ? resolvePerplexityApiKey(perplexityConfig) : undefined;
|
||||
const valyuApiKey = provider === "valyu" ? resolveValyuApiKey(valyuConfig) : undefined;
|
||||
const apiKey =
|
||||
provider === "perplexity" ? perplexityAuth?.apiKey : resolveSearchApiKey(search);
|
||||
provider === "perplexity"
|
||||
? perplexityAuth?.apiKey
|
||||
: provider === "valyu"
|
||||
? valyuApiKey
|
||||
: resolveSearchApiKey(search);
|
||||
|
||||
if (!apiKey) {
|
||||
if (provider === "valyu") return jsonResult(missingValyuKeyPayload());
|
||||
return jsonResult(missingSearchKeyPayload(provider));
|
||||
}
|
||||
const params = args as Record<string, unknown>;
|
||||
@ -415,6 +529,7 @@ export function createWebSearchTool(options?: {
|
||||
perplexityAuth?.apiKey,
|
||||
),
|
||||
perplexityModel: resolvePerplexityModel(perplexityConfig),
|
||||
valyu: valyuConfig,
|
||||
});
|
||||
return jsonResult(result);
|
||||
},
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
normalizeAccountId,
|
||||
} from "../../routing/session-key.js";
|
||||
// Zoho Cliq Channel Plugin - Core Stub
|
||||
// Full implementation is in extensions/zoho-cliq
|
||||
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
|
||||
import type { ChannelMeta } from "./types.js";
|
||||
import type { ChannelPlugin } from "./types.js";
|
||||
|
||||
@ -11,7 +11,7 @@ const meta: ChannelMeta = {
|
||||
selectionLabel: "Zoho Cliq (OAuth)",
|
||||
docsPath: "/channels/zoho-cliq",
|
||||
docsLabel: "zoho-cliq",
|
||||
blurb: "supported (OAuth API).",
|
||||
blurb: "Zoho Cliq team messaging via OAuth API.",
|
||||
};
|
||||
|
||||
export type ResolvedZohoCliqAccount = {
|
||||
@ -24,13 +24,33 @@ export type ResolvedZohoCliqAccount = {
|
||||
refreshToken: string;
|
||||
dc: string;
|
||||
allowFrom?: Array<string | number>;
|
||||
config: {
|
||||
enabled?: boolean;
|
||||
name?: string;
|
||||
clientId?: string;
|
||||
clientSecret?: string;
|
||||
refreshToken?: string;
|
||||
dc?: string;
|
||||
allowFrom?: Array<string | number>;
|
||||
dmPolicy?: "open" | "pairing" | "allowlist";
|
||||
groupPolicy?: "open" | "allowlist";
|
||||
groupAllowFrom?: Array<string | number>;
|
||||
};
|
||||
};
|
||||
|
||||
function parseDataCenter(raw?: string): string {
|
||||
const upper = (raw ?? "US").toUpperCase();
|
||||
if (["US", "EU", "IN", "AU", "JP", "CA", "SA"].includes(upper)) {
|
||||
return upper;
|
||||
}
|
||||
return "US";
|
||||
}
|
||||
|
||||
export const zohoCliqPlugin: ChannelPlugin<ResolvedZohoCliqAccount> = {
|
||||
id: "zoho-cliq",
|
||||
meta,
|
||||
capabilities: {
|
||||
chatTypes: ["direct", "group"],
|
||||
chatTypes: ["direct", "group", "channel"],
|
||||
media: true,
|
||||
},
|
||||
config: {
|
||||
@ -41,59 +61,40 @@ export const zohoCliqPlugin: ChannelPlugin<ResolvedZohoCliqAccount> = {
|
||||
},
|
||||
resolveAccount: (cfg, accountId) => {
|
||||
const resolvedAccountId = normalizeAccountId(accountId);
|
||||
const accounts = (cfg as any).channels?.["zoho-cliq"]?.accounts;
|
||||
if (!accounts) {
|
||||
return {
|
||||
accountId: resolvedAccountId,
|
||||
name: undefined,
|
||||
enabled: false,
|
||||
configured: false,
|
||||
clientId: "",
|
||||
clientSecret: "",
|
||||
refreshToken: "",
|
||||
dc: "",
|
||||
};
|
||||
}
|
||||
const account = accounts[resolvedAccountId];
|
||||
if (!account) {
|
||||
return {
|
||||
accountId: resolvedAccountId,
|
||||
name: undefined,
|
||||
enabled: false,
|
||||
configured: false,
|
||||
clientId: "",
|
||||
clientSecret: "",
|
||||
refreshToken: "",
|
||||
dc: "",
|
||||
};
|
||||
}
|
||||
const channelCfg = (cfg as any).channels?.["zoho-cliq"];
|
||||
const accounts = channelCfg?.accounts;
|
||||
const accountCfg =
|
||||
accounts?.[resolvedAccountId] ??
|
||||
(resolvedAccountId === DEFAULT_ACCOUNT_ID ? channelCfg : undefined) ??
|
||||
{};
|
||||
|
||||
const clientId = accountCfg.clientId ?? process.env.ZOHO_CLIQ_CLIENT_ID ?? "";
|
||||
const clientSecret = accountCfg.clientSecret ?? process.env.ZOHO_CLIQ_CLIENT_SECRET ?? "";
|
||||
const refreshToken = accountCfg.refreshToken ?? process.env.ZOHO_CLIQ_REFRESH_TOKEN ?? "";
|
||||
const dc = parseDataCenter(accountCfg.dc ?? process.env.ZOHO_CLIQ_DC);
|
||||
|
||||
return {
|
||||
accountId: resolvedAccountId,
|
||||
name: account.name,
|
||||
enabled: account.enabled ?? true,
|
||||
configured: !!(
|
||||
account.clientId &&
|
||||
account.clientSecret &&
|
||||
account.refreshToken
|
||||
),
|
||||
clientId: account.clientId,
|
||||
clientSecret: account.clientSecret,
|
||||
refreshToken: account.refreshToken,
|
||||
dc: account.dc,
|
||||
allowFrom: account.allowFrom,
|
||||
name: accountCfg.name,
|
||||
enabled: accountCfg.enabled ?? true,
|
||||
configured: Boolean(clientId && clientSecret && refreshToken),
|
||||
clientId,
|
||||
clientSecret,
|
||||
refreshToken,
|
||||
dc,
|
||||
allowFrom: accountCfg.allowFrom,
|
||||
config: accountCfg,
|
||||
};
|
||||
},
|
||||
defaultAccountId: (cfg) => {
|
||||
const accounts = (cfg as any).channels?.["zoho-cliq"]?.accounts;
|
||||
if (!accounts) return DEFAULT_ACCOUNT_ID;
|
||||
const enabled = Object.entries(accounts).filter(
|
||||
([_, a]) => (a as any).enabled ?? true,
|
||||
);
|
||||
const enabled = Object.entries(accounts).filter(([_, a]) => (a as any).enabled ?? true);
|
||||
if (enabled.length === 1) return enabled[0][0];
|
||||
if (Object.hasOwn(accounts, DEFAULT_ACCOUNT_ID)) {
|
||||
return DEFAULT_ACCOUNT_ID;
|
||||
}
|
||||
return Object.keys(accounts)[0];
|
||||
return Object.keys(accounts)[0] ?? DEFAULT_ACCOUNT_ID;
|
||||
},
|
||||
setAccountEnabled: ({ cfg, accountId, enabled }) => {
|
||||
const resolvedAccountId = accountId ?? DEFAULT_ACCOUNT_ID;
|
||||
@ -125,6 +126,7 @@ export const zohoCliqPlugin: ChannelPlugin<ResolvedZohoCliqAccount> = {
|
||||
name: account.name,
|
||||
enabled: account.enabled,
|
||||
configured: account.configured,
|
||||
dc: account.dc,
|
||||
}),
|
||||
resolveAllowFrom: ({ cfg, accountId }) => {
|
||||
const resolvedAccountId = accountId ?? DEFAULT_ACCOUNT_ID;
|
||||
|
||||
@ -3,6 +3,7 @@ import type { ChannelId } from "./plugins/types.js";
|
||||
import { requireActivePluginRegistry } from "../plugins/runtime.js";
|
||||
|
||||
import { CHAT_CHANNEL_ORDER, type ChatChannelId } from "./order.js";
|
||||
export { CHAT_CHANNEL_ORDER, type ChatChannelId };
|
||||
|
||||
export const CHANNEL_IDS = [...CHAT_CHANNEL_ORDER] as const;
|
||||
|
||||
|
||||
@ -7,6 +7,9 @@ import {
|
||||
IdentitySchema,
|
||||
ToolsMediaSchema,
|
||||
} from "./zod-schema.core.js";
|
||||
import { ToolPolicySchema, ValyuConfigSchema } from "./zod-schema.leaf.js";
|
||||
|
||||
export { ToolPolicySchema };
|
||||
|
||||
export const HeartbeatSchema = z
|
||||
.object({
|
||||
@ -146,18 +149,10 @@ export const SandboxPruneSchema = z
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
export const ToolPolicySchema = z
|
||||
.object({
|
||||
allow: z.array(z.string()).optional(),
|
||||
deny: z.array(z.string()).optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
export const ToolsWebSearchSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
provider: z.union([z.literal("brave"), z.literal("perplexity")]).optional(),
|
||||
provider: z.union([z.literal("brave"), z.literal("perplexity"), z.literal("valyu")]).optional(),
|
||||
apiKey: z.string().optional(),
|
||||
maxResults: z.number().int().positive().optional(),
|
||||
timeoutSeconds: z.number().int().positive().optional(),
|
||||
@ -170,6 +165,7 @@ export const ToolsWebSearchSchema = z
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
valyu: ValyuConfigSchema,
|
||||
})
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
@ -1,7 +1,44 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { isSafeExecutableValue } from "../infra/exec-safety.js";
|
||||
|
||||
import {
|
||||
BlockStreamingChunkSchema,
|
||||
BlockStreamingCoalesceSchema,
|
||||
DmPolicySchema,
|
||||
ExecutableTokenSchema,
|
||||
GroupPolicySchema,
|
||||
HexColorSchema,
|
||||
MarkdownConfigSchema,
|
||||
MarkdownTableModeSchema,
|
||||
MSTeamsReplyStyleSchema,
|
||||
ProviderCommandsSchema,
|
||||
ReplyToModeSchema,
|
||||
RetryConfigSchema,
|
||||
NativeCommandsSettingSchema,
|
||||
TtsConfigSchema,
|
||||
TtsModeSchema,
|
||||
TtsProviderSchema,
|
||||
} from "./zod-schema.leaf.js";
|
||||
|
||||
export {
|
||||
BlockStreamingChunkSchema,
|
||||
BlockStreamingCoalesceSchema,
|
||||
DmPolicySchema,
|
||||
ExecutableTokenSchema,
|
||||
GroupPolicySchema,
|
||||
HexColorSchema,
|
||||
MarkdownConfigSchema,
|
||||
MarkdownTableModeSchema,
|
||||
MSTeamsReplyStyleSchema,
|
||||
ProviderCommandsSchema,
|
||||
ReplyToModeSchema,
|
||||
RetryConfigSchema,
|
||||
NativeCommandsSettingSchema,
|
||||
TtsConfigSchema,
|
||||
TtsModeSchema,
|
||||
TtsProviderSchema,
|
||||
};
|
||||
|
||||
export const ModelApiSchema = z.union([
|
||||
z.literal("openai-completions"),
|
||||
z.literal("openai-responses"),
|
||||
@ -118,100 +155,6 @@ export const QueueDropSchema = z.union([
|
||||
z.literal("new"),
|
||||
z.literal("summarize"),
|
||||
]);
|
||||
export const ReplyToModeSchema = z.union([z.literal("off"), z.literal("first"), z.literal("all")]);
|
||||
|
||||
// GroupPolicySchema: controls how group messages are handled
|
||||
// Used with .default("allowlist").optional() pattern:
|
||||
// - .optional() allows field omission in input config
|
||||
// - .default("allowlist") ensures runtime always resolves to "allowlist" if not provided
|
||||
export const GroupPolicySchema = z.enum(["open", "disabled", "allowlist"]);
|
||||
|
||||
export const DmPolicySchema = z.enum(["pairing", "allowlist", "open", "disabled"]);
|
||||
|
||||
export const BlockStreamingCoalesceSchema = z
|
||||
.object({
|
||||
minChars: z.number().int().positive().optional(),
|
||||
maxChars: z.number().int().positive().optional(),
|
||||
idleMs: z.number().int().nonnegative().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const BlockStreamingChunkSchema = z
|
||||
.object({
|
||||
minChars: z.number().int().positive().optional(),
|
||||
maxChars: z.number().int().positive().optional(),
|
||||
breakPreference: z
|
||||
.union([z.literal("paragraph"), z.literal("newline"), z.literal("sentence")])
|
||||
.optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const MarkdownTableModeSchema = z.enum(["off", "bullets", "code"]);
|
||||
|
||||
export const MarkdownConfigSchema = z
|
||||
.object({
|
||||
tables: MarkdownTableModeSchema.optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
export const TtsProviderSchema = z.enum(["elevenlabs", "openai"]);
|
||||
export const TtsModeSchema = z.enum(["final", "all"]);
|
||||
export const TtsConfigSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
mode: TtsModeSchema.optional(),
|
||||
provider: TtsProviderSchema.optional(),
|
||||
summaryModel: z.string().optional(),
|
||||
modelOverrides: z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
allowText: z.boolean().optional(),
|
||||
allowProvider: z.boolean().optional(),
|
||||
allowVoice: z.boolean().optional(),
|
||||
allowModelId: z.boolean().optional(),
|
||||
allowVoiceSettings: z.boolean().optional(),
|
||||
allowNormalization: z.boolean().optional(),
|
||||
allowSeed: z.boolean().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
elevenlabs: z
|
||||
.object({
|
||||
apiKey: z.string().optional(),
|
||||
baseUrl: z.string().optional(),
|
||||
voiceId: z.string().optional(),
|
||||
modelId: z.string().optional(),
|
||||
seed: z.number().int().min(0).max(4294967295).optional(),
|
||||
applyTextNormalization: z.enum(["auto", "on", "off"]).optional(),
|
||||
languageCode: z.string().optional(),
|
||||
voiceSettings: z
|
||||
.object({
|
||||
stability: z.number().min(0).max(1).optional(),
|
||||
similarityBoost: z.number().min(0).max(1).optional(),
|
||||
style: z.number().min(0).max(1).optional(),
|
||||
useSpeakerBoost: z.boolean().optional(),
|
||||
speed: z.number().min(0.5).max(2).optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
openai: z
|
||||
.object({
|
||||
apiKey: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
voice: z.string().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
prefsPath: z.string().optional(),
|
||||
maxTextLength: z.number().int().min(1).optional(),
|
||||
timeoutMs: z.number().int().min(1000).max(120000).optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
export const HumanDelaySchema = z
|
||||
.object({
|
||||
@ -271,18 +214,6 @@ export const requireOpenAllowFrom = (params: {
|
||||
});
|
||||
};
|
||||
|
||||
export const MSTeamsReplyStyleSchema = z.enum(["thread", "top-level"]);
|
||||
|
||||
export const RetryConfigSchema = z
|
||||
.object({
|
||||
attempts: z.number().int().min(1).optional(),
|
||||
minDelayMs: z.number().int().min(0).optional(),
|
||||
maxDelayMs: z.number().int().min(0).optional(),
|
||||
jitter: z.number().min(0).max(1).optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
export const QueueModeBySurfaceSchema = z
|
||||
.object({
|
||||
whatsapp: QueueModeSchema.optional(),
|
||||
@ -339,12 +270,6 @@ export const TranscribeAudioSchema = z
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
export const HexColorSchema = z.string().regex(/^#?[0-9a-fA-F]{6}$/, "expected hex color (RRGGBB)");
|
||||
|
||||
export const ExecutableTokenSchema = z
|
||||
.string()
|
||||
.refine(isSafeExecutableValue, "expected safe executable name or path");
|
||||
|
||||
export const MediaUnderstandingScopeSchema = z
|
||||
.object({
|
||||
default: z.union([z.literal("allow"), z.literal("deny")]).optional(),
|
||||
@ -452,13 +377,3 @@ export const ToolsMediaSchema = z
|
||||
})
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
export const NativeCommandsSettingSchema = z.union([z.boolean(), z.literal("auto")]);
|
||||
|
||||
export const ProviderCommandsSchema = z
|
||||
.object({
|
||||
native: NativeCommandsSettingSchema.optional(),
|
||||
nativeSkills: NativeCommandsSettingSchema.optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
RetryConfigSchema,
|
||||
requireOpenAllowFrom,
|
||||
} from "./zod-schema.core.js";
|
||||
import { ToolPolicySchema } from "./zod-schema.agent-runtime.js";
|
||||
import { ToolPolicySchema } from "./zod-schema.leaf.js";
|
||||
import { ChannelHeartbeatVisibilitySchema } from "./zod-schema.channels.js";
|
||||
import {
|
||||
normalizeTelegramCommandDescription,
|
||||
|
||||
@ -100,7 +100,7 @@ export {
|
||||
normalizeAllowFrom,
|
||||
requireOpenAllowFrom,
|
||||
} from "../config/zod-schema.core.js";
|
||||
export { ToolPolicySchema } from "../config/zod-schema.agent-runtime.js";
|
||||
export { ToolPolicySchema } from "../config/zod-schema.leaf.js";
|
||||
export type { RuntimeEnv } from "../runtime.js";
|
||||
export type { WizardPrompter } from "../wizard/prompts.js";
|
||||
export { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
|
||||
|
||||
@ -1,11 +1,4 @@
|
||||
import {
|
||||
Editor,
|
||||
type EditorOptions,
|
||||
type EditorTheme,
|
||||
type TUI,
|
||||
Key,
|
||||
matchesKey,
|
||||
} from "@mariozechner/pi-tui";
|
||||
import { Editor, type EditorTheme, type TUI, Key, matchesKey } from "@mariozechner/pi-tui";
|
||||
|
||||
export class CustomEditor extends Editor {
|
||||
onEscape?: () => void;
|
||||
@ -19,8 +12,8 @@ export class CustomEditor extends Editor {
|
||||
onShiftTab?: () => void;
|
||||
onAltEnter?: () => void;
|
||||
|
||||
constructor(tui: TUI, theme: EditorTheme, options?: EditorOptions) {
|
||||
super(tui, theme, options);
|
||||
constructor(tui: TUI, _theme: EditorTheme, _options?: any) {
|
||||
super(tui, _theme);
|
||||
}
|
||||
handleInput(data: string): void {
|
||||
if (matchesKey(data, Key.alt("enter")) && this.onAltEnter) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user