Complete frontend migration to unified Pi harness (Phase 7 + Phase 9)
- Delete legacy hooks: useClaude.ts, useOpenCode.ts, usePiMono.ts, App.backup.tsx - Update all components to use usePi instead of legacy hooks - Replace useVisiblePiMonoModels/useClaudeModels/useOpenCodeModels with useVisiblePiModels - Migrate from LegacyChatMessage to ChatMessage type throughout - Update SessionBar to remove provider and archive props - Simplify ChatDetailPanel to Pi-only (remove Claude/OpenCode components) - Fix useChatSessions calls (remove provider parameter) - Update user-settings types: provider now only 'pi' instead of legacy values - Update PI_HARNESS_REBUILD.md to mark phases complete
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
export type UserSettings = {
|
||||
chat: {
|
||||
defaultProvider: 'claude' | 'opencode' | 'pi-mono';
|
||||
defaultProvider: 'pi';
|
||||
defaultModel: string | null;
|
||||
systemPrompt: string;
|
||||
temperature: number;
|
||||
@@ -11,7 +11,7 @@ export type UserSettings = {
|
||||
enabledProviders: string[];
|
||||
};
|
||||
tasks: {
|
||||
defaultProvider: 'claude' | 'opencode' | 'pi-mono';
|
||||
defaultProvider: 'pi';
|
||||
defaultModel: string | null;
|
||||
};
|
||||
appearance: {
|
||||
@@ -29,18 +29,18 @@ export type UserState = Record<string, unknown>;
|
||||
|
||||
export const DEFAULT_SETTINGS: UserSettings = {
|
||||
chat: {
|
||||
defaultProvider: 'claude',
|
||||
defaultProvider: 'pi',
|
||||
defaultModel: null,
|
||||
systemPrompt: '',
|
||||
temperature: 1,
|
||||
defaultPwd: '~',
|
||||
},
|
||||
ai: {
|
||||
enabledModels: ['claude-sonnet-4-5', 'claude-opus-4-6', 'claude-haiku-4-5'],
|
||||
enabledModels: [],
|
||||
enabledProviders: [],
|
||||
},
|
||||
tasks: {
|
||||
defaultProvider: 'claude',
|
||||
defaultProvider: 'pi',
|
||||
defaultModel: null,
|
||||
},
|
||||
appearance: {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { GroupEntry } from 'apps/Chat';
|
||||
import { useAuth } from 'hooks/useAuth';
|
||||
import { useClient } from 'hooks/useClient';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
export function useChatGroups() {
|
||||
const client = useClient();
|
||||
const queryClient = useQueryClient();
|
||||
const { isAuthenticated } = useAuth();
|
||||
|
||||
const { data: groups = [] } = useQuery<GroupEntry[]>({
|
||||
queryKey: ['PI_GROUPS'],
|
||||
enabled: isAuthenticated,
|
||||
queryFn: () => client.get<{ groups: GroupEntry[] }>('/api/pi/groups').then((r) => r.groups),
|
||||
});
|
||||
|
||||
async function createGroup(name: string, slug: string, description?: string, sessionIds?: string[]) {
|
||||
const result = await client.post<{ group: GroupEntry }>('/api/pi/groups', {
|
||||
name,
|
||||
slug,
|
||||
description,
|
||||
sessionIds,
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: ['PI_GROUPS'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['PI_SESSIONS'] });
|
||||
return result.group;
|
||||
}
|
||||
|
||||
async function updateGroup(slug: string, updates: { name?: string; description?: string }) {
|
||||
await client.patch(`/api/pi/groups/${slug}`, updates);
|
||||
queryClient.invalidateQueries({ queryKey: ['PI_GROUPS'] });
|
||||
}
|
||||
|
||||
async function deleteGroup(slug: string) {
|
||||
await client.delete(`/api/pi/groups/${slug}`);
|
||||
queryClient.invalidateQueries({ queryKey: ['PI_GROUPS'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['PI_SESSIONS'] });
|
||||
}
|
||||
|
||||
async function moveSession(sessionId: string, groupSlug: string | null) {
|
||||
await client.post(`/api/pi/sessions/${sessionId}/move`, { groupSlug });
|
||||
queryClient.invalidateQueries({ queryKey: ['PI_GROUPS'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['PI_SESSIONS'] });
|
||||
}
|
||||
|
||||
return {
|
||||
groups,
|
||||
createGroup,
|
||||
updateGroup,
|
||||
deleteGroup,
|
||||
moveSession,
|
||||
};
|
||||
}
|
||||
@@ -1,94 +1,39 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useClient } from 'hooks/useClient';
|
||||
import { useAuth } from 'hooks/useAuth';
|
||||
import { useSettings } from './useSettings';
|
||||
import type { ModelOption } from 'apps/Chat';
|
||||
|
||||
export type ModelOption = { id: string; name: string; provider?: string; providerId?: string };
|
||||
export type { ModelOption };
|
||||
|
||||
export const modelKey = (m: ModelOption) => (m.provider ? `${m.provider}:${m.id}` : m.id);
|
||||
export function modelKey(m: ModelOption): string {
|
||||
return `${m.provider}:${m.id}`;
|
||||
}
|
||||
|
||||
// Hardcoded fallback in case the API call fails
|
||||
const CLAUDE_MODELS: ModelOption[] = [
|
||||
{ id: 'claude-sonnet-4-5', name: 'Claude Sonnet 4.5' },
|
||||
{ id: 'claude-opus-4-6', name: 'Claude Opus 4.6' },
|
||||
{ id: 'claude-haiku-4-5', name: 'Claude Haiku 4.5' },
|
||||
];
|
||||
|
||||
export const useClaudeModels = () => {
|
||||
export function usePiModels() {
|
||||
const client = useClient();
|
||||
const { isAuthenticated } = useAuth();
|
||||
|
||||
const { data: models = CLAUDE_MODELS } = useQuery<ModelOption[]>({
|
||||
queryKey: ['CLAUDE_MODELS'],
|
||||
const { data: models = [] } = useQuery<ModelOption[]>({
|
||||
queryKey: ['PI_MODELS'],
|
||||
enabled: isAuthenticated,
|
||||
queryFn: async () => {
|
||||
const data = await client.get<ModelOption[]>('/claude/models');
|
||||
return data.length > 0 ? data : CLAUDE_MODELS;
|
||||
const data = await client.get<{ models: ModelOption[] }>('/api/pi/models');
|
||||
return data.models;
|
||||
},
|
||||
staleTime: 10 * 60 * 1000,
|
||||
});
|
||||
|
||||
return models;
|
||||
};
|
||||
|
||||
/** @deprecated Use useClaudeModels() instead */
|
||||
export const claudeModels = CLAUDE_MODELS;
|
||||
|
||||
export const useOpenCodeModels = () => {
|
||||
const client = useClient();
|
||||
const { isAuthenticated } = useAuth();
|
||||
|
||||
const { data: models = [] } = useQuery<ModelOption[]>({
|
||||
queryKey: ['OC_MODELS'],
|
||||
enabled: isAuthenticated,
|
||||
queryFn: () => client.get<ModelOption[]>('/opencode/models'),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
|
||||
return models;
|
||||
};
|
||||
}
|
||||
|
||||
export const useVisibleClaudeModels = () => {
|
||||
const models = useClaudeModels();
|
||||
export function useVisiblePiModels() {
|
||||
const models = usePiModels();
|
||||
const { settings } = useSettings();
|
||||
const enabled = settings.ai?.enabledModels ?? [];
|
||||
return useMemo(() => models.filter((m) => enabled.includes(modelKey(m))), [models, enabled]);
|
||||
};
|
||||
|
||||
export const useVisibleOpenCodeModels = () => {
|
||||
const models = useOpenCodeModels();
|
||||
const { settings } = useSettings();
|
||||
const providers = settings.ai?.enabledProviders ?? [];
|
||||
const enabled = settings.ai?.enabledModels ?? [];
|
||||
return useMemo(
|
||||
() => models.filter((m) => providers.includes(m.provider ?? '') && enabled.includes(modelKey(m))),
|
||||
[models, providers, enabled],
|
||||
);
|
||||
};
|
||||
|
||||
export const usePiMonoModels = () => {
|
||||
const client = useClient();
|
||||
const { isAuthenticated } = useAuth();
|
||||
|
||||
const { data: models = [] } = useQuery<ModelOption[]>({
|
||||
queryKey: ['PI_MONO_MODELS'],
|
||||
enabled: isAuthenticated,
|
||||
queryFn: () => client.get<ModelOption[]>('/pi-mono/models'),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
|
||||
return models;
|
||||
};
|
||||
|
||||
export const useVisiblePiMonoModels = () => {
|
||||
const models = usePiMonoModels();
|
||||
const { settings } = useSettings();
|
||||
const enabled = settings.ai?.enabledModels ?? [];
|
||||
return useMemo(() => {
|
||||
const filtered = models.filter((m) => enabled.includes(modelKey(m)));
|
||||
// If no models match the visibility filter, show all — the provider list
|
||||
// changes dynamically based on API keys so the filter may be stale
|
||||
return filtered.length > 0 ? filtered : models;
|
||||
}, [models, enabled]);
|
||||
};
|
||||
const filtered = models.filter((m) => enabled.includes(modelKey(m)));
|
||||
// If no models match the visibility filter, show all
|
||||
// The provider list changes dynamically based on API keys so the filter may be stale
|
||||
return filtered.length > 0 ? filtered : models;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@ export const useSlashCommands = ({ sessionId }: UseSlashCommandsParams) => {
|
||||
|
||||
switch (command) {
|
||||
case 'rename':
|
||||
return renameSession('claude', sessionId, args);
|
||||
if (!sessionId || !args) return { handled: false };
|
||||
await renameSession(sessionId, args);
|
||||
return { handled: true, feedback: `Session renamed to "${args}"` };
|
||||
default:
|
||||
return { handled: false };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user