fix: sync dev branch with main timeline/schema changes and remove stale providers
Forward-port main's timeline epoch tracking, projected window selection, and schema updates into dev. Remove aider/amp/gemini providers and provider-history-compatibility layers that are no longer needed.
This commit is contained in:
@@ -1,61 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("@react-native-async-storage/async-storage", () => {
|
||||
const storage = new Map<string, string>();
|
||||
return {
|
||||
default: {
|
||||
getItem: vi.fn(async (key: string) => storage.get(key) ?? null),
|
||||
setItem: vi.fn(async (key: string, value: string) => {
|
||||
storage.set(key, value);
|
||||
}),
|
||||
removeItem: vi.fn(async (key: string) => {
|
||||
storage.delete(key);
|
||||
}),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
import { AGENT_PROVIDER_DEFINITIONS } from "@server/server/agent/provider-manifest";
|
||||
import {
|
||||
__providerRecencyStoreTestUtils,
|
||||
sortProvidersByRecency,
|
||||
useProviderRecencyStore,
|
||||
} from "./provider-recency-store";
|
||||
|
||||
describe("provider-recency-store", () => {
|
||||
beforeEach(() => {
|
||||
useProviderRecencyStore.setState({
|
||||
recentProviderIds: [],
|
||||
recordUsage: useProviderRecencyStore.getState().recordUsage,
|
||||
});
|
||||
});
|
||||
|
||||
it("sorts used providers first and keeps unused providers in default order", () => {
|
||||
const sorted = sortProvidersByRecency(AGENT_PROVIDER_DEFINITIONS, ["codex"]);
|
||||
|
||||
expect(sorted.map((provider) => provider.id)).toEqual([
|
||||
"codex",
|
||||
...AGENT_PROVIDER_DEFINITIONS.filter((provider) => provider.id !== "codex").map(
|
||||
(provider) => provider.id,
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
it("moves the latest provider to the front without duplicating prior entries", () => {
|
||||
useProviderRecencyStore.getState().recordUsage("codex");
|
||||
useProviderRecencyStore.getState().recordUsage("opencode");
|
||||
useProviderRecencyStore.getState().recordUsage("codex");
|
||||
|
||||
expect(useProviderRecencyStore.getState().recentProviderIds).toEqual(["codex", "opencode"]);
|
||||
});
|
||||
|
||||
it("filters invalid and duplicate providers during migration", () => {
|
||||
expect(
|
||||
__providerRecencyStoreTestUtils.migratePersistedState({
|
||||
recentProviderIds: ["codex", "invalid", "codex", "claude"],
|
||||
}),
|
||||
).toEqual({
|
||||
recentProviderIds: ["codex", "claude"],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,131 +0,0 @@
|
||||
import { useMemo } from "react";
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import type { AgentProvider } from "@server/server/agent/agent-sdk-types";
|
||||
import {
|
||||
AGENT_PROVIDER_DEFINITIONS,
|
||||
isValidAgentProvider,
|
||||
type AgentProviderDefinition,
|
||||
} from "@server/server/agent/provider-manifest";
|
||||
import { create } from "zustand";
|
||||
import { createJSONStorage, persist } from "zustand/middleware";
|
||||
|
||||
const PROVIDER_RECENCY_STORE_VERSION = 1;
|
||||
|
||||
interface ProviderRecencyStoreState {
|
||||
recentProviderIds: AgentProvider[];
|
||||
recordUsage: (providerId: AgentProvider) => void;
|
||||
}
|
||||
|
||||
function sanitizeRecentProviderIds(providerIds: readonly string[] | undefined): AgentProvider[] {
|
||||
if (!providerIds || providerIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const seen = new Set<AgentProvider>();
|
||||
const sanitized: AgentProvider[] = [];
|
||||
for (const providerId of providerIds) {
|
||||
if (!isValidAgentProvider(providerId)) {
|
||||
continue;
|
||||
}
|
||||
if (seen.has(providerId)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(providerId);
|
||||
sanitized.push(providerId);
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
export function sortProvidersByRecency<T extends { id: string }>(
|
||||
providers: readonly T[],
|
||||
recentProviderIds: readonly string[],
|
||||
): T[] {
|
||||
if (providers.length <= 1) {
|
||||
return [...providers];
|
||||
}
|
||||
|
||||
const recentRank = new Map<string, number>();
|
||||
for (const providerId of recentProviderIds) {
|
||||
if (recentRank.has(providerId)) {
|
||||
continue;
|
||||
}
|
||||
recentRank.set(providerId, recentRank.size);
|
||||
}
|
||||
|
||||
return providers
|
||||
.map((provider, defaultIndex) => ({
|
||||
provider,
|
||||
defaultIndex,
|
||||
recentIndex: recentRank.get(provider.id) ?? Number.POSITIVE_INFINITY,
|
||||
}))
|
||||
.sort((left, right) => {
|
||||
if (left.recentIndex !== right.recentIndex) {
|
||||
return left.recentIndex - right.recentIndex;
|
||||
}
|
||||
return left.defaultIndex - right.defaultIndex;
|
||||
})
|
||||
.map((entry) => entry.provider);
|
||||
}
|
||||
|
||||
function migratePersistedState(
|
||||
state: unknown,
|
||||
): Pick<ProviderRecencyStoreState, "recentProviderIds"> {
|
||||
const record = state as { recentProviderIds?: string[] } | null | undefined;
|
||||
return {
|
||||
recentProviderIds: sanitizeRecentProviderIds(record?.recentProviderIds),
|
||||
};
|
||||
}
|
||||
|
||||
export const useProviderRecencyStore = create<ProviderRecencyStoreState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
recentProviderIds: [],
|
||||
recordUsage: (providerId) => {
|
||||
if (!isValidAgentProvider(providerId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
set((state) => ({
|
||||
recentProviderIds: [
|
||||
providerId,
|
||||
...state.recentProviderIds.filter((id) => id !== providerId),
|
||||
],
|
||||
}));
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: "agent-provider-recency",
|
||||
version: PROVIDER_RECENCY_STORE_VERSION,
|
||||
storage: createJSONStorage(() => AsyncStorage),
|
||||
partialize: (state) => ({
|
||||
recentProviderIds: state.recentProviderIds,
|
||||
}),
|
||||
migrate: (persistedState) => migratePersistedState(persistedState),
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
export function useProviderRecency(
|
||||
availableProviders: readonly AgentProviderDefinition[] = AGENT_PROVIDER_DEFINITIONS,
|
||||
): {
|
||||
providers: AgentProviderDefinition[];
|
||||
recordUsage: (providerId: AgentProvider) => void;
|
||||
} {
|
||||
const recentProviderIds = useProviderRecencyStore((state) => state.recentProviderIds);
|
||||
const recordUsage = useProviderRecencyStore((state) => state.recordUsage);
|
||||
|
||||
const providers = useMemo(
|
||||
() => sortProvidersByRecency(availableProviders, recentProviderIds),
|
||||
[availableProviders, recentProviderIds],
|
||||
);
|
||||
|
||||
return {
|
||||
providers,
|
||||
recordUsage,
|
||||
};
|
||||
}
|
||||
|
||||
export const __providerRecencyStoreTestUtils = {
|
||||
migratePersistedState,
|
||||
sanitizeRecentProviderIds,
|
||||
};
|
||||
@@ -212,6 +212,7 @@ export type DaemonServerInfo = {
|
||||
};
|
||||
|
||||
export interface AgentTimelineCursorState {
|
||||
epoch: string;
|
||||
startSeq: number;
|
||||
endSeq: number;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user