Restore archived agents from History (#2316)
* fix(app): restore archived agents from History History navigation lost the explicit archived-agent intent during tab reconciliation, and workspace recovery stopped after restoring the workspace. Preserve the selected tab and recover its provider session as one action while serializing provider resume with timeline hydration. * fix(app): preserve archived agent recovery invariants * fix(server): preserve timeline hydration call shape * fix(app): retain agent tabs through lookup * fix(server): upgrade in-flight timeline broadcast
This commit is contained in:
@@ -341,13 +341,14 @@ function useAgentPanelDescriptor(
|
||||
}
|
||||
|
||||
function AgentPanel() {
|
||||
const { serverId, target, openFileInWorkspace } = usePaneContext();
|
||||
const { serverId, workspaceId, target, openFileInWorkspace } = usePaneContext();
|
||||
const { isInteractive } = usePaneFocus();
|
||||
invariant(target.kind === "agent", "AgentPanel requires agent target");
|
||||
|
||||
return (
|
||||
<AgentPanelContent
|
||||
serverId={serverId}
|
||||
workspaceId={workspaceId}
|
||||
agentId={target.agentId}
|
||||
isPaneFocused={isInteractive}
|
||||
onOpenWorkspaceFile={openFileInWorkspace}
|
||||
@@ -481,11 +482,13 @@ type AgentLookupState =
|
||||
|
||||
function AgentPanelContent({
|
||||
serverId,
|
||||
workspaceId,
|
||||
agentId,
|
||||
isPaneFocused,
|
||||
onOpenWorkspaceFile,
|
||||
}: {
|
||||
serverId: string;
|
||||
workspaceId: string;
|
||||
agentId: string;
|
||||
isPaneFocused: boolean;
|
||||
onOpenWorkspaceFile?: (request: WorkspaceFileOpenRequest) => void;
|
||||
@@ -535,6 +538,7 @@ function AgentPanelContent({
|
||||
return (
|
||||
<AgentPanelBody
|
||||
serverId={resolvedServerId}
|
||||
workspaceId={workspaceId}
|
||||
agentId={resolvedAgentId}
|
||||
isPaneFocused={isPaneFocused}
|
||||
client={runtimeClient}
|
||||
@@ -547,6 +551,7 @@ function AgentPanelContent({
|
||||
|
||||
function AgentPanelBody({
|
||||
serverId,
|
||||
workspaceId,
|
||||
agentId,
|
||||
isPaneFocused,
|
||||
client,
|
||||
@@ -555,6 +560,7 @@ function AgentPanelBody({
|
||||
onOpenWorkspaceFile,
|
||||
}: {
|
||||
serverId: string;
|
||||
workspaceId: string;
|
||||
agentId?: string;
|
||||
isPaneFocused: boolean;
|
||||
client: ReturnType<typeof useHostRuntimeClient>;
|
||||
@@ -585,6 +591,8 @@ function AgentPanelBody({
|
||||
);
|
||||
const [lookupState, setLookupState] = useState<AgentLookupState>({ tag: "idle" });
|
||||
const lookupAttemptTokenRef = useRef(0);
|
||||
const workspaceKey = buildWorkspaceTabPersistenceKey({ serverId, workspaceId });
|
||||
const resolvePendingAgent = useWorkspaceLayoutStore((state) => state.resolvePendingAgent);
|
||||
|
||||
useEffect(() => {
|
||||
lookupAttemptTokenRef.current += 1;
|
||||
@@ -596,6 +604,9 @@ function AgentPanelBody({
|
||||
return;
|
||||
}
|
||||
if (agentState.id) {
|
||||
if (workspaceKey) {
|
||||
resolvePendingAgent(workspaceKey, agentId);
|
||||
}
|
||||
if (lookupState.tag !== "idle") {
|
||||
setLookupState({ tag: "idle" });
|
||||
}
|
||||
@@ -618,6 +629,9 @@ function AgentPanelBody({
|
||||
return;
|
||||
}
|
||||
if (!result) {
|
||||
if (workspaceKey) {
|
||||
resolvePendingAgent(workspaceKey, agentId);
|
||||
}
|
||||
setLookupState({
|
||||
tag: "not_found",
|
||||
message: `Agent not found: ${agentId}`,
|
||||
@@ -626,6 +640,9 @@ function AgentPanelBody({
|
||||
}
|
||||
|
||||
storeFetchedAgentDetail({ serverId, result });
|
||||
if (workspaceKey) {
|
||||
resolvePendingAgent(workspaceKey, agentId);
|
||||
}
|
||||
setLookupState({ tag: "idle" });
|
||||
return;
|
||||
})
|
||||
@@ -635,12 +652,25 @@ function AgentPanelBody({
|
||||
}
|
||||
const message = toErrorMessage(error);
|
||||
if (isNotFoundErrorMessage(message)) {
|
||||
if (workspaceKey) {
|
||||
resolvePendingAgent(workspaceKey, agentId);
|
||||
}
|
||||
setLookupState({ tag: "not_found", message });
|
||||
return;
|
||||
}
|
||||
setLookupState({ tag: "error", message });
|
||||
});
|
||||
}, [agentId, agentState.id, client, hasSession, isConnected, lookupState.tag, serverId]);
|
||||
}, [
|
||||
agentId,
|
||||
agentState.id,
|
||||
client,
|
||||
hasSession,
|
||||
isConnected,
|
||||
lookupState.tag,
|
||||
resolvePendingAgent,
|
||||
serverId,
|
||||
workspaceKey,
|
||||
]);
|
||||
|
||||
if (lookupState.tag === "not_found") {
|
||||
return (
|
||||
@@ -894,6 +924,7 @@ function ChatAgentContent({
|
||||
routeKey: `${serverId}:${agentId ?? ""}`,
|
||||
input: {
|
||||
agent: agent ?? null,
|
||||
isArchived: agentState.archivedAt !== null,
|
||||
missingAgentState,
|
||||
isConnected,
|
||||
isArchivingCurrentAgent,
|
||||
@@ -947,6 +978,9 @@ function ChatAgentContent({
|
||||
if (!agentId) {
|
||||
return;
|
||||
}
|
||||
if (agentState.archivedAt) {
|
||||
return;
|
||||
}
|
||||
if (agentState.id && hasAppliedAuthoritativeHistory) {
|
||||
if (
|
||||
missingAgentState.kind === "resolving" ||
|
||||
@@ -1012,6 +1046,7 @@ function ChatAgentContent({
|
||||
});
|
||||
}, [
|
||||
agentState.id,
|
||||
agentState.archivedAt,
|
||||
hasAppliedAuthoritativeHistory,
|
||||
agentId,
|
||||
client,
|
||||
|
||||
Reference in New Issue
Block a user