fix(agents): stop falsely reporting saved agent edits on external OpenCode; make model-selector shortcut customizable (#1839)
* feat(shortcuts): make 'Open model selector' shortcut customizable Lets users remap the model selector shortcut (e.g. to Ctrl+M) via Settings > OpenChamber > Shortcuts, matching OpenCode's quick model-switch keybinding workflow. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * fix(agents): surface manual-restart needed when on external OpenCode Agent prompt/permission/settings edits are written to disk, but an external OpenCode server (skip-start or auto-detected on the default port) is not owned by OpenChamber and is only health-probed on config change, so it keeps serving its startup-cached config until restarted. The API previously claimed a successful reload, so the UI silently reverted the edit to the stale/default value on refresh. Now refreshOpenCodeAfterConfigChange reports whether a real reload happened; agent routes return requiresManualRestart for external mode; and the agents UI keeps the saved values and warns the user to restart their OpenCode server instead of showing a false success. Managed mode behavior is unchanged (process is restarted and reload is live). Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> --------- Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
co-authored by
Serhii Dziupin
parent
3e3cd82a47
commit
f4e90ca232
@@ -4,7 +4,7 @@ import { Input } from '@/components/ui/input';
|
||||
import { NumberInput } from '@/components/ui/number-input';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { toast } from '@/components/ui';
|
||||
import { useAgentsStore, type AgentConfig, type AgentScope } from '@/stores/useAgentsStore';
|
||||
import { useAgentsStore, type AgentConfig, type AgentMutationResult, type AgentScope } from '@/stores/useAgentsStore';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useDirectorySync } from '@/sync/sync-context';
|
||||
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
||||
@@ -635,18 +635,22 @@ export const AgentsPage: React.FC = () => {
|
||||
scope: isNewAgent ? draftScope : undefined,
|
||||
};
|
||||
|
||||
let success: boolean;
|
||||
let result: AgentMutationResult;
|
||||
if (isNewAgent) {
|
||||
success = await createAgent(config);
|
||||
if (success) {
|
||||
result = await createAgent(config);
|
||||
if (result.ok) {
|
||||
setAgentDraft(null); // Clear draft after successful creation
|
||||
}
|
||||
} else {
|
||||
success = await updateAgent(agentName, config);
|
||||
result = await updateAgent(agentName, config);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
toast.success(isNewAgent ? t('settings.agents.page.toast.created') : t('settings.agents.page.toast.updated'));
|
||||
if (result.ok) {
|
||||
if (result.requiresManualRestart) {
|
||||
toast.warning(t('settings.agents.page.toast.savedManualRestart'));
|
||||
} else {
|
||||
toast.success(isNewAgent ? t('settings.agents.page.toast.created') : t('settings.agents.page.toast.updated'));
|
||||
}
|
||||
} else {
|
||||
toast.error(isNewAgent ? t('settings.agents.page.toast.createFailed') : t('settings.agents.page.toast.updateFailed'));
|
||||
}
|
||||
|
||||
@@ -182,10 +182,12 @@ export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) =>
|
||||
|
||||
setIsConfirmActionPending(true);
|
||||
try {
|
||||
const success = await deleteAgent(confirmActionAgent.name, (confirmActionAgent as Agent & { scope?: AgentScope }).scope);
|
||||
const result = await deleteAgent(confirmActionAgent.name, (confirmActionAgent as Agent & { scope?: AgentScope }).scope);
|
||||
|
||||
if (success) {
|
||||
if (confirmActionType === 'delete') {
|
||||
if (result.ok) {
|
||||
if (result.requiresManualRestart) {
|
||||
toast.warning(t('settings.agents.page.toast.savedManualRestart'));
|
||||
} else if (confirmActionType === 'delete') {
|
||||
toast.success(t('settings.agents.sidebar.toast.agentDeleted', { name: confirmActionAgent.name }));
|
||||
} else {
|
||||
toast.success(t('settings.agents.sidebar.toast.agentReset', { name: confirmActionAgent.name }));
|
||||
@@ -274,7 +276,7 @@ export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) =>
|
||||
? `${renameDialogAgent.model.providerID}/${renameDialogAgent.model.modelID}`
|
||||
: null;
|
||||
const renameExt = renameDialogAgent as Agent & { scope?: AgentScope; disable?: boolean };
|
||||
const success = await createAgent({
|
||||
const createResult = await createAgent({
|
||||
name: sanitizedName,
|
||||
description: renameDialogAgent.description,
|
||||
model: renameModelStr,
|
||||
@@ -288,11 +290,15 @@ export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) =>
|
||||
scope: renameExt.scope,
|
||||
});
|
||||
|
||||
if (success) {
|
||||
if (createResult.ok) {
|
||||
// Delete old agent
|
||||
const deleteSuccess = await deleteAgent(renameDialogAgent.name, renameExt.scope);
|
||||
if (deleteSuccess) {
|
||||
toast.success(`Agent renamed to "${sanitizedName}"`);
|
||||
const deleteResult = await deleteAgent(renameDialogAgent.name, renameExt.scope);
|
||||
if (deleteResult.ok) {
|
||||
if (createResult.requiresManualRestart || deleteResult.requiresManualRestart) {
|
||||
toast.warning(t('settings.agents.page.toast.savedManualRestart'));
|
||||
} else {
|
||||
toast.success(t('settings.agents.sidebar.toast.agentRenamed', { name: sanitizedName }));
|
||||
}
|
||||
setSelectedAgent(sanitizedName);
|
||||
} else {
|
||||
toast.error(t('settings.agents.sidebar.toast.removeOldAfterRenameFailed'));
|
||||
|
||||
@@ -398,12 +398,14 @@ export const settingsDict = {
|
||||
'settings.agents.sidebar.toast.agentExists': 'An agent with this name already exists',
|
||||
'settings.agents.sidebar.toast.removeOldAfterRenameFailed': 'Failed to remove old agent after rename',
|
||||
'settings.agents.sidebar.toast.renameFailed': 'Failed to rename agent',
|
||||
'settings.agents.sidebar.toast.agentRenamed': 'Agent renamed to "{name}"',
|
||||
'settings.agents.page.toast.permissionNameRequired': 'Permission name is required',
|
||||
'settings.agents.page.toast.created': 'Agent created successfully',
|
||||
'settings.agents.page.toast.updated': 'Agent updated successfully',
|
||||
'settings.agents.page.toast.createFailed': 'Failed to create agent',
|
||||
'settings.agents.page.toast.updateFailed': 'Failed to update agent',
|
||||
'settings.agents.page.toast.saveUnexpectedError': 'An error occurred while saving',
|
||||
'settings.agents.page.toast.savedManualRestart': 'Saved to disk. Restart your connected OpenCode server to apply the changes.',
|
||||
'settings.agents.page.empty.title': 'Select an agent from the sidebar',
|
||||
'settings.agents.page.empty.description': 'or create a new one',
|
||||
'settings.agents.page.title.new': 'New Agent',
|
||||
@@ -959,6 +961,7 @@ export const settingsDict = {
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': 'Cycle agent',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': 'Cycle favorite model forward',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_backward.label': 'Cycle favorite model backward',
|
||||
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': 'Open model selector',
|
||||
'settings.openchamber.keyboardShortcuts.action.expand_input.label': 'Expand input',
|
||||
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': 'Open conversation timeline',
|
||||
'settings.projects.sidebar.total': 'Total {count}',
|
||||
|
||||
@@ -365,12 +365,14 @@ export const settingsDict = {
|
||||
"settings.agents.sidebar.toast.agentExists": "Ya existe un agente con este nombre",
|
||||
"settings.agents.sidebar.toast.removeOldAfterRenameFailed": "No se pudo eliminar el antiguo agente después del cambio de nombre",
|
||||
"settings.agents.sidebar.toast.renameFailed": "No se pudo cambiar el nombre del agente",
|
||||
"settings.agents.sidebar.toast.agentRenamed": "Agente renombrado a \"{name}\"",
|
||||
"settings.agents.page.toast.permissionNameRequired": "Se requiere el nombre del permiso",
|
||||
"settings.agents.page.toast.created": "Agente creado correctamente",
|
||||
"settings.agents.page.toast.updated": "Agente actualizado correctamente",
|
||||
"settings.agents.page.toast.createFailed": "No se pudo crear el agente",
|
||||
"settings.agents.page.toast.updateFailed": "No se pudo actualizar el agente",
|
||||
"settings.agents.page.toast.saveUnexpectedError": "Ocurrió un error al guardar",
|
||||
"settings.agents.page.toast.savedManualRestart": "Guardado en disco. Reinicia tu servidor OpenCode conectado para aplicar los cambios.",
|
||||
"settings.agents.page.empty.title": "Selecciona un agente desde la barra lateral",
|
||||
"settings.agents.page.empty.description": "o crea uno nuevo",
|
||||
"settings.agents.page.title.new": "Nuevo agente",
|
||||
@@ -926,6 +928,7 @@ export const settingsDict = {
|
||||
"settings.openchamber.keyboardShortcuts.action.cycle_agent.label": "Cambiar agente",
|
||||
"settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label": "Siguiente modelo favorito",
|
||||
"settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_backward.label": "Modelo favorito anterior",
|
||||
"settings.openchamber.keyboardShortcuts.action.open_model_selector.label": "Abrir selector de modelo",
|
||||
"settings.openchamber.keyboardShortcuts.action.expand_input.label": "Expandir entrada",
|
||||
"settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label": "Abrir línea de tiempo de conversación",
|
||||
"settings.projects.sidebar.total": "Total {count}",
|
||||
|
||||
@@ -354,12 +354,14 @@ export const settingsDict = {
|
||||
'settings.agents.sidebar.toast.agentExists': 'Un agent portant ce nom existe déjà',
|
||||
'settings.agents.sidebar.toast.removeOldAfterRenameFailed': 'Échec de la suppression de l\'ancien agent après le changement de nom',
|
||||
'settings.agents.sidebar.toast.renameFailed': 'Échec du changement de nom de l\'agent',
|
||||
'settings.agents.sidebar.toast.agentRenamed': 'Agent renommé en "{name}"',
|
||||
'settings.agents.page.toast.permissionNameRequired': 'Le nom de l\'autorisation est requis',
|
||||
'settings.agents.page.toast.created': 'Agent créé avec succès',
|
||||
'settings.agents.page.toast.updated': 'Agent mis à jour avec succès',
|
||||
'settings.agents.page.toast.createFailed': 'Échec de la création de l\'agent',
|
||||
'settings.agents.page.toast.updateFailed': 'Échec de la mise à jour de l\'agent',
|
||||
'settings.agents.page.toast.saveUnexpectedError': 'Une erreur s\'est produite lors de l\'enregistrement',
|
||||
'settings.agents.page.toast.savedManualRestart': 'Enregistré sur le disque. Redémarrez votre serveur OpenCode connecté pour appliquer les modifications.',
|
||||
'settings.agents.page.empty.title': 'Sélectionnez un agent dans la barre latérale',
|
||||
'settings.agents.page.empty.description': 'ou créez-en un nouveau',
|
||||
'settings.agents.page.title.new': 'Nouvel agent',
|
||||
@@ -915,6 +917,7 @@ export const settingsDict = {
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': 'Agent de cycle',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': 'Faire avancer le modèle favori',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_backward.label': 'Faire défiler le modèle favori vers l\'arrière',
|
||||
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': 'Ouvrir le sélecteur de modèle',
|
||||
'settings.openchamber.keyboardShortcuts.action.expand_input.label': 'Développer l\'entrée',
|
||||
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': 'Chronologie de la conversation ouverte',
|
||||
'settings.projects.sidebar.total': 'Total {count}',
|
||||
|
||||
@@ -365,12 +365,14 @@ export const settingsDict = {
|
||||
'settings.agents.sidebar.toast.agentExists': '같은 이름의 에이전트가 이미 존재합니다',
|
||||
'settings.agents.sidebar.toast.removeOldAfterRenameFailed': '이름 변경 후 이전 에이전트를 제거하지 못했습니다',
|
||||
'settings.agents.sidebar.toast.renameFailed': '에이전트 이름을 변경하지 못했습니다',
|
||||
'settings.agents.sidebar.toast.agentRenamed': '에이전트 이름이 "{name}"(으)로 변경되었습니다',
|
||||
'settings.agents.page.toast.permissionNameRequired': '권한 이름은 필수입니다',
|
||||
'settings.agents.page.toast.created': '에이전트를 생성했습니다',
|
||||
'settings.agents.page.toast.updated': '에이전트를 업데이트했습니다',
|
||||
'settings.agents.page.toast.createFailed': '에이전트를 생성하지 못했습니다',
|
||||
'settings.agents.page.toast.updateFailed': '에이전트를 업데이트하지 못했습니다',
|
||||
'settings.agents.page.toast.saveUnexpectedError': '저장 중 오류가 발생했습니다',
|
||||
'settings.agents.page.toast.savedManualRestart': '디스크에 저장되었습니다. 변경 사항을 적용하려면 연결된 OpenCode 서버를 다시 시작하세요.',
|
||||
'settings.agents.page.empty.title': '사이드바에서 에이전트를 선택하세요',
|
||||
'settings.agents.page.empty.description': '또는 새로 생성하세요',
|
||||
'settings.agents.page.title.new': '새 에이전트',
|
||||
@@ -926,6 +928,7 @@ export const settingsDict = {
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': '에이전트 순환',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': '즐겨찾기 모델 앞으로 순환',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_backward.label': '즐겨찾기 모델 뒤로 순환',
|
||||
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': '모델 선택기 열기',
|
||||
'settings.openchamber.keyboardShortcuts.action.expand_input.label': '입력 확장',
|
||||
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': '대화 타임라인 열기',
|
||||
'settings.projects.sidebar.total': '총 {count}개',
|
||||
|
||||
@@ -62,6 +62,7 @@ export const settingsDict = {
|
||||
'settings.agents.page.toast.created': 'Agent został utworzony pomyślnie',
|
||||
'settings.agents.page.toast.permissionNameRequired': 'Nazwa uprawnienia jest wymagana',
|
||||
'settings.agents.page.toast.saveUnexpectedError': 'Wystąpił błąd podczas zapisywania',
|
||||
'settings.agents.page.toast.savedManualRestart': 'Zapisano na dysku. Uruchom ponownie połączony serwer OpenCode, aby zastosować zmiany.',
|
||||
'settings.agents.page.toast.updateFailed': 'Nie udało się zaktualizować agenta',
|
||||
'settings.agents.page.toast.updated': 'Agent został zaktualizowany pomyślnie',
|
||||
'settings.agents.sidebar.badge.system': 'system',
|
||||
@@ -86,6 +87,7 @@ export const settingsDict = {
|
||||
'settings.agents.sidebar.toast.definitionNotFound': 'Nie znaleziono definicji agenta. Nic nie zmieniono.',
|
||||
'settings.agents.sidebar.toast.removeOldAfterRenameFailed': 'Nie udało się usunąć starego agenta po zmianie nazwy',
|
||||
'settings.agents.sidebar.toast.renameFailed': 'Nie udało się zmienić nazwy agenta',
|
||||
'settings.agents.sidebar.toast.agentRenamed': 'Zmieniono nazwę agenta na "{name}"',
|
||||
'settings.agents.sidebar.toast.resetFailed': 'Nie udało się zresetować agenta',
|
||||
'settings.agents.sidebar.total': 'Suma: {count}',
|
||||
'settings.behavior.page.field.systemPromptPlaceholder': 'Jesteś pomocnym asystentem AI...\n\nUżyj tej przestrzeni, aby zdefiniować absolutne reguły zachowania AI we wszystkich sesjach i u dostawców.',
|
||||
@@ -713,6 +715,7 @@ export const settingsDict = {
|
||||
'settings.openchamber.git.showGitignoredAria': 'Wyświetlaj pliki zignorowane przez Git',
|
||||
'settings.openchamber.git.title': 'Preferencje Git',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_backward.label': 'Przełącz ulubiony model wstecz',
|
||||
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': 'Otwórz wybór modelu',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': 'Przełącz ulubiony model w przód',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_right_sidebar_tab.label': 'Przełącz zakładkę prawego paska bocznego',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_services_tab.label': 'Przełącz zakładkę usług',
|
||||
|
||||
@@ -365,12 +365,14 @@ export const settingsDict = {
|
||||
"settings.agents.sidebar.toast.agentExists": "Já existe um agente com este nome",
|
||||
"settings.agents.sidebar.toast.removeOldAfterRenameFailed": "Não foi possível excluir o agente antigo depois da renomeação",
|
||||
"settings.agents.sidebar.toast.renameFailed": "Não foi possível renomear o agente",
|
||||
"settings.agents.sidebar.toast.agentRenamed": "Agente renomeado para \"{name}\"",
|
||||
"settings.agents.page.toast.permissionNameRequired": "É necessário informar o nome da permissão",
|
||||
"settings.agents.page.toast.created": "Agente criado com sucesso",
|
||||
"settings.agents.page.toast.updated": "Agente atualizado com sucesso",
|
||||
"settings.agents.page.toast.createFailed": "Não foi possível criar o agente",
|
||||
"settings.agents.page.toast.updateFailed": "Não foi possível atualizar o agente",
|
||||
"settings.agents.page.toast.saveUnexpectedError": "Ocorreu um erro ao salvar",
|
||||
"settings.agents.page.toast.savedManualRestart": "Salvo no disco. Reinicie o servidor OpenCode conectado para aplicar as alterações.",
|
||||
"settings.agents.page.empty.title": "Selecione um agente da barra lateral",
|
||||
"settings.agents.page.empty.description": "ou crie um novo",
|
||||
"settings.agents.page.title.new": "Novo agente",
|
||||
@@ -926,6 +928,7 @@ export const settingsDict = {
|
||||
"settings.openchamber.keyboardShortcuts.action.cycle_agent.label": "Alternar agente",
|
||||
"settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label": "Próximo modelo favorito",
|
||||
"settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_backward.label": "Modelo favorito anterior",
|
||||
"settings.openchamber.keyboardShortcuts.action.open_model_selector.label": "Abrir seletor de modelo",
|
||||
"settings.openchamber.keyboardShortcuts.action.expand_input.label": "Expandir entrada",
|
||||
"settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label": "Abrir linha do tempo da conversa",
|
||||
"settings.projects.sidebar.total": "Total {count}",
|
||||
|
||||
@@ -365,12 +365,14 @@ export const settingsDict = {
|
||||
"settings.agents.sidebar.toast.agentExists": "Агент із такою назвою вже існує",
|
||||
"settings.agents.sidebar.toast.removeOldAfterRenameFailed": "Не вдалося видалити старого агента після перейменування",
|
||||
"settings.agents.sidebar.toast.renameFailed": "Не вдалося перейменувати агента",
|
||||
"settings.agents.sidebar.toast.agentRenamed": "Агента перейменовано на \"{name}\"",
|
||||
"settings.agents.page.toast.permissionNameRequired": "Потрібна назва дозволу",
|
||||
"settings.agents.page.toast.created": "Агент створено успішно",
|
||||
"settings.agents.page.toast.updated": "Агент успішно оновлено",
|
||||
"settings.agents.page.toast.createFailed": "Не вдалося створити агента",
|
||||
"settings.agents.page.toast.updateFailed": "Не вдалося оновити агента",
|
||||
"settings.agents.page.toast.saveUnexpectedError": "Під час збереження сталася помилка",
|
||||
"settings.agents.page.toast.savedManualRestart": "Збережено на диск. Перезапустіть підключений сервер OpenCode, щоб застосувати зміни.",
|
||||
"settings.agents.page.empty.title": "Виберіть агента на бічній панелі",
|
||||
"settings.agents.page.empty.description": "або створіть нового",
|
||||
"settings.agents.page.title.new": "Новий агент",
|
||||
@@ -926,6 +928,7 @@ export const settingsDict = {
|
||||
"settings.openchamber.keyboardShortcuts.action.cycle_agent.label": "Перемкнути агента",
|
||||
"settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label": "Перемкнути улюблену модель вперед",
|
||||
"settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_backward.label": "Перемкнути улюблену модель назад",
|
||||
"settings.openchamber.keyboardShortcuts.action.open_model_selector.label": "Відкрити вибір моделі",
|
||||
"settings.openchamber.keyboardShortcuts.action.expand_input.label": "Розгорнути введення",
|
||||
"settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label": "Відкрити хронологію розмови",
|
||||
"settings.projects.sidebar.total": "Усього {count}",
|
||||
|
||||
@@ -365,12 +365,14 @@ export const settingsDict = {
|
||||
'settings.agents.sidebar.toast.agentExists': '已存在同名智能体',
|
||||
'settings.agents.sidebar.toast.removeOldAfterRenameFailed': '重命名后移除旧智能体失败',
|
||||
'settings.agents.sidebar.toast.renameFailed': '重命名智能体失败',
|
||||
'settings.agents.sidebar.toast.agentRenamed': '智能体已重命名为 "{name}"',
|
||||
'settings.agents.page.toast.permissionNameRequired': '权限名称为必填项',
|
||||
'settings.agents.page.toast.created': '智能体创建成功',
|
||||
'settings.agents.page.toast.updated': '智能体更新成功',
|
||||
'settings.agents.page.toast.createFailed': '创建智能体失败',
|
||||
'settings.agents.page.toast.updateFailed': '更新智能体失败',
|
||||
'settings.agents.page.toast.saveUnexpectedError': '保存时发生错误',
|
||||
'settings.agents.page.toast.savedManualRestart': '已保存到磁盘。请重启已连接的 OpenCode 服务器以应用更改。',
|
||||
'settings.agents.page.empty.title': '从侧边栏选择一个智能体',
|
||||
'settings.agents.page.empty.description': '或新建一个智能体',
|
||||
'settings.agents.page.title.new': '新建智能体',
|
||||
@@ -926,6 +928,7 @@ export const settingsDict = {
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': '轮换智能体',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': '向前轮换收藏模型',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_backward.label': '向后轮换收藏模型',
|
||||
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': '打开模型选择器',
|
||||
'settings.openchamber.keyboardShortcuts.action.expand_input.label': '展开输入框',
|
||||
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': '打开对话时间线',
|
||||
'settings.projects.sidebar.total': '总计 {count}',
|
||||
|
||||
@@ -362,12 +362,14 @@
|
||||
'settings.agents.sidebar.toast.agentExists': '已存在同名 agent',
|
||||
'settings.agents.sidebar.toast.removeOldAfterRenameFailed': '重新命名後移除舊 agent 失敗',
|
||||
'settings.agents.sidebar.toast.renameFailed': '重新命名 agent 失敗',
|
||||
'settings.agents.sidebar.toast.agentRenamed': 'Agent 已重新命名為 "{name}"',
|
||||
'settings.agents.page.toast.permissionNameRequired': '權限名稱為必填項',
|
||||
'settings.agents.page.toast.created': 'Agent 建立成功',
|
||||
'settings.agents.page.toast.updated': 'Agent 更新成功',
|
||||
'settings.agents.page.toast.createFailed': '建立 agent 失敗',
|
||||
'settings.agents.page.toast.updateFailed': '更新 agent 失敗',
|
||||
'settings.agents.page.toast.saveUnexpectedError': '儲存時發生錯誤',
|
||||
'settings.agents.page.toast.savedManualRestart': '已儲存到磁碟。請重新啟動已連線的 OpenCode 伺服器以套用變更。',
|
||||
'settings.agents.page.empty.title': '從側邊欄選擇一個 agent',
|
||||
'settings.agents.page.empty.description': '或建立一個 agent',
|
||||
'settings.agents.page.title.new': '新增 Agent',
|
||||
@@ -848,6 +850,7 @@
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': '輪換 agent',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_forward.label': '向前輪換收藏模型',
|
||||
'settings.openchamber.keyboardShortcuts.action.cycle_favorite_model_backward.label': '向後輪換收藏模型',
|
||||
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': '開啟模型選擇器',
|
||||
'settings.openchamber.keyboardShortcuts.action.expand_input.label': '展開輸入方塊',
|
||||
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': '開啟對話時間軸',
|
||||
'settings.projects.sidebar.total': '總計 {count}',
|
||||
|
||||
@@ -291,6 +291,7 @@ const SHORTCUT_ACTIONS: ReadonlyArray<ShortcutAction> = [
|
||||
defaultCombo: 'mod+shift+m',
|
||||
label: 'Open model selector',
|
||||
description: 'Open model selector while in chat',
|
||||
customizable: true,
|
||||
},
|
||||
{
|
||||
id: 'cycle_thinking_variant',
|
||||
|
||||
@@ -115,6 +115,17 @@ export interface AgentConfig {
|
||||
scope?: AgentScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of an agent config mutation.
|
||||
* `requiresManualRestart` is true when the change was persisted to disk but the
|
||||
* connected (external) OpenCode server could not be reloaded by OpenChamber, so
|
||||
* the user must restart that server before the change takes effect.
|
||||
*/
|
||||
export interface AgentMutationResult {
|
||||
ok: boolean;
|
||||
requiresManualRestart?: boolean;
|
||||
}
|
||||
|
||||
// Extended Agent type for API properties not in SDK types
|
||||
export type AgentWithExtras = Agent & {
|
||||
native?: boolean;
|
||||
@@ -190,9 +201,9 @@ interface AgentsStore {
|
||||
setSelectedAgent: (name: string | null) => void;
|
||||
setAgentDraft: (draft: AgentDraft | null) => void;
|
||||
loadAgents: () => Promise<boolean>;
|
||||
createAgent: (config: AgentConfig) => Promise<boolean>;
|
||||
updateAgent: (name: string, config: Partial<AgentConfig>) => Promise<boolean>;
|
||||
deleteAgent: (name: string, scope?: AgentScope) => Promise<boolean>;
|
||||
createAgent: (config: AgentConfig) => Promise<AgentMutationResult>;
|
||||
updateAgent: (name: string, config: Partial<AgentConfig>) => Promise<AgentMutationResult>;
|
||||
deleteAgent: (name: string, scope?: AgentScope) => Promise<AgentMutationResult>;
|
||||
getAgentByName: (name: string) => Agent | undefined;
|
||||
// Returns only visible agents (excludes hidden internal agents)
|
||||
getVisibleAgents: () => Agent[];
|
||||
@@ -361,8 +372,16 @@ export const useAgentsStore = create<AgentsStore>()(
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const needsReload = payload?.requiresReload ?? true;
|
||||
invalidateAgentsLoadCache(configDirectory);
|
||||
|
||||
// External OpenCode server: persisted to disk but not reloaded.
|
||||
// Skip the reload so the form keeps the just-saved values instead of
|
||||
// reverting to the server's stale, startup-cached config.
|
||||
if (payload?.requiresManualRestart) {
|
||||
return { ok: true, requiresManualRestart: true };
|
||||
}
|
||||
|
||||
const needsReload = payload?.requiresReload ?? true;
|
||||
if (needsReload) {
|
||||
requiresReload = true;
|
||||
await refreshAfterOpenCodeRestart({
|
||||
@@ -371,17 +390,17 @@ export const useAgentsStore = create<AgentsStore>()(
|
||||
scopes: ["agents"],
|
||||
mode: "projects",
|
||||
});
|
||||
return true;
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
const loaded = await get().loadAgents();
|
||||
if (loaded) {
|
||||
emitConfigChange("agents", { source: CONFIG_EVENT_SOURCE });
|
||||
}
|
||||
return loaded;
|
||||
return { ok: loaded };
|
||||
} catch (error) {
|
||||
console.error('Failed to create agent:', error);
|
||||
return false;
|
||||
return { ok: false };
|
||||
} finally {
|
||||
if (!requiresReload) {
|
||||
finishConfigUpdate();
|
||||
@@ -424,8 +443,16 @@ export const useAgentsStore = create<AgentsStore>()(
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const needsReload = payload?.requiresReload ?? true;
|
||||
invalidateAgentsLoadCache(configDirectory);
|
||||
|
||||
// External OpenCode server: persisted to disk but not reloaded.
|
||||
// Skip the reload so the form keeps the just-saved values instead of
|
||||
// reverting to the server's stale, startup-cached config.
|
||||
if (payload?.requiresManualRestart) {
|
||||
return { ok: true, requiresManualRestart: true };
|
||||
}
|
||||
|
||||
const needsReload = payload?.requiresReload ?? true;
|
||||
if (needsReload) {
|
||||
requiresReload = true;
|
||||
await refreshAfterOpenCodeRestart({
|
||||
@@ -434,14 +461,14 @@ export const useAgentsStore = create<AgentsStore>()(
|
||||
scopes: ["agents"],
|
||||
mode: "projects",
|
||||
});
|
||||
return true;
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
const loaded = await get().loadAgents();
|
||||
if (loaded) {
|
||||
emitConfigChange("agents", { source: CONFIG_EVENT_SOURCE });
|
||||
}
|
||||
return loaded;
|
||||
return { ok: loaded };
|
||||
} catch (error) {
|
||||
console.error('Failed to update agent:', error);
|
||||
throw error;
|
||||
@@ -475,8 +502,18 @@ export const useAgentsStore = create<AgentsStore>()(
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const needsReload = payload?.requiresReload ?? true;
|
||||
invalidateAgentsLoadCache(configDirectory);
|
||||
|
||||
if (get().selectedAgentName === name) {
|
||||
set({ selectedAgentName: null });
|
||||
}
|
||||
|
||||
// External OpenCode server: persisted to disk but not reloaded.
|
||||
if (payload?.requiresManualRestart) {
|
||||
return { ok: true, requiresManualRestart: true };
|
||||
}
|
||||
|
||||
const needsReload = payload?.requiresReload ?? true;
|
||||
if (needsReload) {
|
||||
requiresReload = true;
|
||||
await refreshAfterOpenCodeRestart({
|
||||
@@ -485,7 +522,7 @@ export const useAgentsStore = create<AgentsStore>()(
|
||||
scopes: ["agents"],
|
||||
mode: "projects",
|
||||
});
|
||||
return true;
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
const loaded = await get().loadAgents();
|
||||
@@ -493,10 +530,7 @@ export const useAgentsStore = create<AgentsStore>()(
|
||||
emitConfigChange("agents", { source: CONFIG_EVENT_SOURCE });
|
||||
}
|
||||
|
||||
if (get().selectedAgentName === name) {
|
||||
set({ selectedAgentName: null });
|
||||
}
|
||||
return loaded;
|
||||
return { ok: loaded };
|
||||
} catch (error) {
|
||||
console.error('Failed to delete agent:', error);
|
||||
throw error;
|
||||
|
||||
Reference in New Issue
Block a user