Fork assistant turns into new drafts (#1788)
* feat(chat): fork assistant turns into new drafts Builds chat-history attachments on the daemon and routes them into draft composers so users can continue an assistant turn in an existing workspace tab or New Workspace. * fix(chat): clean up fork draft handling * fix(chat): address fork draft review feedback * fix(server): build fork context from bounded timeline * fix(app): restore assistant turn footer in streams * fix(app): preserve fork draft setup * fix(app): tighten assistant fork footers * fix(app): lock active fork draft handoff * fix(app): preserve attachment-only draft retries * fix(app): unify composer attachment scopes * fix(app): align chat history attachment labels * fix(app): centralize attachment pill content * fix(app): trim fork context for workspace naming
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { create } from "zustand";
|
||||
import type { ComposerAttachment } from "@/attachments/types";
|
||||
import type { AgentProvider } from "@getpaseo/protocol/agent-types";
|
||||
import type { WorkspaceDraftTabSetup } from "@/stores/workspace-tabs-store";
|
||||
|
||||
export interface PendingWorkspaceDraftSubmission {
|
||||
serverId: string;
|
||||
@@ -19,9 +20,21 @@ export interface PendingWorkspaceDraftSubmission {
|
||||
allowEmptyText?: boolean;
|
||||
}
|
||||
|
||||
export interface PendingWorkspaceDraftSetup {
|
||||
setup: WorkspaceDraftTabSetup;
|
||||
sourceDirectory?: string | null;
|
||||
}
|
||||
|
||||
interface WorkspaceDraftSubmissionState {
|
||||
pendingByDraftId: Record<string, PendingWorkspaceDraftSubmission>;
|
||||
setupByDraftId: Record<string, PendingWorkspaceDraftSetup>;
|
||||
setPending: (submission: PendingWorkspaceDraftSubmission) => void;
|
||||
setDraftSetup: (input: {
|
||||
draftId: string;
|
||||
setup: WorkspaceDraftTabSetup;
|
||||
sourceDirectory?: string | null;
|
||||
}) => void;
|
||||
clearDraftSetup: (input: { draftId: string }) => void;
|
||||
consumePending: (input: {
|
||||
serverId: string;
|
||||
workspaceId: string;
|
||||
@@ -40,9 +53,14 @@ function matchesPendingSubmission(
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeDraftId(draftId: string): string {
|
||||
return draftId.trim();
|
||||
}
|
||||
|
||||
export const useWorkspaceDraftSubmissionStore = create<WorkspaceDraftSubmissionState>(
|
||||
(set, get) => ({
|
||||
pendingByDraftId: {},
|
||||
setupByDraftId: {},
|
||||
setPending: (submission) =>
|
||||
set((state) => ({
|
||||
pendingByDraftId: {
|
||||
@@ -50,6 +68,25 @@ export const useWorkspaceDraftSubmissionStore = create<WorkspaceDraftSubmissionS
|
||||
[submission.draftId]: submission,
|
||||
},
|
||||
})),
|
||||
setDraftSetup: ({ draftId, setup, sourceDirectory }) => {
|
||||
const normalizedDraftId = normalizeDraftId(draftId);
|
||||
if (!normalizedDraftId) return;
|
||||
set((state) => ({
|
||||
setupByDraftId: {
|
||||
...state.setupByDraftId,
|
||||
[normalizedDraftId]: { setup, sourceDirectory: sourceDirectory ?? null },
|
||||
},
|
||||
}));
|
||||
},
|
||||
clearDraftSetup: ({ draftId }) => {
|
||||
const normalizedDraftId = normalizeDraftId(draftId);
|
||||
if (!normalizedDraftId) return;
|
||||
set((state) => {
|
||||
if (!state.setupByDraftId[normalizedDraftId]) return state;
|
||||
const { [normalizedDraftId]: _removed, ...setupByDraftId } = state.setupByDraftId;
|
||||
return { setupByDraftId };
|
||||
});
|
||||
},
|
||||
consumePending: (input) => {
|
||||
const pending = get().pendingByDraftId[input.draftId];
|
||||
if (!matchesPendingSubmission(pending, input)) {
|
||||
|
||||
Reference in New Issue
Block a user