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 {
return {
agent: null,
@@ -357,6 +370,86 @@ describe("deriveAgentScreenViewState", () => {
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", () => {
const memory = createBaseMemory({
hasRenderedReady: true,
@@ -93,7 +93,17 @@ export function deriveAgentScreenViewState({
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) {
nextMemory.hasRenderedReady = true;
nextMemory.lastReadyAgent = candidateAgent;
@@ -132,11 +142,13 @@ export function deriveAgentScreenViewState({
};
}
const source: "authoritative" | "optimistic" | "stale" = input.agent
? "authoritative"
: input.shouldUseOptimisticStream
? "optimistic"
: "stale";
const source: "authoritative" | "optimistic" | "stale" = useOptimisticCreateFlowAgent
? "optimistic"
: input.agent
? "authoritative"
: input.shouldUseOptimisticStream
? "optimistic"
: "stale";
let sync: AgentScreenReadySyncState;
if (!input.isConnected) {
@@ -652,6 +652,14 @@ function AgentScreenContent({
}, [optimisticStreamItems, streamItems]);
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(() => {
if (!shouldUseOptimisticStream || !resolvedAgentId) {
@@ -730,7 +738,7 @@ function AgentScreenContent({
item.kind === "user_message" &&
item.id === pendingCreate.clientMessageId
);
if (agent && hasUserMessage) {
if (hasUserMessage && canFinalizePendingCreate) {
if (
resolvedAgentId &&
pendingCreate.images &&
@@ -760,7 +768,7 @@ function AgentScreenContent({
clearPendingCreate();
}
}, [
agent,
canFinalizePendingCreate,
clearPendingCreate,
isPendingCreateForRoute,
markPendingCreateLifecycle,
@@ -1239,6 +1247,7 @@ function AgentScreenContent({
agentId={resolvedAgentId}
serverId={serverId}
autoFocus
isSubmitLoading={showPendingCreateSubmitLoading}
onAddImages={handleAddImagesCallback}
/>
)}