Fix bottom anchoring and Claude wake routing
This commit is contained in:
@@ -64,4 +64,10 @@ describe("useAgentInitialization timeline request policy", () => {
|
||||
projection: "canonical",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not expose an RPC-success init fallback", () => {
|
||||
expect(
|
||||
"shouldResolveInitFromRpcSuccess" in __private__
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,7 +62,7 @@ export function useAgentInitialization({
|
||||
const cursor = session?.agentTimelineCursor.get(agentId);
|
||||
const initialTimelineLimit = resolveInitialTimelineLimit();
|
||||
const hasAuthoritativeHistory =
|
||||
(session?.agentHistorySyncGeneration.get(agentId) ?? -1) >= 0;
|
||||
session?.agentAuthoritativeHistoryApplied.get(agentId) === true;
|
||||
const timelineRequest = deriveInitialTimelineRequest({
|
||||
cursor: cursor
|
||||
? { epoch: cursor.epoch, seq: cursor.endSeq }
|
||||
@@ -95,10 +95,6 @@ export function useAgentInitialization({
|
||||
|
||||
client
|
||||
.fetchAgentTimeline(agentId, timelineRequest)
|
||||
.then(() => {
|
||||
// No-op: hydration completion is handled by SessionContext
|
||||
// when it processes fetch_agent_timeline_response.
|
||||
})
|
||||
.catch((error) => {
|
||||
setAgentInitializing(agentId, false);
|
||||
rejectInitDeferred(
|
||||
|
||||
@@ -342,6 +342,46 @@ describe("deriveAgentScreenViewState", () => {
|
||||
expect(ready.sync.status).toBe("idle");
|
||||
});
|
||||
|
||||
it("keeps first route entry blocked until authoritative history is applied", () => {
|
||||
const memory = createBaseMemory();
|
||||
const input: AgentScreenMachineInput = {
|
||||
...createBaseInput(),
|
||||
agent: createAgent("agent-1"),
|
||||
needsAuthoritativeSync: true,
|
||||
isHistorySyncing: true,
|
||||
hasHydratedHistoryBefore: false,
|
||||
};
|
||||
|
||||
const result = deriveAgentScreenViewState({ input, memory });
|
||||
|
||||
expect(result.state).toEqual({
|
||||
tag: "boot",
|
||||
reason: "loading",
|
||||
source: "none",
|
||||
});
|
||||
expect(result.memory.hasRenderedReady).toBe(false);
|
||||
expect(result.memory.lastReadyAgent).toBeNull();
|
||||
});
|
||||
|
||||
it("still allows optimistic create flow to render before authoritative history arrives", () => {
|
||||
const memory = createBaseMemory();
|
||||
const input: AgentScreenMachineInput = {
|
||||
...createBaseInput(),
|
||||
agent: createAgentWithStatus({ id: "agent-1", status: "idle" }),
|
||||
placeholderAgent: createAgent("agent-1"),
|
||||
shouldUseOptimisticStream: true,
|
||||
needsAuthoritativeSync: true,
|
||||
isHistorySyncing: true,
|
||||
hasHydratedHistoryBefore: false,
|
||||
};
|
||||
|
||||
const result = deriveAgentScreenViewState({ input, memory });
|
||||
const ready = expectReadyState(result.state);
|
||||
|
||||
expect(ready.source).toBe("optimistic");
|
||||
expect(ready.agent.status).toBe("running");
|
||||
});
|
||||
|
||||
it("keeps optimistic flow non-blocking while transitioning to authoritative stream", () => {
|
||||
const initialMemory = createBaseMemory();
|
||||
const optimisticInput: AgentScreenMachineInput = {
|
||||
|
||||
@@ -19,6 +19,16 @@ export interface AgentScreenMachineInput {
|
||||
hasHydratedHistoryBefore: boolean;
|
||||
}
|
||||
|
||||
function shouldBlockInitialAuthoritativeReadyState(
|
||||
input: AgentScreenMachineInput
|
||||
): boolean {
|
||||
return (
|
||||
!input.shouldUseOptimisticStream &&
|
||||
!input.hasHydratedHistoryBefore &&
|
||||
(input.needsAuthoritativeSync || input.isHistorySyncing)
|
||||
);
|
||||
}
|
||||
|
||||
export type AgentScreenToastLatch = "none" | "history_refresh" | "sync_error";
|
||||
|
||||
export interface AgentScreenMachineMemory {
|
||||
@@ -104,10 +114,7 @@ export function deriveAgentScreenViewState({
|
||||
input.agent && useOptimisticCreateFlowAgent && input.placeholderAgent
|
||||
? { ...input.agent, status: input.placeholderAgent.status }
|
||||
: input.agent ?? input.placeholderAgent;
|
||||
if (candidateAgent) {
|
||||
nextMemory.hasRenderedReady = true;
|
||||
nextMemory.lastReadyAgent = candidateAgent;
|
||||
}
|
||||
const shouldBlockReadyState = shouldBlockInitialAuthoritativeReadyState(input);
|
||||
|
||||
if (input.missingAgentState.kind === "not_found") {
|
||||
return {
|
||||
@@ -129,6 +136,22 @@ export function deriveAgentScreenViewState({
|
||||
};
|
||||
}
|
||||
|
||||
if (candidateAgent && shouldBlockReadyState) {
|
||||
return {
|
||||
state: {
|
||||
tag: "boot",
|
||||
reason: "loading",
|
||||
source: "none",
|
||||
},
|
||||
memory: nextMemory,
|
||||
};
|
||||
}
|
||||
|
||||
if (candidateAgent) {
|
||||
nextMemory.hasRenderedReady = true;
|
||||
nextMemory.lastReadyAgent = candidateAgent;
|
||||
}
|
||||
|
||||
const displayAgent =
|
||||
candidateAgent ?? (nextMemory.hasRenderedReady ? nextMemory.lastReadyAgent : null);
|
||||
if (!displayAgent) {
|
||||
|
||||
Reference in New Issue
Block a user