refactor: upgrade claude-code-acp to v0.8.3 and optimize agent initialization flow

This commit is contained in:
Mohamed Boudra
2025-10-25 19:37:13 +02:00
parent 42e5f864a6
commit 1fd9b77680
5 changed files with 42 additions and 56 deletions

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from "react";
import { useEffect, useMemo } from "react";
import { View, Text, ActivityIndicator } from "react-native";
import { useLocalSearchParams } from "expo-router";
import { useSafeAreaInsets } from "react-native-safe-area-context";
@@ -18,7 +18,6 @@ export default function AgentScreen() {
const { id } = useLocalSearchParams<{ id: string }>();
const { agents, agentStreamState, pendingPermissions, respondToPermission, initializeAgent } = useSession();
const { registerFooterControls, unregisterFooterControls } = useFooterControls();
const [isContentReady, setIsContentReady] = useState(false);
// Keyboard animation
const { height: keyboardHeight } = useReanimatedKeyboardAnimation();
@@ -38,6 +37,9 @@ export default function AgentScreen() {
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(() => {
if (!id) {
return;
@@ -50,39 +52,8 @@ export default function AgentScreen() {
return <AgentInputArea agentId={id} />;
}, [id]);
// Defer heavy rendering to next frame for faster initial paint
useEffect(() => {
let isMounted = true;
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) {
if (!agentControls || !agent || isInitializing) {
unregisterFooterControls();
return;
}
@@ -92,7 +63,7 @@ export default function AgentScreen() {
return () => {
unregisterFooterControls();
};
}, [agentControls, agent, isContentReady, registerFooterControls, unregisterFooterControls]);
}, [agentControls, agent, isInitializing, registerFooterControls, unregisterFooterControls]);
if (!agent) {
return (
@@ -112,7 +83,12 @@ export default function AgentScreen() {
{/* Content Area with Keyboard Animation */}
<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
agentId={id!}
@@ -129,10 +105,6 @@ export default function AgentScreen() {
<AgentStatusBar agentId={id!} />
</View>
</>
) : (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={theme.colors.primary} />
</View>
)}
</ReanimatedAnimated.View>
</View>
@@ -154,6 +126,11 @@ const styles = StyleSheet.create((theme) => ({
flex: 1,
alignItems: "center",
justifyContent: "center",
gap: 16,
},
loadingText: {
fontSize: theme.fontSize.base,
color: theme.colors.mutedForeground,
},
errorContainer: {
flex: 1,

View File

@@ -1,4 +1,4 @@
import { useEffect, useRef, useState, useCallback } from 'react';
import { useEffect, useRef, useState, useCallback, useMemo } from 'react';
import type {
WSInboundMessage,
WSOutboundMessage,
@@ -150,12 +150,15 @@ export function useWebSocket(url: string, conversationId?: string | null): UseWe
[send]
);
return {
isConnected,
conversationId: currentConversationId,
send,
on,
sendPing,
sendUserMessage,
};
return useMemo(
() => ({
isConnected,
conversationId: currentConversationId,
send,
on,
sendPing,
sendUserMessage,
}),
[isConnected, currentConversationId, send, on, sendPing, sendUserMessage]
);
}

View File

@@ -53,5 +53,16 @@
},
"createdAt": "2025-10-25T12:44:52.183Z",
"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"
}
]

View File

@@ -18,7 +18,7 @@
"dependencies": {
"@agentclientprotocol/sdk": "^0.4.9",
"@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",
"@modelcontextprotocol/sdk": "^1.20.1",
"@openrouter/ai-sdk-provider": "^1.2.0",

View File

@@ -923,12 +923,7 @@ export class Session {
private async sendSessionState(): Promise<void> {
try {
// Get live agents with session modes
const agents = this.agentManager?.listAgents() || [];
// Subscribe to all existing agents so future updates stream through
for (const agent of agents) {
this.subscribeToAgent(agent.id);
}
const agents = this.agentManager.listAgents();
// Get live commands from terminal manager
let commands: any[] = [];