fix(saas): deep audit round industry template system - critical fixes
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
C1: Use backend createAgentFromTemplate API + tools forwarding C3: seed source='builtin' instead of 'custom' C4: immutable clone data handling (return fresh from store) + spread) H3: assignTemplate error propagation (try/catch) H4: input validation for name/fields H5: assign_template account existence check H6: remove dead route get_full_template H7: model fallback gpt-4o-mini (hardcoded constant) H8: logout clears template state H9: console.warn -> structured logger C2: restoreSession fetches assignedTemplate
This commit is contained in:
@@ -7,8 +7,12 @@
|
||||
import { create } from 'zustand';
|
||||
import type { GatewayClient } from '../lib/gateway-client';
|
||||
import type { AgentTemplateFull } from '../lib/saas-client';
|
||||
import { saasClient } from '../lib/saas-client';
|
||||
import { useChatStore } from './chatStore';
|
||||
import { useConversationStore } from './chat/conversationStore';
|
||||
import { createLogger } from '../lib/logger';
|
||||
|
||||
const log = createLogger('AgentStore');
|
||||
|
||||
// === Types ===
|
||||
|
||||
@@ -134,7 +138,7 @@ export const useAgentStore = create<AgentStore>((set, get) => ({
|
||||
loadClones: async () => {
|
||||
const client = getClient();
|
||||
if (!client) {
|
||||
console.warn('[AgentStore] Client not initialized, skipping loadClones');
|
||||
log.warn('[AgentStore] Client not initialized, skipping loadClones');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -163,7 +167,7 @@ export const useAgentStore = create<AgentStore>((set, get) => ({
|
||||
createClone: async (opts: CloneCreateOptions) => {
|
||||
const client = getClient();
|
||||
if (!client) {
|
||||
console.warn('[AgentStore] Client not initialized');
|
||||
log.warn('[AgentStore] Client not initialized');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -181,76 +185,79 @@ export const useAgentStore = create<AgentStore>((set, get) => ({
|
||||
|
||||
createFromTemplate: async (template: AgentTemplateFull) => {
|
||||
const client = getClient();
|
||||
if (!client) return undefined;
|
||||
if (!client) {
|
||||
set({ error: 'Client not initialized' });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
set({ isLoading: true, error: null });
|
||||
try {
|
||||
// Step 1: Call backend to get server-processed config (tools merge, model fallback)
|
||||
const config = await saasClient.createAgentFromTemplate(template.id);
|
||||
|
||||
// Step 2: Create clone with merged data from backend
|
||||
const result = await client.createClone({
|
||||
name: template.name,
|
||||
emoji: template.emoji,
|
||||
personality: template.personality,
|
||||
name: config.name,
|
||||
emoji: config.emoji,
|
||||
personality: config.personality,
|
||||
scenarios: template.scenarios,
|
||||
communicationStyle: template.communication_style,
|
||||
model: template.model,
|
||||
communicationStyle: config.communication_style,
|
||||
model: config.model,
|
||||
});
|
||||
|
||||
const cloneId = result?.clone?.id;
|
||||
|
||||
if (cloneId) {
|
||||
// Persist SOUL.md via identity system
|
||||
if (template.soul_content) {
|
||||
if (config.soul_content) {
|
||||
try {
|
||||
const { intelligenceClient } = await import('../lib/intelligence-client');
|
||||
await intelligenceClient.identity.updateFile(cloneId, 'soul', template.soul_content);
|
||||
await intelligenceClient.identity.updateFile(cloneId, 'soul', config.soul_content);
|
||||
} catch (e) {
|
||||
console.warn('Failed to persist soul_content:', e);
|
||||
log.warn('Failed to persist soul_content:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Persist system_prompt via identity system
|
||||
if (template.system_prompt) {
|
||||
if (config.system_prompt) {
|
||||
try {
|
||||
const { intelligenceClient } = await import('../lib/intelligence-client');
|
||||
await intelligenceClient.identity.updateFile(cloneId, 'system', template.system_prompt);
|
||||
await intelligenceClient.identity.updateFile(cloneId, 'system', config.system_prompt);
|
||||
} catch (e) {
|
||||
console.warn('Failed to persist system_prompt:', e);
|
||||
log.warn('Failed to persist system_prompt:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Persist temperature / max_tokens if supported
|
||||
if (template.temperature != null || template.max_tokens != null) {
|
||||
try {
|
||||
await client.updateClone(cloneId, {
|
||||
temperature: template.temperature,
|
||||
maxTokens: template.max_tokens,
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('Failed to persist temperature/max_tokens:', e);
|
||||
}
|
||||
}
|
||||
// Persist temperature / max_tokens / tools / source_template_id / welcomeMessage / quickCommands
|
||||
const metadata: Record<string, unknown> = {};
|
||||
if (config.temperature != null) metadata.temperature = config.temperature;
|
||||
if (config.max_tokens != null) metadata.maxTokens = config.max_tokens;
|
||||
if (config.tools?.length) metadata.tools = config.tools;
|
||||
metadata.source_template_id = template.id;
|
||||
if (config.welcome_message) metadata.welcomeMessage = config.welcome_message;
|
||||
if (config.quick_commands?.length) metadata.quickCommands = config.quick_commands;
|
||||
|
||||
// Persist welcome_message + quick_commands as clone metadata
|
||||
if (template.welcome_message || (template.quick_commands && template.quick_commands.length > 0)) {
|
||||
if (Object.keys(metadata).length > 0) {
|
||||
try {
|
||||
await client.updateClone(cloneId, {
|
||||
welcomeMessage: template.welcome_message || '',
|
||||
quickCommands: template.quick_commands || [],
|
||||
});
|
||||
await client.updateClone(cloneId, metadata);
|
||||
} catch (e) {
|
||||
console.warn('Failed to persist welcome_message/quick_commands:', e);
|
||||
log.warn('Failed to persist clone metadata:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await get().loadClones();
|
||||
|
||||
// Merge template welcome/quick data into the returned clone for immediate use
|
||||
const createdClone = result?.clone as Record<string, unknown> | undefined;
|
||||
if (createdClone) {
|
||||
if (template.welcome_message) createdClone.welcomeMessage = template.welcome_message;
|
||||
if (template.quick_commands?.length) createdClone.quickCommands = template.quick_commands;
|
||||
// Return a fresh clone from the store (immutable — no in-place mutation)
|
||||
const freshClone = get().clones.find((c) => c.id === cloneId);
|
||||
if (freshClone) {
|
||||
return {
|
||||
...freshClone,
|
||||
...(config.welcome_message ? { welcomeMessage: config.welcome_message } : {}),
|
||||
...(config.quick_commands?.length ? { quickCommands: config.quick_commands } : {}),
|
||||
};
|
||||
}
|
||||
return createdClone as Clone | undefined;
|
||||
return result?.clone as Clone | undefined;
|
||||
} catch (error) {
|
||||
set({ error: String(error) });
|
||||
return undefined;
|
||||
@@ -262,7 +269,7 @@ export const useAgentStore = create<AgentStore>((set, get) => ({
|
||||
updateClone: async (id: string, updates: Partial<Clone>) => {
|
||||
const client = getClient();
|
||||
if (!client) {
|
||||
console.warn('[AgentStore] Client not initialized');
|
||||
log.warn('[AgentStore] Client not initialized');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -281,7 +288,7 @@ export const useAgentStore = create<AgentStore>((set, get) => ({
|
||||
deleteClone: async (id: string) => {
|
||||
const client = getClient();
|
||||
if (!client) {
|
||||
console.warn('[AgentStore] Client not initialized');
|
||||
log.warn('[AgentStore] Client not initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -325,7 +332,7 @@ export const useAgentStore = create<AgentStore>((set, get) => ({
|
||||
loadPluginStatus: async () => {
|
||||
const client = getClient();
|
||||
if (!client) {
|
||||
console.warn('[AgentStore] Client not initialized, skipping loadPluginStatus');
|
||||
log.warn('[AgentStore] Client not initialized, skipping loadPluginStatus');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -397,8 +397,10 @@ export const useSaaSStore = create<SaaSStore>((set, get) => {
|
||||
authToken: null,
|
||||
connectionMode: 'tauri',
|
||||
availableModels: [],
|
||||
availableTemplates: [],
|
||||
assignedTemplate: null,
|
||||
error: null,
|
||||
totpRequired: false,
|
||||
toTopRequired: false,
|
||||
totpSetupData: null,
|
||||
});
|
||||
},
|
||||
@@ -720,6 +722,8 @@ export const useSaaSStore = create<SaaSStore>((set, get) => {
|
||||
connectionMode: loadConnectionMode() === 'saas' ? 'saas' : 'tauri',
|
||||
});
|
||||
get().fetchAvailableModels().catch(() => {});
|
||||
get().fetchAvailableTemplates().catch(() => {});
|
||||
get().fetchAssignedTemplate().catch(() => {});
|
||||
get().syncConfigFromSaaS().then(() => {
|
||||
get().pushConfigToSaaS().catch(() => {});
|
||||
}).catch(() => {});
|
||||
|
||||
Reference in New Issue
Block a user