fix: keep optimistic status during agent bootstrap phase

This commit is contained in:
Mohamed Boudra
2026-02-22 20:01:05 +07:00
parent 1974c33971
commit 84e3140f19
3 changed files with 122 additions and 8 deletions
@@ -47,6 +47,19 @@ function createAgent(id: string): Agent {
}; };
} }
function createAgentWithStatus({
id,
status,
}: {
id: string;
status: Agent["status"];
}): Agent {
return {
...createAgent(id),
status,
};
}
function createBaseInput(): AgentScreenMachineInput { function createBaseInput(): AgentScreenMachineInput {
return { return {
agent: null, agent: null,
@@ -357,6 +370,86 @@ describe("deriveAgentScreenViewState", () => {
expect(handoffReady.agent.id).toBe("draft-agent"); expect(handoffReady.agent.id).toBe("draft-agent");
}); });
it("keeps optimistic running status while authoritative agent is still bootstrapping", () => {
const memory = createBaseMemory();
const input: AgentScreenMachineInput = {
...createBaseInput(),
agent: createAgentWithStatus({ id: "agent-1", status: "idle" }),
placeholderAgent: createAgent("agent-1"),
shouldUseOptimisticStream: true,
};
const result = deriveAgentScreenViewState({ input, memory });
const ready = expectReadyState(result.state);
expect(ready.source).toBe("optimistic");
expect(ready.agent.status).toBe("running");
});
it("keeps optimistic running status while authoritative agent is initializing", () => {
const memory = createBaseMemory();
const input: AgentScreenMachineInput = {
...createBaseInput(),
agent: createAgentWithStatus({ id: "agent-1", status: "initializing" }),
placeholderAgent: createAgent("agent-1"),
shouldUseOptimisticStream: true,
};
const result = deriveAgentScreenViewState({ input, memory });
const ready = expectReadyState(result.state);
expect(ready.source).toBe("optimistic");
expect(ready.agent.status).toBe("running");
});
it("hands off to authoritative once agent reaches running", () => {
const memory = createBaseMemory();
const input: AgentScreenMachineInput = {
...createBaseInput(),
agent: createAgentWithStatus({ id: "agent-1", status: "running" }),
placeholderAgent: createAgent("agent-1"),
shouldUseOptimisticStream: true,
};
const result = deriveAgentScreenViewState({ input, memory });
const ready = expectReadyState(result.state);
expect(ready.source).toBe("authoritative");
expect(ready.agent.status).toBe("running");
});
it("hands off to authoritative for terminal error states", () => {
const memory = createBaseMemory();
const input: AgentScreenMachineInput = {
...createBaseInput(),
agent: createAgentWithStatus({ id: "agent-1", status: "error" }),
placeholderAgent: createAgent("agent-1"),
shouldUseOptimisticStream: true,
};
const result = deriveAgentScreenViewState({ input, memory });
const ready = expectReadyState(result.state);
expect(ready.source).toBe("authoritative");
expect(ready.agent.status).toBe("error");
});
it("hands off to authoritative for terminal closed states", () => {
const memory = createBaseMemory();
const input: AgentScreenMachineInput = {
...createBaseInput(),
agent: createAgentWithStatus({ id: "agent-1", status: "closed" }),
placeholderAgent: createAgent("agent-1"),
shouldUseOptimisticStream: true,
};
const result = deriveAgentScreenViewState({ input, memory });
const ready = expectReadyState(result.state);
expect(ready.source).toBe("authoritative");
expect(ready.agent.status).toBe("closed");
});
it("emits history refresh toast only on transition into toast catch-up state", () => { it("emits history refresh toast only on transition into toast catch-up state", () => {
const memory = createBaseMemory({ const memory = createBaseMemory({
hasRenderedReady: true, hasRenderedReady: true,
@@ -93,7 +93,17 @@ export function deriveAgentScreenViewState({
nextMemory.hadInitialSyncFailure = true; nextMemory.hadInitialSyncFailure = true;
} }
const candidateAgent = input.agent ?? input.placeholderAgent; const useOptimisticCreateFlowAgent =
input.shouldUseOptimisticStream &&
Boolean(input.placeholderAgent) &&
(!input.agent ||
input.agent.status === "initializing" ||
input.agent.status === "idle");
const candidateAgent =
input.agent && useOptimisticCreateFlowAgent && input.placeholderAgent
? { ...input.agent, status: input.placeholderAgent.status }
: input.agent ?? input.placeholderAgent;
if (candidateAgent) { if (candidateAgent) {
nextMemory.hasRenderedReady = true; nextMemory.hasRenderedReady = true;
nextMemory.lastReadyAgent = candidateAgent; nextMemory.lastReadyAgent = candidateAgent;
@@ -132,11 +142,13 @@ export function deriveAgentScreenViewState({
}; };
} }
const source: "authoritative" | "optimistic" | "stale" = input.agent const source: "authoritative" | "optimistic" | "stale" = useOptimisticCreateFlowAgent
? "authoritative" ? "optimistic"
: input.shouldUseOptimisticStream : input.agent
? "optimistic" ? "authoritative"
: "stale"; : input.shouldUseOptimisticStream
? "optimistic"
: "stale";
let sync: AgentScreenReadySyncState; let sync: AgentScreenReadySyncState;
if (!input.isConnected) { if (!input.isConnected) {
@@ -652,6 +652,14 @@ function AgentScreenContent({
}, [optimisticStreamItems, streamItems]); }, [optimisticStreamItems, streamItems]);
const shouldUseOptimisticStream = isPendingCreateForRoute && optimisticStreamItems.length > 0; const shouldUseOptimisticStream = isPendingCreateForRoute && optimisticStreamItems.length > 0;
const authoritativeStatus = agent?.status;
const isAuthoritativeBootstrapping =
authoritativeStatus === "initializing" || authoritativeStatus === "idle";
const showPendingCreateSubmitLoading =
isPendingCreateForRoute &&
(!authoritativeStatus || isAuthoritativeBootstrapping);
const canFinalizePendingCreate =
Boolean(authoritativeStatus) && !isAuthoritativeBootstrapping;
const placeholderAgent: Agent | null = useMemo(() => { const placeholderAgent: Agent | null = useMemo(() => {
if (!shouldUseOptimisticStream || !resolvedAgentId) { if (!shouldUseOptimisticStream || !resolvedAgentId) {
@@ -730,7 +738,7 @@ function AgentScreenContent({
item.kind === "user_message" && item.kind === "user_message" &&
item.id === pendingCreate.clientMessageId item.id === pendingCreate.clientMessageId
); );
if (agent && hasUserMessage) { if (hasUserMessage && canFinalizePendingCreate) {
if ( if (
resolvedAgentId && resolvedAgentId &&
pendingCreate.images && pendingCreate.images &&
@@ -760,7 +768,7 @@ function AgentScreenContent({
clearPendingCreate(); clearPendingCreate();
} }
}, [ }, [
agent, canFinalizePendingCreate,
clearPendingCreate, clearPendingCreate,
isPendingCreateForRoute, isPendingCreateForRoute,
markPendingCreateLifecycle, markPendingCreateLifecycle,
@@ -1239,6 +1247,7 @@ function AgentScreenContent({
agentId={resolvedAgentId} agentId={resolvedAgentId}
serverId={serverId} serverId={serverId}
autoFocus autoFocus
isSubmitLoading={showPendingCreateSubmitLoading}
onAddImages={handleAddImagesCallback} onAddImages={handleAddImagesCallback}
/> />
)} )}