refactor: upgrade claude-code-acp to v0.8.3 and optimize agent initialization flow
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import { View, Text, ActivityIndicator } from "react-native";
|
import { View, Text, ActivityIndicator } from "react-native";
|
||||||
import { useLocalSearchParams } from "expo-router";
|
import { useLocalSearchParams } from "expo-router";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
@@ -18,7 +18,6 @@ export default function AgentScreen() {
|
|||||||
const { id } = useLocalSearchParams<{ id: string }>();
|
const { id } = useLocalSearchParams<{ id: string }>();
|
||||||
const { agents, agentStreamState, pendingPermissions, respondToPermission, initializeAgent } = useSession();
|
const { agents, agentStreamState, pendingPermissions, respondToPermission, initializeAgent } = useSession();
|
||||||
const { registerFooterControls, unregisterFooterControls } = useFooterControls();
|
const { registerFooterControls, unregisterFooterControls } = useFooterControls();
|
||||||
const [isContentReady, setIsContentReady] = useState(false);
|
|
||||||
|
|
||||||
// Keyboard animation
|
// Keyboard animation
|
||||||
const { height: keyboardHeight } = useReanimatedKeyboardAnimation();
|
const { height: keyboardHeight } = useReanimatedKeyboardAnimation();
|
||||||
@@ -38,6 +37,9 @@ export default function AgentScreen() {
|
|||||||
Array.from(pendingPermissions.entries()).filter(([_, perm]) => perm.agentId === id)
|
Array.from(pendingPermissions.entries()).filter(([_, perm]) => perm.agentId === id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Agent is initializing if we don't have stream state yet
|
||||||
|
const isInitializing = id ? !agentStreamState.has(id) : false;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return;
|
return;
|
||||||
@@ -50,39 +52,8 @@ export default function AgentScreen() {
|
|||||||
return <AgentInputArea agentId={id} />;
|
return <AgentInputArea agentId={id} />;
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
// Defer heavy rendering to next frame for faster initial paint
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true;
|
if (!agentControls || !agent || isInitializing) {
|
||||||
let frameId: number | null = null;
|
|
||||||
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
||||||
|
|
||||||
const markReady = () => {
|
|
||||||
if (isMounted) {
|
|
||||||
setIsContentReady(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof requestAnimationFrame === "function") {
|
|
||||||
frameId = requestAnimationFrame(markReady);
|
|
||||||
} else {
|
|
||||||
timeoutId = setTimeout(markReady, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
isMounted = false;
|
|
||||||
|
|
||||||
if (frameId !== null && typeof cancelAnimationFrame === "function") {
|
|
||||||
cancelAnimationFrame(frameId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeoutId !== null) {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!agentControls || !agent || !isContentReady) {
|
|
||||||
unregisterFooterControls();
|
unregisterFooterControls();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -92,7 +63,7 @@ export default function AgentScreen() {
|
|||||||
return () => {
|
return () => {
|
||||||
unregisterFooterControls();
|
unregisterFooterControls();
|
||||||
};
|
};
|
||||||
}, [agentControls, agent, isContentReady, registerFooterControls, unregisterFooterControls]);
|
}, [agentControls, agent, isInitializing, registerFooterControls, unregisterFooterControls]);
|
||||||
|
|
||||||
if (!agent) {
|
if (!agent) {
|
||||||
return (
|
return (
|
||||||
@@ -112,7 +83,12 @@ export default function AgentScreen() {
|
|||||||
|
|
||||||
{/* Content Area with Keyboard Animation */}
|
{/* Content Area with Keyboard Animation */}
|
||||||
<ReanimatedAnimated.View style={[styles.content, animatedKeyboardStyle]}>
|
<ReanimatedAnimated.View style={[styles.content, animatedKeyboardStyle]}>
|
||||||
{isContentReady ? (
|
{isInitializing ? (
|
||||||
|
<View style={styles.loadingContainer}>
|
||||||
|
<ActivityIndicator size="large" color={theme.colors.primary} />
|
||||||
|
<Text style={styles.loadingText}>Loading agent...</Text>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
<>
|
<>
|
||||||
<AgentStreamView
|
<AgentStreamView
|
||||||
agentId={id!}
|
agentId={id!}
|
||||||
@@ -129,10 +105,6 @@ export default function AgentScreen() {
|
|||||||
<AgentStatusBar agentId={id!} />
|
<AgentStatusBar agentId={id!} />
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
) : (
|
|
||||||
<View style={styles.loadingContainer}>
|
|
||||||
<ActivityIndicator size="large" color={theme.colors.primary} />
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
</ReanimatedAnimated.View>
|
</ReanimatedAnimated.View>
|
||||||
</View>
|
</View>
|
||||||
@@ -154,6 +126,11 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
|
gap: 16,
|
||||||
|
},
|
||||||
|
loadingText: {
|
||||||
|
fontSize: theme.fontSize.base,
|
||||||
|
color: theme.colors.mutedForeground,
|
||||||
},
|
},
|
||||||
errorContainer: {
|
errorContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef, useState, useCallback } from 'react';
|
import { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
||||||
import type {
|
import type {
|
||||||
WSInboundMessage,
|
WSInboundMessage,
|
||||||
WSOutboundMessage,
|
WSOutboundMessage,
|
||||||
@@ -150,12 +150,15 @@ export function useWebSocket(url: string, conversationId?: string | null): UseWe
|
|||||||
[send]
|
[send]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return useMemo(
|
||||||
isConnected,
|
() => ({
|
||||||
conversationId: currentConversationId,
|
isConnected,
|
||||||
send,
|
conversationId: currentConversationId,
|
||||||
on,
|
send,
|
||||||
sendPing,
|
on,
|
||||||
sendUserMessage,
|
sendPing,
|
||||||
};
|
sendUserMessage,
|
||||||
|
}),
|
||||||
|
[isConnected, currentConversationId, send, on, sendPing, sendUserMessage]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,5 +53,16 @@
|
|||||||
},
|
},
|
||||||
"createdAt": "2025-10-25T12:44:52.183Z",
|
"createdAt": "2025-10-25T12:44:52.183Z",
|
||||||
"cwd": "/Users/moboudra/dev/faro/main"
|
"cwd": "/Users/moboudra/dev/faro/main"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "415c2eb6-6649-4f3f-aee5-acc3d6b2cfd4",
|
||||||
|
"title": "Agent 415c2eb6",
|
||||||
|
"sessionId": "019a1c5a-0929-76de-ad58-1b80fa056209",
|
||||||
|
"options": {
|
||||||
|
"type": "claude",
|
||||||
|
"sessionId": null
|
||||||
|
},
|
||||||
|
"createdAt": "2025-10-25T17:10:52.912Z",
|
||||||
|
"cwd": "/Users/moboudra/dev/faro/main"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@agentclientprotocol/sdk": "^0.4.9",
|
"@agentclientprotocol/sdk": "^0.4.9",
|
||||||
"@ai-sdk/openai": "^2.0.52",
|
"@ai-sdk/openai": "^2.0.52",
|
||||||
"@boudra/claude-code-acp": "^0.8.2",
|
"@boudra/claude-code-acp": "^0.8.3",
|
||||||
"@deepgram/sdk": "^3.4.0",
|
"@deepgram/sdk": "^3.4.0",
|
||||||
"@modelcontextprotocol/sdk": "^1.20.1",
|
"@modelcontextprotocol/sdk": "^1.20.1",
|
||||||
"@openrouter/ai-sdk-provider": "^1.2.0",
|
"@openrouter/ai-sdk-provider": "^1.2.0",
|
||||||
|
|||||||
@@ -923,12 +923,7 @@ export class Session {
|
|||||||
private async sendSessionState(): Promise<void> {
|
private async sendSessionState(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// Get live agents with session modes
|
// Get live agents with session modes
|
||||||
const agents = this.agentManager?.listAgents() || [];
|
const agents = this.agentManager.listAgents();
|
||||||
|
|
||||||
// Subscribe to all existing agents so future updates stream through
|
|
||||||
for (const agent of agents) {
|
|
||||||
this.subscribeToAgent(agent.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get live commands from terminal manager
|
// Get live commands from terminal manager
|
||||||
let commands: any[] = [];
|
let commands: any[] = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user