fix(knowledge): scope pins to sessions
This commit is contained in:
@@ -373,6 +373,27 @@ describe("issue 2039 draft auto-accept", () => {
|
||||
expect(permissionAutoAcceptCalls).toHaveLength(0)
|
||||
})
|
||||
|
||||
test("transfers draft project context pins only to the session it creates", async () => {
|
||||
useSessionUIStore.getState().openNewSessionDraft({
|
||||
projectContextPins: { notes: ["note-a"], plans: [] },
|
||||
})
|
||||
useSessionUIStore.getState().setDraftProjectContextPin("plan", "plan-a", true)
|
||||
|
||||
await materializeOpenDraftSession({ providerID: "provider", modelID: "model" })
|
||||
|
||||
expect(createSessionCalls[0]?.metadata).toEqual({
|
||||
openchamber: {
|
||||
project_context_pins: { notes: ["note-a"], plans: ["plan-a"] },
|
||||
},
|
||||
})
|
||||
expect(useSessionUIStore.getState().newSessionDraft.projectContextPins).toBe(undefined)
|
||||
|
||||
useSessionUIStore.getState().openNewSessionDraft()
|
||||
await materializeOpenDraftSession({ providerID: "provider", modelID: "model" })
|
||||
|
||||
expect(createSessionCalls[1]?.metadata).toBe(undefined)
|
||||
})
|
||||
|
||||
test("uses the server-authoritative directory after worktree session creation", async () => {
|
||||
createdSessionDirectory = "/canonical/worktree"
|
||||
useSessionUIStore.getState().openNewSessionDraft({
|
||||
|
||||
@@ -270,6 +270,7 @@ export type NewSessionDraftState = {
|
||||
initialPrompt?: string
|
||||
syntheticParts?: SyntheticContextPart[]
|
||||
targetFolderId?: string
|
||||
projectContextPins?: { notes: string[]; plans: string[] }
|
||||
}
|
||||
|
||||
export type ViewportAnchor = {
|
||||
@@ -319,6 +320,7 @@ export type SessionUIState = {
|
||||
setNewSessionDraftTarget: (target: { projectId?: string | null; selectedProjectId?: string | null; directoryOverride?: string | null }, options?: { force?: boolean }) => void
|
||||
setDraftPreserveDirectoryOverride: (value: boolean) => void
|
||||
setDraftPermissionAutoAcceptEnabled: (enabled: boolean) => void
|
||||
setDraftProjectContextPin: (kind: "note" | "plan", id: string, pinned: boolean) => void
|
||||
acknowledgeSessionAbort: (sessionId: string) => void
|
||||
clearAbortPrompt: () => void
|
||||
armAbortPrompt: (durationMs?: number) => number | null
|
||||
@@ -726,7 +728,15 @@ export async function materializeOpenDraftSession(selection: {
|
||||
|
||||
await waitForWorktreeBootstrapIfConfigured(draftDirectoryOverride, draftProjectId)
|
||||
|
||||
const created = await store.createSession(draft.title, draftDirectoryOverride, draft.parentID ?? null)
|
||||
const draftPins = draft.projectContextPins ?? { notes: [], plans: [] }
|
||||
const created = await store.createSession(
|
||||
draft.title,
|
||||
draftDirectoryOverride,
|
||||
draft.parentID ?? null,
|
||||
draftPins.notes.length > 0 || draftPins.plans.length > 0
|
||||
? { openchamber: { project_context_pins: draftPins } }
|
||||
: undefined,
|
||||
)
|
||||
if (!created?.id) throw new Error("Failed to create session")
|
||||
|
||||
// The server response is authoritative. It may canonicalize a requested
|
||||
@@ -1026,6 +1036,7 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
|
||||
initialPrompt: options?.initialPrompt,
|
||||
syntheticParts: options?.syntheticParts,
|
||||
targetFolderId: options?.targetFolderId,
|
||||
projectContextPins: options?.projectContextPins,
|
||||
}
|
||||
|
||||
set({
|
||||
@@ -1138,6 +1149,22 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
|
||||
return { newSessionDraft: { ...s.newSessionDraft, permissionAutoAcceptEnabled: enabled } }
|
||||
}),
|
||||
|
||||
setDraftProjectContextPin: (kind, id, pinned) =>
|
||||
set((s) => {
|
||||
if (!s.newSessionDraft?.open) return s
|
||||
const pins = s.newSessionDraft.projectContextPins ?? { notes: [], plans: [] }
|
||||
const key = kind === "note" ? "notes" : "plans"
|
||||
const next = new Set(pins[key])
|
||||
if (pinned) next.add(id)
|
||||
else next.delete(id)
|
||||
return {
|
||||
newSessionDraft: {
|
||||
...s.newSessionDraft,
|
||||
projectContextPins: { ...pins, [key]: [...next] },
|
||||
},
|
||||
}
|
||||
}),
|
||||
|
||||
acknowledgeSessionAbort: (sessionId) =>
|
||||
set((s) => {
|
||||
const flags = new Map(s.sessionAbortFlags)
|
||||
@@ -1578,14 +1605,7 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
|
||||
// ---------------------------------------------------------------------------
|
||||
// deleteSession — calls SDK, SSE event updates child store
|
||||
// ---------------------------------------------------------------------------
|
||||
deleteSession: async (id, options) => {
|
||||
const deleted = await deleteSessionAction(id, options)
|
||||
if (deleted) {
|
||||
// Nothing to forget here any more: what a session was told lives in its
|
||||
// own metadata and goes with it.
|
||||
}
|
||||
return deleted
|
||||
},
|
||||
deleteSession: async (id, options) => deleteSessionAction(id, options),
|
||||
|
||||
deleteSessions: async (ids, options) => {
|
||||
const result = await deleteSessionsAction(ids, options)
|
||||
|
||||
Reference in New Issue
Block a user