Files
openhuman/app/src/utils/tauriCommands/config.ts
T
+16 a40fbb79d2 Promote main → release (#5242)
Co-authored-by: YellowSnnowmann <167776381+YellowSnnowmann@users.noreply.github.com>
Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cyrus Gray <144336577+graycyrus@users.noreply.github.com>
Co-authored-by: Horst1993 <horst.w@gmicloud.ai>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: James Gentes <jgentes@users.noreply.github.com>
Co-authored-by: Sam <samrusani@users.noreply.github.com>
Co-authored-by: Sami Rusani <14844597+samrusani@users.noreply.github.com>
Co-authored-by: oxoxDev <164490987+oxoxDev@users.noreply.github.com>
Co-authored-by: Muhammad Ismail <78064250+myi1@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: nb213 <binyangzhu000@gmail.com>
Co-authored-by: binyangzhu000-sudo <224954946+binyangzhu000-sudo@users.noreply.github.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
Co-authored-by: CodeGhost21 <164498022+CodeGhost21@users.noreply.github.com>
Co-authored-by: sanil-23 <sanil@tinyhumans.ai>
Co-authored-by: M3gA-Mind <elvin@mahadao.com>
Co-authored-by: oxoxDev <oxoxdev@users.noreply.github.com>
Co-authored-by: mysma-9403 <64923976+mysma-9403@users.noreply.github.com>
Co-authored-by: mwakidenis <mwakidenice@gmail.com>
Co-authored-by: NgoQuocViet2001 <123613986+NgoQuocViet2001@users.noreply.github.com>
Co-authored-by: viet.ngo <viet.ngo@sotatek.com>
Co-authored-by: Maciej Myszkiewicz <mmyszkiewicz@bwcoders.com>
2026-07-28 16:11:03 +05:30

1017 lines
34 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Config and settings commands.
*/
import { invoke } from '@tauri-apps/api/core';
import debug from 'debug';
import { callCoreRpc } from '../../services/coreRpcClient';
import { CORE_RPC_METHODS } from '../../services/rpcMethods';
import { CommandResponse, isTauri, tauriErrorMessage } from './common';
const log = debug('composio:rpc');
export interface ConfigSnapshot {
config: Record<string, unknown>;
workspace_dir: string;
config_path: string;
}
export interface ModelRoute {
hint: string;
model: string;
}
/** Authentication header style. Matches Rust AuthStyle enum. */
export type AuthStyle = 'bearer' | 'anthropic' | 'openhuman_jwt' | 'none';
/** @deprecated Use AuthStyle. Kept for back-compat with old wire format. */
export type CloudProviderType =
| 'openhuman'
| 'openai'
| 'anthropic'
| 'openrouter'
| 'orcarouter'
| 'custom';
/**
* Endpoint config for one cloud LLM provider (new slug-keyed shape).
* API keys are NOT carried here — they live in `auth-profiles.json`
* (set/cleared through the `auth_*` RPCs, keyed by `provider:<slug>`).
*/
export interface CloudProviderCreds {
/** Opaque stable id, e.g. `"p_openai_a8c3f"`. Never shown in UI. */
id: string;
/** User-chosen routing key, e.g. `"openai"`. Used in `"<slug>:<model>"` strings. */
slug: string;
/** Human-readable display label, e.g. `"OpenAI"`. */
label: string;
endpoint: string;
auth_style: AuthStyle;
}
/**
* Per-model registry entry. Mirrors the Rust `ModelRegistryEntry`
* (`config/schema/types.rs`). Carries the user-set `vision` flag that lets a
* custom/BYOK model accept chat image attachments.
*/
export interface ModelRegistryEntry {
id: string;
provider: string;
/** USD per 1M input tokens. `0`/absent ⇒ unknown. Pre-filled for known
* vendor models from the Rust pricing catalog (`cost/catalog.rs`). */
cost_per_1m_input?: number;
/** USD per 1M cached-prefix input tokens. `0`/absent ⇒ unknown. */
cost_per_1m_cached_input?: number;
cost_per_1m_output: number;
/** Max context window in tokens. `0`/absent ⇒ unknown. Pre-filled for known
* vendor models from the Rust pricing catalog. Used to budget prompts and
* trigger compaction — providers differ widely (128K1M+). */
context_window?: number;
vision: boolean;
}
export interface ModelSettingsUpdate {
/**
* OpenHuman product backend URL. Almost always left untouched; the
* inference endpoint is the separate `inference_url` field.
*/
api_url?: string | null;
/**
* Custom OpenAI-compatible LLM endpoint. When set together with
* `api_key`, inference talks directly to this URL instead of routing
* through the OpenHuman backend. Send an empty string to clear.
*/
inference_url?: string | null;
api_key?: string | null;
default_model?: string | null;
default_temperature?: number | null;
/**
* When present, REPLACES `config.model_routes` wholesale with these
* `(hint, model)` pairs. Send `[]` to clear all routes (used when switching
* back to the OpenHuman backend whose built-in router picks per-task models
* on its own). Omit to leave existing routes untouched.
*/
model_routes?: ModelRoute[] | null;
/**
* When present, REPLACES `config.cloud_providers` wholesale. API keys are
* NOT carried here — store them via `authStoreProviderCredentials`.
* Each entry: { id?, slug, label?, endpoint, auth_style? }
*/
cloud_providers?: CloudProviderCreds[] | null;
/**
* When present, REPLACES `config.model_registry` wholesale. Carries each
* model's `vision` flag (Settings → Advanced LLM → custom model → "Supports
* vision"). Send `[]` to clear; omit to leave untouched.
*/
model_registry?: ModelRegistryEntry[] | null;
/** @deprecated No longer used — slug-based routing replaces primary_cloud. */
primary_cloud?: string | null;
/** Per-workload provider strings — see Rust `providers::factory` grammar. */
chat_provider?: string | null;
reasoning_provider?: string | null;
agentic_provider?: string | null;
coding_provider?: string | null;
vision_provider?: string | null;
memory_provider?: string | null;
embeddings_provider?: string | null;
heartbeat_provider?: string | null;
learning_provider?: string | null;
subconscious_provider?: string | null;
}
/**
* Stepped user-facing memory-context window preset. Mirrors the core
* `MemoryContextWindow` enum (`src/openhuman/config/schema/agent.rs`)
* — the actual char budgets are owned by the core, this is the label.
*/
export type MemoryContextWindow = 'minimal' | 'balanced' | 'extended' | 'maximum';
export const MEMORY_CONTEXT_WINDOWS: MemoryContextWindow[] = [
'minimal',
'balanced',
'extended',
'maximum',
];
export interface MemorySettingsUpdate {
backend?: string | null;
auto_save?: boolean | null;
embedding_provider?: string | null;
embedding_model?: string | null;
embedding_dimensions?: number | null;
/** One of `MEMORY_CONTEXT_WINDOWS`. */
memory_window?: MemoryContextWindow | null;
}
export interface RuntimeSettingsUpdate {
kind?: string | null;
reasoning_enabled?: boolean | null;
}
export interface BrowserSettingsUpdate {
enabled?: boolean | null;
backend?: 'agent_browser' | 'playwright' | 'rust_native' | 'computer_use' | 'auto' | null;
}
export interface LocalAiSettingsUpdate {
runtime_enabled?: boolean | null;
/**
* MVP opt-in marker. Bootstrap hard-overrides status to "disabled" when
* this is `false`, regardless of `runtime_enabled`. The unified AI panel
* toggle flips this in tandem with `runtime_enabled` so a single click
* actually turns local AI on — without it, the daemon spawns but
* bootstrap immediately forces status back to disabled (cloud fallback).
*/
opt_in_confirmed?: boolean | null;
provider?: string | null;
base_url?: string | null;
/**
* Bearer credential for OpenAI-compatible local runtimes that require a key
* (e.g. OMLX). Stored in `config.local_ai.api_key` and sent as a Bearer token
* on inference. Keyless runtimes (Ollama / LM Studio) omit this.
*/
api_key?: string | null;
model_id?: string | null;
chat_model_id?: string | null;
usage_embeddings?: boolean | null;
usage_heartbeat?: boolean | null;
usage_learning_reflection?: boolean | null;
usage_subconscious?: boolean | null;
}
export interface RuntimeFlags {
browser_allow_all: boolean;
log_prompts: boolean;
}
export interface AIPreview {
soul: {
raw: string;
name: string;
description: string;
personalityPreview: string[];
safetyRulesPreview: string[];
loadedAt: number;
};
tools: {
raw: string;
totalTools: number;
activeSkills: number;
skillsPreview: string[];
loadedAt: number;
};
metadata: {
loadedAt: number;
loadingDuration: number;
hasFallbacks: boolean;
sources: { soul: string; tools: string };
errors: string[];
};
}
export async function openhumanGetConfig(): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({ method: CORE_RPC_METHODS.configGet });
}
/**
* Safe client-facing config slice. Never contains the raw api_key — only
* `api_key_set` indicates whether a custom backend key is stored. See
* `config.get_client_config` in `src/openhuman/config/schemas.rs`.
*/
export interface ClientConfig {
/** OpenHuman product backend URL (auth/billing/voice). */
api_url: string | null;
/**
* Custom OpenAI-compatible LLM endpoint. Legacy field, retained for
* back-compat — the new AI settings panel reads/writes
* `cloud_providers` + `*_provider` fields instead.
*/
inference_url: string | null;
default_model: string | null;
app_version: string;
api_key_set: boolean;
/** Legacy per-task-hint model overrides (deprecated; will be removed). */
model_routes: ModelRoute[];
/** Configured cloud providers (no API keys — those live in auth-profiles.json). */
cloud_providers: CloudProviderCreds[];
/** Per-model registry carrying each model's `vision` flag. */
model_registry: ModelRegistryEntry[];
/** Id of the `cloud_providers` entry resolved by the `"cloud"` sentinel. */
primary_cloud: string | null;
/**
* #3767: authoritative, core-side per-tier flags — for each chat-mode tier
* (`chat` = Quick mode, `reasoning` = Reasoning mode), true when that tier runs
* on a non-managed provider the user funds themselves (a usable BYO key, local
* runtime, or claude-code). The UI checks whichever tier the user has selected;
* when true the "buy credits" prompt is suppressed for that mode. Optional for
* back-compat with older snapshots.
*/
credits_bypass?: { chat?: boolean; reasoning?: boolean };
/** Per-workload provider strings (e.g. `"cloud"`, `"ollama:llama3.1:8b"`, `"openai:gpt-4o"`). */
chat_provider: string | null;
reasoning_provider: string | null;
agentic_provider: string | null;
coding_provider: string | null;
vision_provider: string | null;
memory_provider: string | null;
embeddings_provider: string | null;
heartbeat_provider: string | null;
learning_provider: string | null;
subconscious_provider: string | null;
}
export async function openhumanGetClientConfig(): Promise<CommandResponse<ClientConfig>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ClientConfig>>({
method: 'openhuman.inference_get_client_config',
});
}
/**
* Status payload for the Claude Code CLI provider — mirrors Rust
* `claude_code::types::CliStatus`. The `status` discriminator is the
* snake_case Serde rename; `path` and `version` may be absent depending
* on which variant fired.
*/
export type ClaudeCodeStatus =
| { status: 'ok'; version: string; path: string }
| { status: 'not_installed' }
| { status: 'outdated'; version: string; min_required: string; path: string }
| { status: 'unusable'; path: string; reason: string };
/**
* Probe the local `claude` CLI binary (Claude Code CLI provider). Returns
* install + version status; never throws on a missing binary — the
* `not_installed` variant signals that case explicitly.
*/
export async function openhumanClaudeCodeStatus(): Promise<CommandResponse<ClaudeCodeStatus>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ClaudeCodeStatus>>({
method: 'openhuman.inference_claude_code_status',
});
}
/**
* Auth state for the Claude Code CLI provider — mirrors Rust
* `claude_code::auth_status::AuthSource`. The `source` discriminator is
* the snake_case Serde rename. `account_email` / `subscription_type` /
* `expires_at` are best-effort: absent when the CLI's auth-status schema
* drifts. `unknown` means we couldn't determine the state (binary missing,
* spawn failed, or a CLI older than `auth status`) — it is NEVER signed-out.
*/
export type ClaudeCodeAuthStatus =
| {
source: 'subscription';
account_email: string | null;
subscription_type: string | null;
expires_at: string | null;
last_checked: number;
}
| { source: 'api_key_env'; last_checked: number }
| { source: 'none'; last_checked: number }
| { source: 'unknown'; reason: string | null; last_checked: number };
/**
* Detect Claude Code CLI auth state via `claude auth status --json`
* (cross-platform: abstracts the macOS Keychain vs. Linux/Windows file
* stores), or `ANTHROPIC_API_KEY` env. Spawns the CLI — call on-demand /
* Recheck, not on a tight loop.
*/
export async function openhumanClaudeCodeAuthStatus(): Promise<ClaudeCodeAuthStatus> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
// The core handler returns the value via `RpcOutcome::new(_, vec![])` with no
// logs, which `into_cli_compatible_json` serializes as the BARE value (not a
// `{ result, logs }` envelope). `callCoreRpc` returns the JSON-RPC `result`,
// so this resolves directly to the AuthStatus — do NOT read `.result`.
return await callCoreRpc<ClaudeCodeAuthStatus>({
method: 'openhuman.inference_claude_code_auth_status',
});
}
/**
* Persisted Claude Code provider settings — mirrors Rust
* `claude_code::settings::ClaudeCodeSettings`. `full_access=true` runs the
* CLI with `--permission-mode bypassPermissions` + its full native toolset
* (Bash/network/subagents); `false` (default) is the safer `acceptEdits`
* posture (auto-apply file edits, gate the rest). On macOS the Seatbelt jail
* still walls off `~/.openhuman` in either mode.
*/
export interface ClaudeCodeSettings {
full_access: boolean;
}
/**
* Read the persisted Claude Code full-access toggle. Bare value (no
* `{ result, logs }` envelope) — see {@link openhumanClaudeCodeAuthStatus}.
*/
export async function openhumanClaudeCodeSettings(): Promise<ClaudeCodeSettings> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<ClaudeCodeSettings>({
method: 'openhuman.inference_claude_code_settings',
});
}
/**
* Persist the Claude Code full-access toggle. Returns the saved settings.
* Takes effect on the next chat turn (the driver reads the file per-turn).
*/
export async function openhumanClaudeCodeSetFullAccess(
enabled: boolean
): Promise<ClaudeCodeSettings> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<ClaudeCodeSettings>({
method: 'openhuman.inference_claude_code_set_full_access',
params: { enabled },
});
}
/**
* Open the user's native terminal and run `claude login` inside it. The
* CLI's OAuth flow is interactive, so we can't host it in-app — we
* detach into a terminal window and let the user complete the flow
* there, then click Recheck back in the settings card.
*
* Returns the name of the terminal emulator that was launched.
*/
export async function openhumanClaudeCodeLoginLaunch(): Promise<string> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await invoke<string>('claude_code_login_launch');
}
export async function openhumanUpdateModelSettings(
update: ModelSettingsUpdate
): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
method: 'openhuman.inference_update_model_settings',
params: update,
});
}
export async function openhumanUpdateMemorySettings(
update: MemorySettingsUpdate
): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
method: CORE_RPC_METHODS.configUpdateMemorySettings,
params: update,
});
}
export async function openhumanUpdateRuntimeSettings(
update: RuntimeSettingsUpdate
): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
method: CORE_RPC_METHODS.configUpdateRuntimeSettings,
params: update,
});
}
export async function openhumanUpdateBrowserSettings(
update: BrowserSettingsUpdate
): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
method: CORE_RPC_METHODS.configUpdateBrowserSettings,
params: update,
});
}
// ── Agent access mode (autonomy / filesystem permissions) ───────────────────
export type AutonomyLevel = 'readonly' | 'supervised' | 'full';
export type TrustedAccess = 'read' | 'readwrite';
export interface TrustedRoot {
path: string;
access: TrustedAccess;
}
/** The full [autonomy] block as returned by config_get_autonomy_settings. */
export interface AutonomySettings {
level: AutonomyLevel;
workspace_only: boolean;
allowed_commands: string[];
forbidden_paths: string[];
trusted_roots: TrustedRoot[];
allow_tool_install: boolean;
max_actions_per_hour: number;
/** "Always allow" allowlist — tool names the agent runs without a prompt. */
auto_approve: string[];
/** Require approval before an agent executes a task-board plan. */
require_task_plan_approval?: boolean;
/**
* When true, the approval gate auto-approves ALL tool calls without
* prompting — a blanket bypass, not just the `auto_approve` allowlist
* above. Subconscious-tainted and unlabelled origins are still denied by
* the gate regardless of this flag; hard security blocks are unaffected.
* Defaults to `false`.
*/
auto_approve_all?: boolean;
}
/** Partial update — omitted fields are left unchanged. */
export interface AutonomySettingsUpdate {
level?: AutonomyLevel;
workspace_only?: boolean;
allowed_commands?: string[];
forbidden_paths?: string[];
trusted_roots?: TrustedRoot[];
allow_tool_install?: boolean;
max_actions_per_hour?: number;
/** Replaces the "Always allow" allowlist wholesale. */
auto_approve?: string[];
require_task_plan_approval?: boolean;
/** Blanket "auto-approve everything" bypass. See `AutonomySettings`. */
auto_approve_all?: boolean;
}
export async function openhumanGetAutonomySettings(): Promise<CommandResponse<AutonomySettings>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AutonomySettings>>({
method: CORE_RPC_METHODS.configGetAutonomySettings,
});
}
/**
* Agent filesystem roots returned by `config_get_agent_paths`. All three are
* already-canonicalised path strings; the UI renders them verbatim instead of
* hard-coding defaults like `~/OpenHuman/projects`.
*
* - `action_dir` — agent CWD for `shell` / `node_exec` / `npm_exec` / file
* writes. Defaults to `projects_dir`; overridable via `OPENHUMAN_ACTION_DIR`.
* - `workspace_dir` — internal product state (memory / sessions / vault).
* Agent-blocked.
* - `projects_dir` — default projects home; matches `action_dir` when no
* override is set.
* - `action_dir_source` — where the effective `action_dir` came from:
* `'env'` (pinned by OPENHUMAN_ACTION_DIR — UI must disable editing),
* `'override'` (a persisted user choice), or `'default'`.
*/
export interface AgentPaths {
action_dir: string;
workspace_dir: string;
projects_dir: string;
action_dir_source: 'env' | 'override' | 'default';
}
export async function openhumanGetAgentPaths(): Promise<CommandResponse<AgentPaths>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AgentPaths>>({
method: CORE_RPC_METHODS.configGetAgentPaths,
});
}
/** Partial update for the agent's editable filesystem roots (issue #3240). */
export interface AgentPathsUpdate {
action_dir?: string;
}
export async function openhumanUpdateAgentPaths(
update: AgentPathsUpdate
): Promise<CommandResponse<AgentPaths>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AgentPaths>>({
method: CORE_RPC_METHODS.configUpdateAgentPaths,
params: update,
});
}
export async function openhumanUpdateAutonomySettings(
update: AutonomySettingsUpdate
): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
method: CORE_RPC_METHODS.configUpdateAutonomySettings,
params: update,
});
}
// ── "Super context" toggle ───────────────────────────────────────────────────
/**
* Reads the "super context" flag (`context.super_context_enabled`). When on,
* the harness runs a read-only context-collection pass on the first turn of a
* new thread — before the orchestrator LLM runs — and folds the result into the
* user message. Surfaced as the toggle below the chat composer.
*/
export async function openhumanGetSuperContextEnabled(): Promise<CommandResponse<boolean>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<boolean>>({
method: CORE_RPC_METHODS.configGetSuperContextEnabled,
});
}
/**
* Enables or disables "super context". Takes effect for threads started after
* the change (the value is baked into the frozen turn-1 prefix), so toggling it
* mid-conversation only affects the next new thread.
*/
export async function openhumanSetSuperContextEnabled(
value: boolean
): Promise<CommandResponse<boolean>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<boolean>>({
method: CORE_RPC_METHODS.configSetSuperContextEnabled,
params: { value },
});
}
// ── Sandbox execution backend settings ───────────────────────────────────────
export type SandboxBackendId = 'auto' | 'docker' | 'landlock' | 'firejail' | 'bubblewrap' | 'none';
/** Current sandbox settings returned by config_get_sandbox_settings. */
export interface SandboxSettings {
enabled: boolean;
backend: SandboxBackendId;
docker_image: string;
docker_memory_limit_mb: number | null;
docker_cpu_limit: number | null;
docker_available: boolean;
detected_backend: string;
env_passthrough: string[];
}
/** Partial update — omitted fields are left unchanged. */
export interface SandboxSettingsUpdate {
backend?: SandboxBackendId;
enabled?: boolean;
docker_image?: string;
docker_memory_limit_mb?: number | null;
docker_cpu_limit?: number | null;
env_passthrough?: string[];
}
export async function openhumanGetSandboxSettings(): Promise<CommandResponse<SandboxSettings>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<SandboxSettings>>({
method: CORE_RPC_METHODS.configGetSandboxSettings,
});
}
export async function openhumanUpdateSandboxSettings(
update: SandboxSettingsUpdate
): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
method: CORE_RPC_METHODS.configUpdateSandboxSettings,
params: update,
});
}
// ── Memory sync schedule (#3302) ─────────────────────────────────────────────
/** Global memory-sync schedule returned by config_get_memory_sync_settings. */
export interface MemorySyncSettings {
/** Stored value: null = use the default cadence, 0 = Manual only, n>0 = seconds. */
sync_interval_secs: number | null;
/** Resolved cadence to highlight in the UI (the default when unset; 0 for manual). */
selected_secs: number;
/** True when the user picked "Manual only" (stored value is 0). */
is_manual: boolean;
/** True when no explicit choice is stored (falls back to `default_secs`). */
is_default: boolean;
/** The effective default cadence (seconds) applied when unset (24h). */
default_secs: number;
/** Preset cadences (seconds) offered in the UI: 4h / 12h / 24h. */
presets: number[];
}
/** Partial update — set `sync_interval_secs` to `null` to reset to default. */
export interface MemorySyncSettingsUpdate {
/** null = default, 0 = Manual only, n>0 = sync every n seconds. */
sync_interval_secs?: number | null;
}
export async function openhumanGetMemorySyncSettings(): Promise<
CommandResponse<MemorySyncSettings>
> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<MemorySyncSettings>>({
method: CORE_RPC_METHODS.configGetMemorySyncSettings,
});
}
export async function openhumanUpdateMemorySyncSettings(
update: MemorySyncSettingsUpdate
): Promise<CommandResponse<MemorySyncSettings>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<MemorySyncSettings>>({
method: CORE_RPC_METHODS.configUpdateMemorySyncSettings,
params: update,
});
}
// ── Agent execution settings (action/tool timeout) ──────────────────────────
/** Agent execution settings as returned by config_get_agent_settings. */
export interface AgentSettings {
/** Configured wall-clock timeout for a single tool/action, in seconds. */
agent_timeout_secs: number;
/** Runtime-effective timeout (may differ from configured when env-overridden). */
effective_timeout_secs: number;
/** True when OPENHUMAN_TOOL_TIMEOUT_SECS overrides the configured value. */
env_override: boolean;
/** Lowest accepted timeout (seconds). */
min_timeout_secs: number;
/** Highest accepted timeout (seconds). */
max_timeout_secs: number;
}
/** Partial update — omitted fields are left unchanged. */
export interface AgentSettingsUpdate {
agent_timeout_secs?: number;
}
export async function openhumanGetAgentSettings(): Promise<CommandResponse<AgentSettings>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AgentSettings>>({
method: CORE_RPC_METHODS.configGetAgentSettings,
});
}
export async function openhumanUpdateAgentSettings(
update: AgentSettingsUpdate
): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
method: CORE_RPC_METHODS.configUpdateAgentSettings,
params: update,
});
}
export async function openhumanUpdateLocalAiSettings(
update: LocalAiSettingsUpdate
): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
method: 'openhuman.inference_update_local_settings',
params: update,
});
}
export async function openhumanUpdateAnalyticsSettings(update: {
enabled?: boolean;
}): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
method: CORE_RPC_METHODS.configUpdateAnalyticsSettings,
params: update,
});
}
export async function openhumanGetAnalyticsSettings(): Promise<
CommandResponse<{ enabled: boolean }>
> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<{ enabled: boolean }>>({
method: CORE_RPC_METHODS.configGetAnalyticsSettings,
});
}
/** Meeting Assistant calendar auto-join policy (issue #3511). */
export type MeetAutoJoinPolicy = 'ask_each_time' | 'always' | 'never';
/** Meeting Assistant post-call summary policy. */
export type MeetAutoSummarizePolicy = 'ask' | 'always' | 'never';
/** Full shape returned by `openhuman.config_get_meet_settings`. */
export interface MeetSettings {
auto_orchestrator_handoff: boolean;
auto_join_policy: MeetAutoJoinPolicy;
auto_summarize_policy: MeetAutoSummarizePolicy;
listen_only_default: boolean;
ingest_backend_transcripts: boolean;
/** Per-platform auto-join policy overrides. Keys: "gmeet"|"zoom"|"teams"|"webex". */
platform_auto_join_policies?: Record<string, MeetAutoJoinPolicy>;
/**
* Master switch for calendar-driven meeting actions (auto-join / ask-to-join).
* Decoupled from the heartbeat reminder-notification toggle.
*/
watch_calendar: boolean;
/** Calendar detection source for Google Meet: composio (default) | recall. */
calendar_provider?: 'composio' | 'recall';
/** The user's meeting display name, reused as the bot's reply anchor on join. */
reply_display_name?: string;
}
/** Partial update accepted by `openhuman.config_update_meet_settings`. */
export interface MeetSettingsUpdate {
auto_orchestrator_handoff?: boolean;
auto_join_policy?: MeetAutoJoinPolicy;
auto_summarize_policy?: MeetAutoSummarizePolicy;
listen_only_default?: boolean;
ingest_backend_transcripts?: boolean;
/** Per-platform auto-join policy overrides. Keys: "gmeet"|"zoom"|"teams"|"webex". */
platform_auto_join_policies?: Record<string, MeetAutoJoinPolicy>;
/** Master switch for calendar-driven auto-join / ask-to-join. */
watch_calendar?: boolean;
/** Calendar detection source for Google Meet: composio (default) | recall. */
calendar_provider?: 'composio' | 'recall';
/** The user's meeting display name, reused as the bot's reply anchor on join. */
reply_display_name?: string;
}
export async function openhumanUpdateMeetSettings(
update: MeetSettingsUpdate
): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
method: 'openhuman.config_update_meet_settings',
params: update,
});
}
export async function openhumanGetMeetSettings(): Promise<CommandResponse<MeetSettings>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<MeetSettings>>({
method: 'openhuman.config_get_meet_settings',
});
}
export type SearchEngineId = 'disabled' | 'managed' | 'parallel' | 'brave' | 'querit' | 'exa';
export interface SearchSettingsUpdate {
engine?: SearchEngineId;
max_results?: number;
timeout_secs?: number;
/** Empty string clears the stored key. */
parallel_api_key?: string;
/** Empty string clears the stored key. */
brave_api_key?: string;
/** Empty string clears the stored key. */
querit_api_key?: string;
/**
* Exa API key (BYOK). Empty string clears the stored key. When set and
* `engine: 'exa'` is selected, search calls go straight to api.exa.ai.
*/
exa_api_key?: string;
/**
* Websites the assistant may open/read (web_fetch / curl). Exact hosts
* match their subdomains; `"*"` allows all public sites; an empty list
* blocks all web access.
*/
allowed_domains?: string[];
/**
* "Allow all sites" toggle. true → allowlist becomes `["*"]`.
* NOTE: `allow_all` is applied AFTER `allowed_domains` server-side, so when
* both are sent in one patch `allow_all` wins (true → `["*"]`, false → the
* `"*"` wildcard is dropped). Don't send both with conflicting intent.
*/
allow_all?: boolean;
}
export interface SearchSettings {
engine: SearchEngineId | string;
effective_engine: SearchEngineId;
max_results: number;
timeout_secs: number;
parallel_configured: boolean;
brave_configured: boolean;
querit_configured: boolean;
exa_configured: boolean;
/** Current allowed-websites host list (may contain `"*"`). */
allowed_domains: string[];
/** True when the allowlist contains the `"*"` wildcard. */
allow_all: boolean;
}
export interface DiagramViewerSettings {
enabled: boolean;
source_url: string;
refresh_interval_seconds: number;
}
export interface DashboardSettings {
diagram_viewer: DiagramViewerSettings;
}
export async function openhumanGetDashboardSettings(): Promise<CommandResponse<DashboardSettings>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<DashboardSettings>>({
method: CORE_RPC_METHODS.configGetDashboardSettings,
});
}
export async function openhumanGetSearchSettings(): Promise<CommandResponse<SearchSettings>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<SearchSettings>>({
method: CORE_RPC_METHODS.configGetSearchSettings,
});
}
export async function openhumanUpdateSearchSettings(
update: SearchSettingsUpdate
): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
method: CORE_RPC_METHODS.configUpdateSearchSettings,
params: update,
});
}
export interface ComposioTriggerSettingsUpdate {
triage_disabled?: boolean | null;
triage_disabled_toolkits?: string[] | null;
}
export interface ComposioTriggerSettings {
triage_disabled: boolean;
triage_disabled_toolkits: string[];
}
export async function openhumanUpdateComposioTriggerSettings(
update: ComposioTriggerSettingsUpdate
): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
try {
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
method: 'openhuman.config_update_composio_trigger_settings',
params: update,
});
} catch (err) {
if (tauriErrorMessage(err).includes('unknown method')) {
// Stale core sidecar predates composio trigger settings (#1597).
log(
'[composio:rpc] graceful degradation: stale core lacks config_update_composio_trigger_settings (#1597)'
);
return { result: { config: {}, workspace_dir: '', config_path: '' }, logs: [] };
}
throw err;
}
}
export async function openhumanGetComposioTriggerSettings(): Promise<
CommandResponse<ComposioTriggerSettings>
> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
try {
return await callCoreRpc<CommandResponse<ComposioTriggerSettings>>({
method: 'openhuman.config_get_composio_trigger_settings',
});
} catch (err) {
if (tauriErrorMessage(err).includes('unknown method')) {
// Stale core sidecar predates composio trigger settings (#1597).
log(
'[composio:rpc] graceful degradation: stale core lacks config_get_composio_trigger_settings (#1597)'
);
return { result: { triage_disabled: false, triage_disabled_toolkits: [] }, logs: [] };
}
throw err;
}
}
export async function openhumanGetRuntimeFlags(): Promise<CommandResponse<RuntimeFlags>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<RuntimeFlags>>({
method: CORE_RPC_METHODS.configGetRuntimeFlags,
});
}
export async function openhumanSetBrowserAllowAll(
enabled: boolean
): Promise<CommandResponse<RuntimeFlags>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<RuntimeFlags>>({
method: CORE_RPC_METHODS.configSetBrowserAllowAll,
params: { enabled },
});
}
export async function aiGetConfig(): Promise<AIPreview> {
return {
soul: {
raw: '',
name: 'OpenHuman',
description: 'Agent',
personalityPreview: [],
safetyRulesPreview: [],
loadedAt: Date.now(),
},
tools: { raw: '', totalTools: 0, activeSkills: 0, skillsPreview: [], loadedAt: Date.now() },
metadata: {
loadedAt: Date.now(),
loadingDuration: 0,
hasFallbacks: true,
sources: { soul: 'frontend', tools: 'frontend' },
errors: ['AI prompt preview has been moved out of the Tauri host.'],
},
};
}
export async function aiRefreshConfig(): Promise<AIPreview> {
return aiGetConfig();
}