fix: keep optimistic status during agent bootstrap phase
This commit is contained in:
@@ -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}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user