feat(omo): add OMO Slim (oh-my-opencode-slim) support

Implement full OMO Slim profile management to align with ai-toolbox:
- Backend: Slim service methods, DAO, Tauri commands, plugin conflict handling
- Frontend: types, API, query hooks, form integration with isSlim parameterization
- Slim variant: 6 agents (no categories), separate config file and plugin name
- Mutual exclusion: standard OMO and Slim cannot coexist as plugins
- i18n: zh/en/ja translations for all Slim agent descriptions
This commit is contained in:
Jason
2026-02-19 20:47:55 +08:00
parent 51476953ae
commit 8e219b5eb1
26 changed files with 1176 additions and 165 deletions
@@ -23,6 +23,7 @@ interface OmoCommonConfigEditorProps {
onGlobalConfigStateChange: (config: OmoGlobalConfig) => void;
globalConfigRef: React.RefObject<OmoGlobalConfigFieldsRef | null>;
fieldsKey: number;
isSlim?: boolean;
}
export function OmoCommonConfigEditor({
@@ -37,6 +38,7 @@ export function OmoCommonConfigEditor({
onGlobalConfigStateChange,
globalConfigRef,
fieldsKey,
isSlim = false,
}: OmoCommonConfigEditorProps) {
const { t } = useTranslation();
const [isDarkMode, setIsDarkMode] = useState(false);
@@ -153,6 +155,7 @@ export function OmoCommonConfigEditor({
ref={globalConfigRef as React.Ref<OmoGlobalConfigFieldsRef>}
onStateChange={onGlobalConfigStateChange}
hideSaveButtons
isSlim={isSlim}
/>
</div>
</FullScreenPanel>
@@ -41,10 +41,11 @@ import {
} from "lucide-react";
import { cn } from "@/lib/utils";
import { toast } from "sonner";
import { useReadOmoLocalFile } from "@/lib/query/omo";
import { useReadOmoLocalFile, useReadOmoSlimLocalFile } from "@/lib/query/omo";
import {
OMO_BUILTIN_AGENTS,
OMO_BUILTIN_CATEGORIES,
OMO_SLIM_BUILTIN_AGENTS,
type OmoAgentDef,
type OmoCategoryDef,
} from "@/types/omo";
@@ -69,12 +70,13 @@ interface OmoFormFieldsProps {
>;
agents: Record<string, Record<string, unknown>>;
onAgentsChange: (agents: Record<string, Record<string, unknown>>) => void;
categories: Record<string, Record<string, unknown>>;
onCategoriesChange: (
categories?: Record<string, Record<string, unknown>>;
onCategoriesChange?: (
categories: Record<string, Record<string, unknown>>,
) => void;
otherFieldsStr: string;
onOtherFieldsStrChange: (value: string) => void;
isSlim?: boolean;
}
export type CustomModelItem = {
@@ -121,6 +123,9 @@ function DeferredKeyInput({
}
const BUILTIN_AGENT_KEYS = new Set(OMO_BUILTIN_AGENTS.map((a) => a.key));
const BUILTIN_AGENT_KEYS_SLIM = new Set(
OMO_SLIM_BUILTIN_AGENTS.map((a) => a.key),
);
const BUILTIN_CATEGORY_KEYS = new Set(OMO_BUILTIN_CATEGORIES.map((c) => c.key));
const EMPTY_VARIANT_VALUE = "__cc_switch_omo_variant_empty__";
@@ -303,13 +308,21 @@ export function OmoFormFields({
presetMetaMap: _presetMetaMap = {},
agents,
onAgentsChange,
categories,
categories = {},
onCategoriesChange,
otherFieldsStr,
onOtherFieldsStrChange,
isSlim = false,
}: OmoFormFieldsProps) {
const { t } = useTranslation();
const builtinAgentDefs = isSlim
? OMO_SLIM_BUILTIN_AGENTS
: OMO_BUILTIN_AGENTS;
const builtinAgentKeys = isSlim
? BUILTIN_AGENT_KEYS_SLIM
: BUILTIN_AGENT_KEYS;
const [mainAgentsOpen, setMainAgentsOpen] = useState(true);
const [subAgentsOpen, setSubAgentsOpen] = useState(true);
const [categoriesOpen, setCategoriesOpen] = useState(true);
@@ -329,7 +342,7 @@ export function OmoFormFields({
>({});
const [customAgents, setCustomAgents] = useState<CustomModelItem[]>(() =>
collectCustomModels(agents, BUILTIN_AGENT_KEYS),
collectCustomModels(agents, builtinAgentKeys),
);
const [customCategories, setCustomCategories] = useState<CustomModelItem[]>(
@@ -337,7 +350,7 @@ export function OmoFormFields({
);
useEffect(() => {
setCustomAgents(collectCustomModels(agents, BUILTIN_AGENT_KEYS));
setCustomAgents(collectCustomModels(agents, builtinAgentKeys));
}, [agents]);
useEffect(() => {
@@ -349,17 +362,18 @@ export function OmoFormFields({
onAgentsChange(
mergeCustomModelsIntoStore(
agents,
BUILTIN_AGENT_KEYS,
builtinAgentKeys,
customs,
modelVariantsMap,
),
);
},
[agents, onAgentsChange, modelVariantsMap],
[agents, onAgentsChange, modelVariantsMap, builtinAgentKeys],
);
const syncCustomCategories = useCallback(
(customs: CustomModelItem[]) => {
if (!onCategoriesChange) return;
onCategoriesChange(
mergeCustomModelsIntoStore(
categories,
@@ -709,7 +723,7 @@ export function OmoFormFields({
}
const updatedAgents = { ...agents };
for (const agentDef of OMO_BUILTIN_AGENTS) {
for (const agentDef of builtinAgentDefs) {
const recommendedValue = resolveRecommendedModel(agentDef.recommended);
if (recommendedValue && !updatedAgents[agentDef.key]?.model) {
updatedAgents[agentDef.key] = {
@@ -720,30 +734,35 @@ export function OmoFormFields({
}
onAgentsChange(updatedAgents);
const updatedCategories = { ...categories };
for (const catDef of OMO_BUILTIN_CATEGORIES) {
const recommendedValue = resolveRecommendedModel(catDef.recommended);
if (recommendedValue && !updatedCategories[catDef.key]?.model) {
updatedCategories[catDef.key] = {
...updatedCategories[catDef.key],
model: recommendedValue,
};
if (!isSlim && onCategoriesChange) {
const updatedCategories = { ...categories };
for (const catDef of OMO_BUILTIN_CATEGORIES) {
const recommendedValue = resolveRecommendedModel(catDef.recommended);
if (recommendedValue && !updatedCategories[catDef.key]?.model) {
updatedCategories[catDef.key] = {
...updatedCategories[catDef.key],
model: recommendedValue,
};
}
}
onCategoriesChange(updatedCategories);
}
onCategoriesChange(updatedCategories);
};
const configuredAgentCount = Object.keys(agents).length;
const configuredCategoryCount = Object.keys(categories).length;
const mainAgents = OMO_BUILTIN_AGENTS.filter((a) => a.group === "main");
const subAgents = OMO_BUILTIN_AGENTS.filter((a) => a.group === "sub");
const configuredCategoryCount = isSlim ? 0 : Object.keys(categories).length;
const mainAgents = builtinAgentDefs.filter((a) => a.group === "main");
const subAgents = builtinAgentDefs.filter((a) => a.group === "sub");
const readLocalFile = useReadOmoLocalFile();
const readSlimLocalFile = useReadOmoSlimLocalFile();
const [localFilePath, setLocalFilePath] = useState<string | null>(null);
const handleImportFromLocal = useCallback(async () => {
try {
const data = await readLocalFile.mutateAsync();
const data = isSlim
? await readSlimLocalFile.mutateAsync()
: await readLocalFile.mutateAsync();
const importedAgents =
(data.agents as Record<string, Record<string, unknown>> | undefined) ||
{};
@@ -753,16 +772,20 @@ export function OmoFormFields({
| undefined) || {};
onAgentsChange(importedAgents);
onCategoriesChange(importedCategories);
if (!isSlim && onCategoriesChange) {
onCategoriesChange(importedCategories);
}
onOtherFieldsStrChange(
data.otherFields ? JSON.stringify(data.otherFields, null, 2) : "",
);
setAgentAdvancedDrafts({});
setCategoryAdvancedDrafts({});
setCustomAgents(collectCustomModels(importedAgents, BUILTIN_AGENT_KEYS));
setCustomCategories(
collectCustomModels(importedCategories, BUILTIN_CATEGORY_KEYS),
);
setCustomAgents(collectCustomModels(importedAgents, builtinAgentKeys));
if (!isSlim) {
setCustomCategories(
collectCustomModels(importedCategories, BUILTIN_CATEGORY_KEYS),
);
}
setLocalFilePath(data.filePath);
toast.success(
t("omo.importLocalReplaceSuccess", {
@@ -792,7 +815,7 @@ export function OmoFormFields({
) => {
const isAgent = scope === "agent";
const store = isAgent ? agents : categories;
const setter = isAgent ? onAgentsChange : onCategoriesChange;
const setter = isAgent ? onAgentsChange : onCategoriesChange!;
const drafts = isAgent ? agentAdvancedDrafts : categoryAdvancedDrafts;
const expanded = isAgent ? expandedAgents : expandedCategories;
@@ -866,7 +889,7 @@ export function OmoFormFields({
) => {
const isAgent = scope === "agent";
const store = isAgent ? agents : categories;
const setter = isAgent ? onAgentsChange : onCategoriesChange;
const setter = isAgent ? onAgentsChange : onCategoriesChange!;
const drafts = isAgent ? agentAdvancedDrafts : categoryAdvancedDrafts;
const expanded = isAgent ? expandedAgents : expandedCategories;
const customs = isAgent ? customAgents : customCategories;
@@ -1153,30 +1176,31 @@ export function OmoFormFields({
),
})}
{renderModelSection({
title: t("omo.categories", { defaultValue: "Categories" }),
isOpen: categoriesOpen,
onToggle: () => setCategoriesOpen(!categoriesOpen),
badge: `${OMO_BUILTIN_CATEGORIES.length + customCategories.length}`,
action: renderCustomAddButton(() => addCustomModel("category")),
children: (
<>
{OMO_BUILTIN_CATEGORIES.map(renderCategoryRow)}
{customCategories.length > 0 && (
<>
{renderCustomDivider(
t("omo.customCategories", {
defaultValue: "Custom Categories",
}),
)}
{customCategories.map((c, i) =>
renderCustomModelRow("category", c, i),
)}
</>
)}
</>
),
})}
{!isSlim &&
renderModelSection({
title: t("omo.categories", { defaultValue: "Categories" }),
isOpen: categoriesOpen,
onToggle: () => setCategoriesOpen(!categoriesOpen),
badge: `${OMO_BUILTIN_CATEGORIES.length + customCategories.length}`,
action: renderCustomAddButton(() => addCustomModel("category")),
children: (
<>
{OMO_BUILTIN_CATEGORIES.map(renderCategoryRow)}
{customCategories.length > 0 && (
<>
{renderCustomDivider(
t("omo.customCategories", {
defaultValue: "Custom Categories",
}),
)}
{customCategories.map((c, i) =>
renderCustomModelRow("category", c, i),
)}
</>
)}
</>
),
})}
{renderModelSection({
title: t("omo.otherFieldsJson", {
@@ -40,11 +40,18 @@ import {
OMO_BACKGROUND_TASK_PLACEHOLDER,
OMO_BROWSER_AUTOMATION_PLACEHOLDER,
OMO_CLAUDE_CODE_PLACEHOLDER,
OMO_SLIM_DISABLEABLE_AGENTS,
OMO_SLIM_DISABLEABLE_MCPS,
OMO_SLIM_DISABLEABLE_HOOKS,
OMO_SLIM_DEFAULT_SCHEMA_URL,
} from "@/types/omo";
import {
useOmoGlobalConfig,
useSaveOmoGlobalConfig,
useReadOmoLocalFile,
useOmoSlimGlobalConfig,
useSaveOmoSlimGlobalConfig,
useReadOmoSlimLocalFile,
} from "@/lib/query/omo";
interface PresetOption {
@@ -61,6 +68,7 @@ export interface OmoGlobalConfigFieldsRef {
interface OmoGlobalConfigFieldsProps {
onStateChange?: (config: OmoGlobalConfig) => void;
hideSaveButtons?: boolean;
isSlim?: boolean;
}
type OmoAdvancedFieldKey =
@@ -114,6 +122,11 @@ const OMO_ADVANCED_JSON_FIELDS: ReadonlyArray<{
},
];
const OMO_SLIM_ADVANCED_KEYS: ReadonlySet<OmoAdvancedFieldKey> = new Set([
"lspStr",
"experimentalStr",
]);
function TagListEditor({
label,
values,
@@ -310,12 +323,25 @@ function JsonTextareaField({
export const OmoGlobalConfigFields = forwardRef<
OmoGlobalConfigFieldsRef,
OmoGlobalConfigFieldsProps
>(function OmoGlobalConfigFields({ onStateChange, hideSaveButtons }, ref) {
>(function OmoGlobalConfigFields(
{ onStateChange, hideSaveButtons, isSlim = false },
ref,
) {
const { t } = useTranslation();
const { data: config } = useOmoGlobalConfig();
const saveMutation = useSaveOmoGlobalConfig();
const { data: standardConfig } = useOmoGlobalConfig(!isSlim);
const { data: slimConfig } = useOmoSlimGlobalConfig(isSlim);
const config = isSlim ? slimConfig : standardConfig;
const standardSaveMutation = useSaveOmoGlobalConfig();
const slimSaveMutation = useSaveOmoSlimGlobalConfig();
const saveMutation = isSlim ? slimSaveMutation : standardSaveMutation;
const standardReadLocal = useReadOmoLocalFile();
const slimReadLocal = useReadOmoSlimLocalFile();
const [schemaUrl, setSchemaUrl] = useState(OMO_DEFAULT_SCHEMA_URL);
const defaultSchemaUrl = isSlim
? OMO_SLIM_DEFAULT_SCHEMA_URL
: OMO_DEFAULT_SCHEMA_URL;
const [schemaUrl, setSchemaUrl] = useState(defaultSchemaUrl);
const [sisyphusAgentStr, setSisyphusAgentStr] = useState("");
const [disabledAgents, setDisabledAgents] = useState<string[]>([]);
const [disabledMcps, setDisabledMcps] = useState<string[]>([]);
@@ -330,7 +356,7 @@ export const OmoGlobalConfigFields = forwardRef<
const [loaded, setLoaded] = useState(false);
const applyGlobalState = useCallback((global: OmoGlobalConfig) => {
setSchemaUrl(global.schemaUrl || OMO_DEFAULT_SCHEMA_URL);
setSchemaUrl(global.schemaUrl || defaultSchemaUrl);
setSisyphusAgentStr(
global.sisyphusAgent ? JSON.stringify(global.sisyphusAgent, null, 2) : "",
);
@@ -545,7 +571,7 @@ export const OmoGlobalConfigFields = forwardRef<
placeholder: t("omo.disabledAgentsPlaceholder", {
defaultValue: "Disabled Agents",
}),
presets: OMO_DISABLEABLE_AGENTS,
presets: isSlim ? OMO_SLIM_DISABLEABLE_AGENTS : OMO_DISABLEABLE_AGENTS,
},
{
key: "mcps",
@@ -555,7 +581,7 @@ export const OmoGlobalConfigFields = forwardRef<
placeholder: t("omo.disabledMcpsPlaceholder", {
defaultValue: "Disabled MCPs",
}),
presets: OMO_DISABLEABLE_MCPS,
presets: isSlim ? OMO_SLIM_DISABLEABLE_MCPS : OMO_DISABLEABLE_MCPS,
},
{
key: "hooks",
@@ -565,21 +591,25 @@ export const OmoGlobalConfigFields = forwardRef<
placeholder: t("omo.disabledHooksPlaceholder", {
defaultValue: "Disabled Hooks",
}),
presets: OMO_DISABLEABLE_HOOKS,
presets: isSlim ? OMO_SLIM_DISABLEABLE_HOOKS : OMO_DISABLEABLE_HOOKS,
},
{
key: "skills",
label: t("omo.disabledSkills", { defaultValue: "Skills" }),
values: disabledSkills,
onChange: setDisabledSkills,
placeholder: t("omo.disabledSkillsPlaceholder", {
defaultValue: "Disabled Skills",
}),
presets: OMO_DISABLEABLE_SKILLS,
},
] as const;
...(!isSlim
? [
{
key: "skills" as const,
label: t("omo.disabledSkills", { defaultValue: "Skills" }),
values: disabledSkills,
onChange: setDisabledSkills,
placeholder: t("omo.disabledSkillsPlaceholder", {
defaultValue: "Disabled Skills",
}),
presets: OMO_DISABLEABLE_SKILLS,
},
]
: []),
];
const readLocalFile = useReadOmoLocalFile();
const readLocalFile = isSlim ? slimReadLocal : standardReadLocal;
const handleImportGlobalFromLocal = useCallback(async () => {
try {
@@ -668,25 +698,27 @@ export const OmoGlobalConfigFields = forwardRef<
<Input
value={schemaUrl}
onChange={(e) => setSchemaUrl(e.target.value)}
placeholder={OMO_DEFAULT_SCHEMA_URL}
placeholder={defaultSchemaUrl}
className="text-sm h-8"
/>
</div>
<div className="rounded-md border border-border/40 bg-muted/10 p-2 space-y-2">
<Label className="text-sm font-semibold">
{t("omo.sisyphusAgentConfig", {
defaultValue: "Sisyphus Agent",
})}
</Label>
<Textarea
value={sisyphusAgentStr}
onChange={(e) => setSisyphusAgentStr(e.target.value)}
placeholder={OMO_SISYPHUS_AGENT_PLACEHOLDER}
className="font-mono text-sm"
style={{ minHeight: "140px" }}
/>
</div>
{!isSlim && (
<div className="rounded-md border border-border/40 bg-muted/10 p-2 space-y-2">
<Label className="text-sm font-semibold">
{t("omo.sisyphusAgentConfig", {
defaultValue: "Sisyphus Agent",
})}
</Label>
<Textarea
value={sisyphusAgentStr}
onChange={(e) => setSisyphusAgentStr(e.target.value)}
placeholder={OMO_SISYPHUS_AGENT_PLACEHOLDER}
className="font-mono text-sm"
style={{ minHeight: "140px" }}
/>
</div>
)}
<div className="rounded-md border border-border/40 bg-muted/10 p-2 space-y-3">
<div className="flex items-center gap-2">
@@ -715,7 +747,9 @@ export const OmoGlobalConfigFields = forwardRef<
<Label className="text-sm font-semibold">
{t("omo.advanced", { defaultValue: "Advanced Settings" })}
</Label>
{OMO_ADVANCED_JSON_FIELDS.map((field) => (
{OMO_ADVANCED_JSON_FIELDS.filter(
(field) => !isSlim || OMO_SLIM_ADVANCED_KEYS.has(field.key),
).map((field) => (
<JsonTextareaField
key={field.key}
label={t(field.labelKey, { defaultValue: field.defaultLabel })}
+46 -21
View File
@@ -78,7 +78,7 @@ import {
useOmoDraftState,
useOpenclawFormState,
} from "./hooks";
import { useOmoGlobalConfig } from "@/lib/query/omo";
import { useOmoGlobalConfig, useOmoSlimGlobalConfig } from "@/lib/query/omo";
import {
CLAUDE_DEFAULT_CONFIG,
CODEX_DEFAULT_CONFIG,
@@ -184,7 +184,15 @@ export function ProviderForm({
initialCategory: initialData?.category,
});
const isOmoCategory = appId === "opencode" && category === "omo";
const { data: queriedOmoGlobalConfig } = useOmoGlobalConfig(isOmoCategory);
const isOmoSlimCategory = appId === "opencode" && category === "omo-slim";
const isAnyOmoCategory = isOmoCategory || isOmoSlimCategory;
const { data: queriedStandardOmoGlobalConfig } =
useOmoGlobalConfig(isOmoCategory);
const { data: queriedSlimOmoGlobalConfig } =
useOmoSlimGlobalConfig(isOmoSlimCategory);
const queriedOmoGlobalConfig = isOmoSlimCategory
? queriedSlimOmoGlobalConfig
: queriedStandardOmoGlobalConfig;
useEffect(() => {
setSelectedPresetId(initialData ? null : "custom");
@@ -495,7 +503,7 @@ export function ProviderForm({
omoModelVariantsMap,
omoPresetMetaMap,
existingOpencodeKeys,
} = useOmoModelSource({ isOmoCategory, providerId });
} = useOmoModelSource({ isOmoCategory: isAnyOmoCategory, providerId });
const opencodeForm = useOpencodeFormState({
initialData,
@@ -506,7 +514,8 @@ export function ProviderForm({
});
const initialOmoSettings =
appId === "opencode" && initialData?.category === "omo"
appId === "opencode" &&
(initialData?.category === "omo" || initialData?.category === "omo-slim")
? (initialData.settingsConfig as Record<string, unknown> | undefined)
: undefined;
@@ -677,13 +686,19 @@ export function ProviderForm({
} catch (err) {
settingsConfig = values.settingsConfig.trim();
}
} else if (appId === "opencode" && category === "omo") {
} else if (
appId === "opencode" &&
(category === "omo" || category === "omo-slim")
) {
const omoConfig: Record<string, unknown> = {};
omoConfig.useCommonConfig = omoDraft.useOmoCommonConfig;
if (Object.keys(omoDraft.omoAgents).length > 0) {
omoConfig.agents = omoDraft.omoAgents;
}
if (Object.keys(omoDraft.omoCategories).length > 0) {
if (
category === "omo" &&
Object.keys(omoDraft.omoCategories).length > 0
) {
omoConfig.categories = omoDraft.omoCategories;
}
if (omoDraft.omoOtherFieldsStr.trim()) {
@@ -1334,19 +1349,25 @@ export function ProviderForm({
/>
)}
{appId === "opencode" && category === "omo" && (
<OmoFormFields
modelOptions={omoModelOptions}
modelVariantsMap={omoModelVariantsMap}
presetMetaMap={omoPresetMetaMap}
agents={omoDraft.omoAgents}
onAgentsChange={omoDraft.setOmoAgents}
categories={omoDraft.omoCategories}
onCategoriesChange={omoDraft.setOmoCategories}
otherFieldsStr={omoDraft.omoOtherFieldsStr}
onOtherFieldsStrChange={omoDraft.setOmoOtherFieldsStr}
/>
)}
{appId === "opencode" &&
(category === "omo" || category === "omo-slim") && (
<OmoFormFields
modelOptions={omoModelOptions}
modelVariantsMap={omoModelVariantsMap}
presetMetaMap={omoPresetMetaMap}
agents={omoDraft.omoAgents}
onAgentsChange={omoDraft.setOmoAgents}
categories={
category === "omo" ? omoDraft.omoCategories : undefined
}
onCategoriesChange={
category === "omo" ? omoDraft.setOmoCategories : undefined
}
otherFieldsStr={omoDraft.omoOtherFieldsStr}
onOtherFieldsStrChange={omoDraft.setOmoOtherFieldsStr}
isSlim={category === "omo-slim"}
/>
)}
{/* OpenClaw 专属字段 */}
{appId === "openclaw" && (
@@ -1408,7 +1429,8 @@ export function ProviderForm({
/>
{settingsConfigErrorField}
</>
) : appId === "opencode" && category === "omo" ? (
) : appId === "opencode" &&
(category === "omo" || category === "omo-slim") ? (
<OmoCommonConfigEditor
previewValue={omoDraft.mergedOmoJsonPreview}
useCommonConfig={omoDraft.useOmoCommonConfig}
@@ -1421,8 +1443,11 @@ export function ProviderForm({
onGlobalConfigStateChange={omoDraft.setOmoGlobalState}
globalConfigRef={omoDraft.omoGlobalConfigRef}
fieldsKey={omoDraft.omoFieldsKey}
isSlim={category === "omo-slim"}
/>
) : appId === "opencode" && category !== "omo" ? (
) : appId === "opencode" &&
category !== "omo" &&
category !== "omo-slim" ? (
<>
<div className="space-y-2">
<Label htmlFor="settingsConfig">{t("provider.configJson")}</Label>
@@ -2,7 +2,11 @@ import { useState, useCallback, useEffect, useRef, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
import type { OmoGlobalConfig } from "@/types/omo";
import { mergeOmoConfigPreview } from "@/types/omo";
import {
mergeOmoConfigPreview,
mergeOmoSlimConfigPreview,
buildOmoSlimProfilePreview,
} from "@/types/omo";
import { type OmoGlobalConfigFieldsRef } from "../OmoGlobalConfigFields";
import * as configApi from "@/lib/api/config";
import {
@@ -54,6 +58,8 @@ export function useOmoDraftState({
category,
}: UseOmoDraftStateParams): OmoDraftState {
const { t } = useTranslation();
const isSlim = category === "omo-slim";
const commonConfigKey = isSlim ? "omo_slim" : "omo";
const [omoAgents, setOmoAgents] = useState<
Record<string, Record<string, unknown>>
@@ -93,6 +99,14 @@ export function useOmoDraftState({
const mergedOmoJsonPreview = useMemo(() => {
if (useOmoCommonConfig) {
if (isSlim) {
const merged = mergeOmoSlimConfigPreview(
effectiveOmoGlobalConfig,
omoAgents,
omoOtherFieldsStr,
);
return JSON.stringify(merged, null, 2);
}
const merged = mergeOmoConfigPreview(
effectiveOmoGlobalConfig,
omoAgents,
@@ -101,6 +115,13 @@ export function useOmoDraftState({
);
return JSON.stringify(merged, null, 2);
} else {
if (isSlim) {
return JSON.stringify(
buildOmoSlimProfilePreview(omoAgents, omoOtherFieldsStr),
null,
2,
);
}
return JSON.stringify(
buildOmoProfilePreview(omoAgents, omoCategories, omoOtherFieldsStr),
null,
@@ -113,16 +134,22 @@ export function useOmoDraftState({
omoAgents,
omoCategories,
omoOtherFieldsStr,
isSlim,
]);
// Auto-detect whether common config has content for new OMO profiles
// Auto-detect whether common config has content for new OMO/OMO Slim profiles
useEffect(() => {
if (appId !== "opencode" || category !== "omo" || isEditMode) return;
if (
appId !== "opencode" ||
(category !== "omo" && category !== "omo-slim") ||
isEditMode
)
return;
let active = true;
(async () => {
let next = false;
try {
const raw = await configApi.getCommonConfigSnippet("omo");
const raw = await configApi.getCommonConfigSnippet(commonConfigKey);
if (raw) {
const parsed = JSON.parse(raw) as Record<string, unknown>;
next = Object.keys(parsed).some(
@@ -135,14 +162,17 @@ export function useOmoDraftState({
return () => {
active = false;
};
}, [appId, category, isEditMode]);
}, [appId, category, isEditMode, commonConfigKey]);
const handleOmoGlobalConfigSave = useCallback(async () => {
if (!omoGlobalConfigRef.current) return;
setIsOmoSaving(true);
try {
const config = omoGlobalConfigRef.current.buildCurrentConfigStrict();
await configApi.setCommonConfigSnippet("omo", JSON.stringify(config));
await configApi.setCommonConfigSnippet(
commonConfigKey,
JSON.stringify(config),
);
setIsOmoConfigModalOpen(false);
toast.success(
t("omo.globalConfigSaved", { defaultValue: "Global config saved" }),
@@ -152,7 +182,7 @@ export function useOmoDraftState({
} finally {
setIsOmoSaving(false);
}
}, [t]);
}, [t, commonConfigKey]);
const handleOmoEditClick = useCallback(() => {
setOmoFieldsKey((k) => k + 1);
@@ -133,7 +133,7 @@ export function useOmoModelSource({
const parseFailedProviders: string[] = [];
for (const [providerKey, provider] of Object.entries(allProviders)) {
if (provider.category === "omo") {
if (provider.category === "omo" || provider.category === "omo-slim") {
continue;
}
if (liveSet && !liveSet.has(providerKey)) {