fix(agent): restore MCP wait timeout and fix direct URL navigation
- Restore 50-second self-imposed timeout for agent wait operations in MCP server. Returns friendly message instead of letting SDK timeout trigger generic "tool failed" error that confuses orchestrator agents. - Fix direct URL navigation to agent screens by waiting for WebSocket connection before initializing. Previously, init would fire before WS connected, fail silently, and never retry due to guard ref. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
348242541c
commit
33a3031f74
@@ -438,6 +438,11 @@ function AgentScreenContent({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip if not connected - will re-run when connection is established
|
||||||
|
if (!ws?.isConnected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Skip if already initializing
|
// Skip if already initializing
|
||||||
if (isInitializingFromMap === true) {
|
if (isInitializingFromMap === true) {
|
||||||
return;
|
return;
|
||||||
@@ -459,7 +464,7 @@ function AgentScreenContent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
initializeAgent({ agentId: resolvedAgentId });
|
initializeAgent({ agentId: resolvedAgentId });
|
||||||
}, [resolvedAgentId, initializeAgent, isInitializingFromMap]);
|
}, [resolvedAgentId, initializeAgent, isInitializingFromMap, ws?.isConnected]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Platform.OS !== "web") {
|
if (Platform.OS !== "web") {
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ const AgentStatusEnum = z.enum([
|
|||||||
"closed",
|
"closed",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// 50 seconds - surface friendly message before SDK tool timeout (~60s)
|
||||||
|
const AGENT_WAIT_TIMEOUT_MS = 50000;
|
||||||
|
|
||||||
function expandPath(path: string): string {
|
function expandPath(path: string): string {
|
||||||
if (path.startsWith("~/") || path === "~") {
|
if (path.startsWith("~/") || path === "~") {
|
||||||
return resolve(homedir(), path.slice(2));
|
return resolve(homedir(), path.slice(2));
|
||||||
@@ -99,6 +102,74 @@ function expandPath(path: string): string {
|
|||||||
return resolve(path);
|
return resolve(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps agentManager.waitForAgentEvent with a self-imposed timeout.
|
||||||
|
* Returns a friendly message when timeout occurs, rather than letting
|
||||||
|
* the SDK tool timeout trigger a generic "tool failed" error.
|
||||||
|
*/
|
||||||
|
async function waitForAgentWithTimeout(
|
||||||
|
agentManager: AgentManager,
|
||||||
|
agentId: string,
|
||||||
|
options?: {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
waitForActive?: boolean;
|
||||||
|
}
|
||||||
|
): Promise<WaitForAgentResult> {
|
||||||
|
const timeoutController = new AbortController();
|
||||||
|
const combinedController = new AbortController();
|
||||||
|
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
timeoutController.abort(new Error("wait timeout"));
|
||||||
|
}, AGENT_WAIT_TIMEOUT_MS);
|
||||||
|
|
||||||
|
const forwardAbort = (reason: unknown) => {
|
||||||
|
if (!combinedController.signal.aborted) {
|
||||||
|
combinedController.abort(reason);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Forward external signal abort
|
||||||
|
if (options?.signal) {
|
||||||
|
if (options.signal.aborted) {
|
||||||
|
forwardAbort(options.signal.reason);
|
||||||
|
} else {
|
||||||
|
options.signal.addEventListener(
|
||||||
|
"abort",
|
||||||
|
() => forwardAbort(options.signal!.reason),
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward timeout abort
|
||||||
|
timeoutController.signal.addEventListener(
|
||||||
|
"abort",
|
||||||
|
() => forwardAbort(timeoutController.signal.reason),
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await agentManager.waitForAgentEvent(agentId, {
|
||||||
|
signal: combinedController.signal,
|
||||||
|
waitForActive: options?.waitForActive,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error && error.message === "wait timeout") {
|
||||||
|
const snapshot = agentManager.getAgent(agentId);
|
||||||
|
return {
|
||||||
|
status: snapshot?.lifecycle ?? "idle",
|
||||||
|
permission: null,
|
||||||
|
lastMessage:
|
||||||
|
"Awaiting the agent timed out. This does not mean the agent failed - call wait_for_agent again to continue waiting.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function startAgentRun(
|
function startAgentRun(
|
||||||
agentManager: AgentManager,
|
agentManager: AgentManager,
|
||||||
agentId: string,
|
agentId: string,
|
||||||
@@ -373,9 +444,11 @@ export async function createAgentMcpServer(
|
|||||||
|
|
||||||
// If not running in background, wait for completion
|
// If not running in background, wait for completion
|
||||||
if (!background) {
|
if (!background) {
|
||||||
const result = await agentManager.waitForAgentEvent(snapshot.id, {
|
const result = await waitForAgentWithTimeout(
|
||||||
waitForActive: true,
|
agentManager,
|
||||||
});
|
snapshot.id,
|
||||||
|
{ waitForActive: true }
|
||||||
|
);
|
||||||
|
|
||||||
const responseData = {
|
const responseData = {
|
||||||
agentId: snapshot.id,
|
agentId: snapshot.id,
|
||||||
@@ -481,10 +554,11 @@ export async function createAgentMcpServer(
|
|||||||
cleanupFns.push(unregister);
|
cleanupFns.push(unregister);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result: WaitForAgentResult =
|
const result: WaitForAgentResult = await waitForAgentWithTimeout(
|
||||||
await agentManager.waitForAgentEvent(agentId, {
|
agentManager,
|
||||||
signal: abortController.signal,
|
agentId,
|
||||||
});
|
{ signal: abortController.signal }
|
||||||
|
);
|
||||||
|
|
||||||
const validJson = ensureValidJson({
|
const validJson = ensureValidJson({
|
||||||
agentId,
|
agentId,
|
||||||
@@ -596,7 +670,7 @@ export async function createAgentMcpServer(
|
|||||||
|
|
||||||
// If not running in background, wait for completion
|
// If not running in background, wait for completion
|
||||||
if (!background) {
|
if (!background) {
|
||||||
const result = await agentManager.waitForAgentEvent(agentId, {
|
const result = await waitForAgentWithTimeout(agentManager, agentId, {
|
||||||
waitForActive: true,
|
waitForActive: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user