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'));
|
||||
|
||||
Reference in New Issue
Block a user