Remove active host dependency from UI actions
This commit is contained in:
@@ -82,21 +82,9 @@ export default function AgentScreen() {
|
|||||||
const { serverId, agentId } = useLocalSearchParams<{ serverId: string; agentId: string }>();
|
const { serverId, agentId } = useLocalSearchParams<{ serverId: string; agentId: string }>();
|
||||||
const resolvedAgentId = typeof agentId === "string" ? agentId : undefined;
|
const resolvedAgentId = typeof agentId === "string" ? agentId : undefined;
|
||||||
const resolvedServerId = typeof serverId === "string" ? serverId : undefined;
|
const resolvedServerId = typeof serverId === "string" ? serverId : undefined;
|
||||||
const { connectionStates, activeDaemonId, setActiveDaemonId } = useDaemonConnections();
|
const { connectionStates } = useDaemonConnections();
|
||||||
const hasConnectionEntry = resolvedServerId ? connectionStates.has(resolvedServerId) : false;
|
const hasConnectionEntry = resolvedServerId ? connectionStates.has(resolvedServerId) : false;
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!resolvedServerId || !hasConnectionEntry) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeDaemonId === resolvedServerId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setActiveDaemonId(resolvedServerId, { source: "agent_route" });
|
|
||||||
}, [resolvedServerId, hasConnectionEntry, activeDaemonId, setActiveDaemonId]);
|
|
||||||
|
|
||||||
const session = useDaemonSession(resolvedServerId, {
|
const session = useDaemonSession(resolvedServerId, {
|
||||||
suppressUnavailableAlert: true,
|
suppressUnavailableAlert: true,
|
||||||
allowUnavailable: true,
|
allowUnavailable: true,
|
||||||
@@ -644,7 +632,7 @@ function AgentSessionUnavailableState({
|
|||||||
Cannot open this agent because {serverLabel} is not configured on this device.
|
Cannot open this agent because {serverLabel} is not configured on this device.
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={styles.statusText}>
|
<Text style={styles.statusText}>
|
||||||
Add the daemon in Settings or open an agent on a configured server to continue.
|
Add the host in Settings or open an agent on a configured server to continue.
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -661,7 +649,7 @@ function AgentSessionUnavailableState({
|
|||||||
<>
|
<>
|
||||||
<ActivityIndicator size="large" />
|
<ActivityIndicator size="large" />
|
||||||
<Text style={styles.loadingText}>Connecting to {serverLabel}...</Text>
|
<Text style={styles.loadingText}>Connecting to {serverLabel}...</Text>
|
||||||
<Text style={styles.statusText}>We will show this agent once the daemon is online.</Text>
|
<Text style={styles.statusText}>We will show this agent once the host is online.</Text>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -669,7 +657,7 @@ function AgentSessionUnavailableState({
|
|||||||
Cannot open this agent while {serverLabel} is {connectionStatusLabel.toLowerCase()}.
|
Cannot open this agent while {serverLabel} is {connectionStatusLabel.toLowerCase()}.
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={styles.statusText}>
|
<Text style={styles.statusText}>
|
||||||
Connect this daemon or switch to another one to continue.
|
Connect this host or switch to another one to continue.
|
||||||
</Text>
|
</Text>
|
||||||
{lastError ? <Text style={styles.errorDetails}>{lastError}</Text> : null}
|
{lastError ? <Text style={styles.errorDetails}>{lastError}</Text> : null}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default function HomeScreen() {
|
|||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { agents } = useSession();
|
const { agents } = useSession();
|
||||||
const { activeDaemonId, connectionStates } = useDaemonConnections();
|
const { connectionStates } = useDaemonConnections();
|
||||||
const aggregatedAgents = useAggregatedAgents(agents);
|
const aggregatedAgents = useAggregatedAgents(agents);
|
||||||
const [showCreateModal, setShowCreateModal] = useState(false);
|
const [showCreateModal, setShowCreateModal] = useState(false);
|
||||||
const [showImportModal, setShowImportModal] = useState(false);
|
const [showImportModal, setShowImportModal] = useState(false);
|
||||||
@@ -63,15 +63,11 @@ export default function HomeScreen() {
|
|||||||
setShowCreateModal(true);
|
setShowCreateModal(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const openImportModal = useCallback(
|
const openImportModal = useCallback((serverIdOverride?: string | null) => {
|
||||||
(serverIdOverride?: string | null) => {
|
setPendingImportServerId(serverIdOverride ?? null);
|
||||||
const resolvedServerId = serverIdOverride ?? activeDaemonId ?? null;
|
setImportModalMounted(true);
|
||||||
setPendingImportServerId(resolvedServerId);
|
setShowImportModal(true);
|
||||||
setImportModalMounted(true);
|
}, []);
|
||||||
setShowImportModal(true);
|
|
||||||
},
|
|
||||||
[activeDaemonId]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleImportAgent = useCallback(() => {
|
const handleImportAgent = useCallback(() => {
|
||||||
openImportModal();
|
openImportModal();
|
||||||
@@ -166,14 +162,13 @@ export default function HomeScreen() {
|
|||||||
<CreateAgentModal
|
<CreateAgentModal
|
||||||
isVisible={showCreateModal}
|
isVisible={showCreateModal}
|
||||||
onClose={handleCloseCreateModal}
|
onClose={handleCloseCreateModal}
|
||||||
serverId={activeDaemonId}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{importModalMounted ? (
|
{importModalMounted ? (
|
||||||
<ImportAgentModal
|
<ImportAgentModal
|
||||||
isVisible={showImportModal}
|
isVisible={showImportModal}
|
||||||
onClose={handleCloseImportModal}
|
onClose={handleCloseImportModal}
|
||||||
serverId={pendingImportServerId ?? activeDaemonId}
|
serverId={pendingImportServerId}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import type { Agent } from "@/contexts/session-context";
|
|||||||
import { formatTimeAgo } from "@/utils/time";
|
import { formatTimeAgo } from "@/utils/time";
|
||||||
import { getAgentStatusColor, getAgentStatusLabel } from "@/utils/agent-status";
|
import { getAgentStatusColor, getAgentStatusLabel } from "@/utils/agent-status";
|
||||||
import { getAgentProviderDefinition } from "@server/server/agent/provider-manifest";
|
import { getAgentProviderDefinition } from "@server/server/agent/provider-manifest";
|
||||||
import { useDaemonConnections } from "@/contexts/daemon-connections-context";
|
|
||||||
import { type AggregatedAgentGroup } from "@/hooks/use-aggregated-agents";
|
import { type AggregatedAgentGroup } from "@/hooks/use-aggregated-agents";
|
||||||
import { useDaemonSession } from "@/hooks/use-daemon-session";
|
import { useDaemonSession } from "@/hooks/use-daemon-session";
|
||||||
|
|
||||||
@@ -16,7 +15,6 @@ interface AgentListProps {
|
|||||||
|
|
||||||
export function AgentList({ agentGroups }: AgentListProps) {
|
export function AgentList({ agentGroups }: AgentListProps) {
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
const { setActiveDaemonId } = useDaemonConnections();
|
|
||||||
const [actionAgent, setActionAgent] = useState<Agent | null>(null);
|
const [actionAgent, setActionAgent] = useState<Agent | null>(null);
|
||||||
const [actionAgentServerId, setActionAgentServerId] = useState<string | null>(null);
|
const [actionAgentServerId, setActionAgentServerId] = useState<string | null>(null);
|
||||||
const actionSession = useDaemonSession(actionAgentServerId ?? undefined, {
|
const actionSession = useDaemonSession(actionAgentServerId ?? undefined, {
|
||||||
@@ -28,19 +26,21 @@ export function AgentList({ agentGroups }: AgentListProps) {
|
|||||||
const isActionSheetVisible = actionAgent !== null;
|
const isActionSheetVisible = actionAgent !== null;
|
||||||
const isActionDaemonUnavailable = Boolean(actionAgentServerId && !actionSession);
|
const isActionDaemonUnavailable = Boolean(actionAgentServerId && !actionSession);
|
||||||
|
|
||||||
const handleAgentPress = useCallback((serverId: string, agentId: string) => {
|
const handleAgentPress = useCallback(
|
||||||
if (isActionSheetVisible) {
|
(serverId: string, agentId: string) => {
|
||||||
return;
|
if (isActionSheetVisible) {
|
||||||
}
|
return;
|
||||||
setActiveDaemonId(serverId, { source: "agent_row_press" });
|
}
|
||||||
router.push({
|
router.push({
|
||||||
pathname: "/agent/[serverId]/[agentId]",
|
pathname: "/agent/[serverId]/[agentId]",
|
||||||
params: {
|
params: {
|
||||||
serverId,
|
serverId,
|
||||||
agentId,
|
agentId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, [isActionSheetVisible, setActiveDaemonId]);
|
},
|
||||||
|
[isActionSheetVisible]
|
||||||
|
);
|
||||||
|
|
||||||
const handleAgentLongPress = useCallback((serverId: string, agent: Agent) => {
|
const handleAgentLongPress = useCallback((serverId: string, agent: Agent) => {
|
||||||
setActionAgentServerId(serverId);
|
setActionAgentServerId(serverId);
|
||||||
|
|||||||
@@ -215,17 +215,14 @@ function AgentFlowModal({
|
|||||||
const isCreateFlow = !isImportFlow;
|
const isCreateFlow = !isImportFlow;
|
||||||
|
|
||||||
const { recentPaths, addRecentPath } = useRecentPaths();
|
const { recentPaths, addRecentPath } = useRecentPaths();
|
||||||
const { connectionStates, activeDaemonId, setActiveDaemonId } = useDaemonConnections();
|
const { connectionStates } = useDaemonConnections();
|
||||||
const daemonEntries = useMemo(() => Array.from(connectionStates.values()), [connectionStates]);
|
const daemonEntries = useMemo(() => Array.from(connectionStates.values()), [connectionStates]);
|
||||||
const initialServerId = useMemo(() => {
|
const initialServerId = useMemo(() => {
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
return serverId;
|
return serverId;
|
||||||
}
|
}
|
||||||
if (activeDaemonId) {
|
|
||||||
return activeDaemonId;
|
|
||||||
}
|
|
||||||
return daemonEntries[0]?.daemon.id ?? null;
|
return daemonEntries[0]?.daemon.id ?? null;
|
||||||
}, [serverId, activeDaemonId, daemonEntries]);
|
}, [serverId, daemonEntries]);
|
||||||
const [selectedServerId, setSelectedServerId] = useState<string | null>(initialServerId);
|
const [selectedServerId, setSelectedServerId] = useState<string | null>(initialServerId);
|
||||||
const activeSession = useSession();
|
const activeSession = useSession();
|
||||||
const selectedSession = useSessionForServer(selectedServerId);
|
const selectedSession = useSessionForServer(selectedServerId);
|
||||||
@@ -308,8 +305,8 @@ function AgentFlowModal({
|
|||||||
} = gitRepoInfoRequest;
|
} = gitRepoInfoRequest;
|
||||||
const isWsConnected = ws?.isConnected ?? false;
|
const isWsConnected = ws?.isConnected ?? false;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const activeServerId = session?.serverId ?? null;
|
const sessionServerId = session?.serverId ?? null;
|
||||||
const selectedDaemonId = selectedServerId ?? activeServerId;
|
const selectedDaemonId = selectedServerId ?? sessionServerId;
|
||||||
const selectedDaemonConnection = selectedDaemonId
|
const selectedDaemonConnection = selectedDaemonId
|
||||||
? connectionStates.get(selectedDaemonId)
|
? connectionStates.get(selectedDaemonId)
|
||||||
: null;
|
: null;
|
||||||
@@ -330,8 +327,6 @@ function AgentFlowModal({
|
|||||||
const selectedDaemonStatusLabel = formatConnectionStatus(selectedDaemonStatus);
|
const selectedDaemonStatusLabel = formatConnectionStatus(selectedDaemonStatus);
|
||||||
const selectedDaemonIsUnavailable =
|
const selectedDaemonIsUnavailable =
|
||||||
!session || selectedDaemonStatus !== "online" || !ws?.isConnected;
|
!session || selectedDaemonStatus !== "online" || !ws?.isConnected;
|
||||||
const isBackgroundDaemonTarget =
|
|
||||||
Boolean(selectedDaemonId && activeDaemonId && selectedDaemonId !== activeDaemonId);
|
|
||||||
const selectedDaemonLastError = selectedDaemonConnection?.lastError?.trim();
|
const selectedDaemonLastError = selectedDaemonConnection?.lastError?.trim();
|
||||||
const daemonAvailabilityError = selectedDaemonIsUnavailable
|
const daemonAvailabilityError = selectedDaemonIsUnavailable
|
||||||
? `${selectedDaemonLabel} is ${selectedDaemonStatusLabel}. Connect to it before creating or importing agents.${
|
? `${selectedDaemonLabel} is ${selectedDaemonStatusLabel}. Connect to it before creating or importing agents.${
|
||||||
@@ -407,6 +402,7 @@ function AgentFlowModal({
|
|||||||
typeof InteractionManager.runAfterInteractions
|
typeof InteractionManager.runAfterInteractions
|
||||||
> | null>(null);
|
> | null>(null);
|
||||||
const pendingNavigationAgentIdRef = useRef<string | null>(null);
|
const pendingNavigationAgentIdRef = useRef<string | null>(null);
|
||||||
|
const pendingNavigationServerIdRef = useRef<string | null>(null);
|
||||||
const openDropdownSheet = useCallback((key: DropdownKey) => {
|
const openDropdownSheet = useCallback((key: DropdownKey) => {
|
||||||
setOpenDropdown(key);
|
setOpenDropdown(key);
|
||||||
}, []);
|
}, []);
|
||||||
@@ -862,45 +858,17 @@ function AgentFlowModal({
|
|||||||
type: "offline_daemon_action_attempt",
|
type: "offline_daemon_action_attempt",
|
||||||
action,
|
action,
|
||||||
daemonId: selectedDaemonId,
|
daemonId: selectedDaemonId,
|
||||||
activeDaemonId,
|
|
||||||
status: selectedDaemonStatus ?? null,
|
status: selectedDaemonStatus ?? null,
|
||||||
isBackground: isBackgroundDaemonTarget,
|
|
||||||
reason: reason ?? daemonAvailabilityError,
|
reason: reason ?? daemonAvailabilityError,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
activeDaemonId,
|
|
||||||
daemonAvailabilityError,
|
daemonAvailabilityError,
|
||||||
isBackgroundDaemonTarget,
|
|
||||||
selectedDaemonId,
|
selectedDaemonId,
|
||||||
selectedDaemonStatus,
|
selectedDaemonStatus,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const logBackgroundAgentAction = useCallback(
|
|
||||||
(
|
|
||||||
action: "create" | "resume",
|
|
||||||
payload: { cwd?: string; provider?: string; modeId?: string; model?: string; baseBranch?: string }
|
|
||||||
) => {
|
|
||||||
if (!isBackgroundDaemonTarget || !selectedDaemonId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
trackAnalyticsEvent({
|
|
||||||
type: "background_agent_action",
|
|
||||||
action,
|
|
||||||
daemonId: selectedDaemonId,
|
|
||||||
activeDaemonId,
|
|
||||||
isBackground: true,
|
|
||||||
cwd: payload.cwd,
|
|
||||||
provider: payload.provider,
|
|
||||||
modeId: payload.modeId,
|
|
||||||
model: payload.model,
|
|
||||||
baseBranch: payload.baseBranch,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[activeDaemonId, isBackgroundDaemonTarget, selectedDaemonId]
|
|
||||||
);
|
|
||||||
|
|
||||||
const resetFormState = useCallback(() => {
|
const resetFormState = useCallback(() => {
|
||||||
setInitialPrompt("");
|
setInitialPrompt("");
|
||||||
promptBaselineHeightRef.current = null;
|
promptBaselineHeightRef.current = null;
|
||||||
@@ -918,6 +886,7 @@ function AgentFlowModal({
|
|||||||
resetRepoInfo();
|
resetRepoInfo();
|
||||||
setOpenDropdown(null);
|
setOpenDropdown(null);
|
||||||
pendingRequestIdRef.current = null;
|
pendingRequestIdRef.current = null;
|
||||||
|
pendingNavigationServerIdRef.current = null;
|
||||||
shouldSyncBaseBranchRef.current = true;
|
shouldSyncBaseBranchRef.current = true;
|
||||||
dictationRequestIdRef.current = null;
|
dictationRequestIdRef.current = null;
|
||||||
setIsDictating(false);
|
setIsDictating(false);
|
||||||
@@ -1024,24 +993,23 @@ function AgentFlowModal({
|
|||||||
|
|
||||||
const navigateToAgentIfNeeded = useCallback(() => {
|
const navigateToAgentIfNeeded = useCallback(() => {
|
||||||
const agentId = pendingNavigationAgentIdRef.current;
|
const agentId = pendingNavigationAgentIdRef.current;
|
||||||
if (!agentId || !activeServerId) {
|
const targetServerId = pendingNavigationServerIdRef.current ?? selectedDaemonId;
|
||||||
|
if (!agentId || !targetServerId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingNavigationAgentIdRef.current = null;
|
pendingNavigationAgentIdRef.current = null;
|
||||||
if (activeDaemonId !== activeServerId) {
|
pendingNavigationServerIdRef.current = null;
|
||||||
setActiveDaemonId(activeServerId, { source: "agent_create_resume_nav" });
|
|
||||||
}
|
|
||||||
InteractionManager.runAfterInteractions(() => {
|
InteractionManager.runAfterInteractions(() => {
|
||||||
router.push({
|
router.push({
|
||||||
pathname: "/agent/[serverId]/[agentId]",
|
pathname: "/agent/[serverId]/[agentId]",
|
||||||
params: {
|
params: {
|
||||||
serverId: activeServerId,
|
serverId: targetServerId,
|
||||||
agentId,
|
agentId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, [activeDaemonId, activeServerId, router, setActiveDaemonId]);
|
}, [router, selectedDaemonId]);
|
||||||
|
|
||||||
const handleSelectServer = useCallback((serverId: string) => {
|
const handleSelectServer = useCallback((serverId: string) => {
|
||||||
setSelectedServerId(serverId);
|
setSelectedServerId(serverId);
|
||||||
@@ -1408,6 +1376,7 @@ function AgentFlowModal({
|
|||||||
const requestId = generateMessageId();
|
const requestId = generateMessageId();
|
||||||
|
|
||||||
pendingRequestIdRef.current = requestId;
|
pendingRequestIdRef.current = requestId;
|
||||||
|
pendingNavigationServerIdRef.current = selectedDaemonId ?? null;
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setErrorMessage("");
|
setErrorMessage("");
|
||||||
|
|
||||||
@@ -1443,14 +1412,6 @@ function AgentFlowModal({
|
|||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
logBackgroundAgentAction("create", {
|
|
||||||
cwd: trimmedPath,
|
|
||||||
provider: selectedProvider,
|
|
||||||
modeId,
|
|
||||||
model: trimmedModel,
|
|
||||||
baseBranch: trimmedBaseBranch || undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
createAgent({
|
createAgent({
|
||||||
config,
|
config,
|
||||||
@@ -1463,6 +1424,7 @@ function AgentFlowModal({
|
|||||||
setErrorMessage("Failed to create agent. Please try again.");
|
setErrorMessage("Failed to create agent. Please try again.");
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
pendingRequestIdRef.current = null;
|
pendingRequestIdRef.current = null;
|
||||||
|
pendingNavigationServerIdRef.current = null;
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
workingDir,
|
workingDir,
|
||||||
@@ -1475,7 +1437,6 @@ function AgentFlowModal({
|
|||||||
repoInfo,
|
repoInfo,
|
||||||
selectedMode,
|
selectedMode,
|
||||||
modeOptions,
|
modeOptions,
|
||||||
logBackgroundAgentAction,
|
|
||||||
logOfflineDaemonAction,
|
logOfflineDaemonAction,
|
||||||
selectedProvider,
|
selectedProvider,
|
||||||
isLoading,
|
isLoading,
|
||||||
@@ -1484,6 +1445,7 @@ function AgentFlowModal({
|
|||||||
createAgent,
|
createAgent,
|
||||||
daemonAvailabilityError,
|
daemonAvailabilityError,
|
||||||
isTargetDaemonReady,
|
isTargetDaemonReady,
|
||||||
|
selectedDaemonId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handleImportCandidatePress = useCallback(
|
const handleImportCandidatePress = useCallback(
|
||||||
@@ -1499,13 +1461,10 @@ function AgentFlowModal({
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logBackgroundAgentAction("resume", {
|
|
||||||
cwd: candidate.cwd,
|
|
||||||
provider: candidate.provider,
|
|
||||||
});
|
|
||||||
setErrorMessage("");
|
setErrorMessage("");
|
||||||
const requestId = generateMessageId();
|
const requestId = generateMessageId();
|
||||||
pendingRequestIdRef.current = requestId;
|
pendingRequestIdRef.current = requestId;
|
||||||
|
pendingNavigationServerIdRef.current = selectedDaemonId ?? null;
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
resumeAgent({
|
resumeAgent({
|
||||||
handle: candidate.persistence,
|
handle: candidate.persistence,
|
||||||
@@ -1515,10 +1474,10 @@ function AgentFlowModal({
|
|||||||
[
|
[
|
||||||
daemonAvailabilityError,
|
daemonAvailabilityError,
|
||||||
isLoading,
|
isLoading,
|
||||||
logBackgroundAgentAction,
|
|
||||||
logOfflineDaemonAction,
|
logOfflineDaemonAction,
|
||||||
isTargetDaemonReady,
|
isTargetDaemonReady,
|
||||||
resumeAgent,
|
resumeAgent,
|
||||||
|
selectedDaemonId,
|
||||||
setImportError,
|
setImportError,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@@ -1603,6 +1562,7 @@ function AgentFlowModal({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pendingRequestIdRef.current = null;
|
pendingRequestIdRef.current = null;
|
||||||
|
pendingNavigationServerIdRef.current = null;
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setErrorMessage(
|
setErrorMessage(
|
||||||
payload.error ??
|
payload.error ??
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { AudioLines, Users, Plus, Download } from "lucide-react-native";
|
|||||||
import { useRealtime } from "@/contexts/realtime-context";
|
import { useRealtime } from "@/contexts/realtime-context";
|
||||||
import { useSession } from "@/contexts/session-context";
|
import { useSession } from "@/contexts/session-context";
|
||||||
import { useFooterControls, FOOTER_HEIGHT } from "@/contexts/footer-controls-context";
|
import { useFooterControls, FOOTER_HEIGHT } from "@/contexts/footer-controls-context";
|
||||||
import { useDaemonConnections } from "@/contexts/daemon-connections-context";
|
|
||||||
import { RealtimeControls } from "./realtime-controls";
|
import { RealtimeControls } from "./realtime-controls";
|
||||||
import { CreateAgentModal, ImportAgentModal } from "./create-agent-modal";
|
import { CreateAgentModal, ImportAgentModal } from "./create-agent-modal";
|
||||||
import Animated, {
|
import Animated, {
|
||||||
@@ -24,7 +23,6 @@ export function GlobalFooter() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { isRealtimeMode, startRealtime } = useRealtime();
|
const { isRealtimeMode, startRealtime } = useRealtime();
|
||||||
const { ws } = useSession();
|
const { ws } = useSession();
|
||||||
const { activeDaemonId } = useDaemonConnections();
|
|
||||||
const { controls } = useFooterControls();
|
const { controls } = useFooterControls();
|
||||||
const [showCreateModal, setShowCreateModal] = useState(false);
|
const [showCreateModal, setShowCreateModal] = useState(false);
|
||||||
const [showImportModal, setShowImportModal] = useState(false);
|
const [showImportModal, setShowImportModal] = useState(false);
|
||||||
@@ -188,12 +186,10 @@ export function GlobalFooter() {
|
|||||||
<CreateAgentModal
|
<CreateAgentModal
|
||||||
isVisible={showCreateModal}
|
isVisible={showCreateModal}
|
||||||
onClose={() => setShowCreateModal(false)}
|
onClose={() => setShowCreateModal(false)}
|
||||||
serverId={activeDaemonId}
|
|
||||||
/>
|
/>
|
||||||
<ImportAgentModal
|
<ImportAgentModal
|
||||||
isVisible={showImportModal}
|
isVisible={showImportModal}
|
||||||
onClose={() => setShowImportModal(false)}
|
onClose={() => setShowImportModal(false)}
|
||||||
serverId={activeDaemonId}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -26,9 +26,8 @@ const sortAgents = (agents: Agent[]): Agent[] => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function useAggregatedAgents(fallbackAgents: Map<string, Agent>): AggregatedAgentGroup[] {
|
export function useAggregatedAgents(fallbackAgents: Map<string, Agent>): AggregatedAgentGroup[] {
|
||||||
const { activeDaemonId, connectionStates } = useDaemonConnections();
|
const { connectionStates } = useDaemonConnections();
|
||||||
const sessionDirectory = useSessionDirectory();
|
const sessionDirectory = useSessionDirectory();
|
||||||
const activeServerId = activeDaemonId ?? "default";
|
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const groups = new Map<string, { serverLabel: string; agents: Agent[] }>();
|
const groups = new Map<string, { serverLabel: string; agents: Agent[] }>();
|
||||||
@@ -57,10 +56,11 @@ export function useAggregatedAgents(fallbackAgents: Map<string, Agent>): Aggrega
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (groups.size === 0) {
|
if (groups.size === 0) {
|
||||||
const label = connectionStates.get(activeServerId)?.daemon.label ?? activeServerId;
|
const fallbackServerId = connectionStates.keys().next().value ?? "default";
|
||||||
|
const label = connectionStates.get(fallbackServerId)?.daemon.label ?? fallbackServerId;
|
||||||
const fallbackList = Array.from(fallbackAgents.values());
|
const fallbackList = Array.from(fallbackAgents.values());
|
||||||
if (fallbackList.length > 0) {
|
if (fallbackList.length > 0) {
|
||||||
groups.set(activeServerId, { serverLabel: label, agents: fallbackList });
|
groups.set(fallbackServerId, { serverLabel: label, agents: fallbackList });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,5 +86,5 @@ export function useAggregatedAgents(fallbackAgents: Map<string, Agent>): Aggrega
|
|||||||
});
|
});
|
||||||
|
|
||||||
return aggregatedGroups;
|
return aggregatedGroups;
|
||||||
}, [sessionDirectory, fallbackAgents, activeServerId, connectionStates]);
|
}, [sessionDirectory, fallbackAgents, connectionStates]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
type BackgroundAgentAction = "create" | "resume";
|
|
||||||
type OfflineAction =
|
type OfflineAction =
|
||||||
| "create"
|
| "create"
|
||||||
| "resume"
|
| "resume"
|
||||||
@@ -12,25 +11,11 @@ type AnalyticsEvent =
|
|||||||
previousDaemonId: string | null;
|
previousDaemonId: string | null;
|
||||||
source?: string;
|
source?: string;
|
||||||
}
|
}
|
||||||
| {
|
|
||||||
type: "background_agent_action";
|
|
||||||
action: BackgroundAgentAction;
|
|
||||||
daemonId: string;
|
|
||||||
activeDaemonId: string | null;
|
|
||||||
isBackground: boolean;
|
|
||||||
cwd?: string;
|
|
||||||
provider?: string;
|
|
||||||
modeId?: string;
|
|
||||||
model?: string;
|
|
||||||
baseBranch?: string;
|
|
||||||
}
|
|
||||||
| {
|
| {
|
||||||
type: "offline_daemon_action_attempt";
|
type: "offline_daemon_action_attempt";
|
||||||
action: OfflineAction;
|
action: OfflineAction;
|
||||||
daemonId: string | null;
|
daemonId: string | null;
|
||||||
activeDaemonId: string | null;
|
|
||||||
status: string | null;
|
status: string | null;
|
||||||
isBackground: boolean;
|
|
||||||
reason?: string | null;
|
reason?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,12 +22,16 @@ The multi-daemon infrastructure is in place: session directory with daemon-scope
|
|||||||
|
|
||||||
### 2. Remove Active/Primary/Auto-Connect Concepts
|
### 2. Remove Active/Primary/Auto-Connect Concepts
|
||||||
- [x] Remove the concept of "active daemon" from the UI and simplify to just "hosts".
|
- [x] Remove the concept of "active daemon" from the UI and simplify to just "hosts".
|
||||||
- Removed the Settings "Set Active" controls/labels, scrubbed the copy that mentioned an active host (including git diff fallback text), and left the per-host cards focused on status/default/test actions; no automated tests were run.
|
- Summary: Home/footer actions, create/import flows, and agent navigation now route directly via explicit host IDs with analytics/docs/aggregated list updates, verified with `npm run typecheck --workspace=@paseo/app`.
|
||||||
- [x] Remove the concept of "primary daemon"—no default host for actions.
|
- Context: `GlobalFooter`, the create/import modals, the agent screen, and the agent list previously read/write `activeDaemonId`, forcing a global active host before routing; they now rely on explicit host IDs instead.
|
||||||
- Removed the Default host toggle/state in the registry and settings UI, updated host docs, and kept connection fallback logic simple; tests not run (not requested).
|
- [ ] Remove the concept of "primary daemon"—no default host for actions.
|
||||||
|
- Context: `DaemonConnectionsProvider` still persists `activeDaemonId` in AsyncStorage and `MultiDaemonSessionHost` filters by `primary` roles to decide which hosts stay backgrounded, so the “primary/default” pipeline is intact and needs to be unwound.
|
||||||
- [ ] Remove the "auto-connect" toggle from host settings—hosts always auto-connect when added.
|
- [ ] Remove the "auto-connect" toggle from host settings—hosts always auto-connect when added.
|
||||||
- [ ] Clean up any related state/UI that exposes these concepts to users.
|
- [ ] Clean up any related state/UI that exposes these concepts to users.
|
||||||
|
|
||||||
|
### Review: No Silent Defaults
|
||||||
|
- [ ] Review the changes to ensure we didn't just replace "active daemon" with "first host" (e.g., `hosts[0]}`. The goal is explicit user choice, not a hidden default.
|
||||||
|
|
||||||
### 3. Simplify Settings Screen
|
### 3. Simplify Settings Screen
|
||||||
- [ ] Remove the standalone "Test Connection" form/URL input at the top of settings.
|
- [ ] Remove the standalone "Test Connection" form/URL input at the top of settings.
|
||||||
- [ ] Keep the per-host "Test" button in each host row (already exists).
|
- [ ] Keep the per-host "Test" button in each host row (already exists).
|
||||||
@@ -49,5 +53,5 @@ The multi-daemon infrastructure is in place: session directory with daemon-scope
|
|||||||
|
|
||||||
### 7. Home Screen Agent List
|
### 7. Home Screen Agent List
|
||||||
- [ ] Remove grouping of agents by host—show a single flat list.
|
- [ ] Remove grouping of agents by host—show a single flat list.
|
||||||
- [ ] Each agent row displays its host name as metadata (badge, subtitle, etc.).
|
- [ ] Each agent row displays its host name as metadata (badge, subtitle, etc.).
|
||||||
- [ ] Sort agents by recent activity or alphabetically (not by host).
|
- [ ] Sort agents by recent activity or alphabetically (not by host).
|
||||||
|
|||||||
Reference in New Issue
Block a user