feat(desktop): integrate SaaS llm_routing, template API, and onboarding template selection

- Add AgentTemplateAvailable/AgentTemplateFull types and fetchAvailableTemplates/fetchTemplateFull API methods to saas-client
- Add llm_routing field to SaaSAccountInfo for admin-configured routing priority
- Add availableTemplates state and fetchAvailableTemplates action to saasStore with background fetch on login
- Add admin llm_routing priority check in connectionStore connect() to force relay or local mode
- Add createFromTemplate action to agentStore with SOUL.md persistence
- Add Step 0 template selection to AgentOnboardingWizard with grid layout for template browsing
This commit is contained in:
iven
2026-03-31 03:15:45 +08:00
parent 9fb9c3204c
commit c9b9c5231b
6 changed files with 927 additions and 1025 deletions

View File

@@ -26,6 +26,7 @@ import {
type SaaSLoginResponse,
type TotpSetupResponse,
type SyncConfigRequest,
type AgentTemplateAvailable,
} from '../lib/saas-client';
import { createLogger } from '../lib/logger';
import {
@@ -70,6 +71,8 @@ export interface SaaSStateSlice {
totpSetupData: TotpSetupResponse | null;
/** Whether SaaS backend is currently reachable */
saasReachable: boolean;
/** Agent templates available for onboarding */
availableTemplates: AgentTemplateAvailable[];
/** Consecutive heartbeat/health-check failures */
_consecutiveFailures: number;
_heartbeatTimer?: ReturnType<typeof setInterval>;
@@ -86,6 +89,7 @@ export interface SaaSActionsSlice {
syncConfigFromSaaS: () => Promise<void>;
pushConfigToSaaS: () => Promise<void>;
registerCurrentDevice: () => Promise<void>;
fetchAvailableTemplates: () => Promise<void>;
clearError: () => void;
restoreSession: () => void;
setupTotp: () => Promise<TotpSetupResponse>;
@@ -140,6 +144,7 @@ export const useSaaSStore = create<SaaSStore>((set, get) => {
totpRequired: false,
totpSetupData: null,
saasReachable: true,
availableTemplates: [],
_consecutiveFailures: 0,
// === Actions ===
@@ -189,6 +194,11 @@ export const useSaaSStore = create<SaaSStore>((set, get) => {
log.warn('Failed to register device:', err);
});
// Fetch available templates in background (non-blocking)
get().fetchAvailableTemplates().catch((err: unknown) => {
log.warn('Failed to fetch templates after login:', err);
});
// Fetch available models in background (non-blocking)
get().fetchAvailableModels().catch((err: unknown) => {
log.warn('Failed to fetch models after login:', err);
@@ -587,6 +597,16 @@ export const useSaaSStore = create<SaaSStore>((set, get) => {
}
},
fetchAvailableTemplates: async () => {
try {
const templates = await saasClient.fetchAvailableTemplates();
set({ availableTemplates: templates });
} catch {
// Graceful degradation - don't block login
set({ availableTemplates: [] });
}
},
clearError: () => {
set({ error: null });
},