diff --git a/.changeset/agent-manager-history-routing-fix.md b/.changeset/agent-manager-history-routing-fix.md new file mode 100644 index 0000000000..a015ab3147 --- /dev/null +++ b/.changeset/agent-manager-history-routing-fix.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Fix project-scoped Agent Manager history activation and session placement. diff --git a/packages/kilo-vscode/src/agent-manager/project/messages.ts b/packages/kilo-vscode/src/agent-manager/project/messages.ts index 55deaf6980..d3fa3bb80b 100644 --- a/packages/kilo-vscode/src/agent-manager/project/messages.ts +++ b/packages/kilo-vscode/src/agent-manager/project/messages.ts @@ -45,6 +45,8 @@ export interface ProjectMessageDeps { expand: (ctx: ProjectContext) => void /** Push the current project snapshots to the webview. */ push: () => void + /** Push one project's managed state to the webview. */ + pushState?: (ctx: ProjectContext) => void /** Acknowledge an atomically validated sidebar selection. */ selected: (target: SidebarTarget) => void /** Show a user-facing error. */ @@ -156,6 +158,7 @@ async function openSessionLocally(projectId: string, sessionId: string, deps: Pr } state?.moveSession(sessionId, null) deps.routeSession?.(projectId, sessionId, ctx.root, ctx.generation) + deps.pushState?.(ctx) deps.push() finish({ projectId, kind: "session", sessionId }, deps) } diff --git a/packages/kilo-vscode/src/agent-manager/project/wiring.ts b/packages/kilo-vscode/src/agent-manager/project/wiring.ts index c4a0044a3d..59a47ed011 100644 --- a/packages/kilo-vscode/src/agent-manager/project/wiring.ts +++ b/packages/kilo-vscode/src/agent-manager/project/wiring.ts @@ -67,6 +67,7 @@ export function createProjectWiring(opts: { expand: opts.expand, ready: opts.ready, push: opts.push, + pushState: opts.pushState, selected: opts.selected, routeSession: opts.routeSession, error: (message) => opts.host.showError(message), diff --git a/packages/kilo-vscode/tests/unit/agent-project-selection.test.ts b/packages/kilo-vscode/tests/unit/agent-project-selection.test.ts index 7e7e746022..171df5b67d 100644 --- a/packages/kilo-vscode/tests/unit/agent-project-selection.test.ts +++ b/packages/kilo-vscode/tests/unit/agent-project-selection.test.ts @@ -19,6 +19,7 @@ function fakeState(persisted?: { current?: unknown }) { return { getWorktree: (id: string) => (id === "wt1" ? { path: "/repo/prj-extra/wt1" } : undefined), getSession: (id: string) => (id === "sess1" ? {} : undefined), + moveSession: () => {}, getActiveTarget: () => store.current, setActiveTarget: (target: unknown) => { store.current = target @@ -172,6 +173,27 @@ describe("activateSelection — cross-project selection", () => { expect(calls.error).toEqual([]) }) + it("pushes moved-session state before acknowledging local activation", async () => { + const { contexts, deps, calls, extra } = setup() + const ctx = contexts.expand(extra)! + ctx.stateManager() + await ctx.ensureReady(async () => ({ ok: true, refsFixed: 0 })) + contexts.activate(extra) + + const order: string[] = [] + deps.push = () => order.push("projects") + deps.pushState = () => order.push("state") + deps.selected = () => order.push("selected") + + await handleProjectMessage( + { type: "agentManager.openSessionLocally", projectId: extra, sessionId: "sess1" } as never, + deps, + ) + + expect(order).toEqual(["state", "projects", "projects", "selected"]) + expect(calls.error).toEqual([]) + }) + it("restores the persisted target when the selection asks for it", async () => { const persisted = { current: undefined as unknown } const { contexts, deps, calls, extra } = setup({ state: () => fakeState(persisted) }) diff --git a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx index 113e28acd8..e0a6cd8564 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx @@ -300,13 +300,18 @@ const AgentManagerContent: Component = () => { const [history, setHistory] = createSignal(false) /** Project whose sessions the history view is scoped to (multi-project). */ const [historyProject, setHistoryProject] = createSignal() + const [historySwitch, setHistorySwitch] = createSignal() const closeHistory = () => { setHistory(false) setHistoryProject(undefined) + setHistorySwitch(undefined) } /** Open the sessions view; a project id scopes it and activates that project. */ const openHistory = (pid?: string) => { const scoped = pid !== undefined && multiProject() + setHistorySwitch(scoped && currentProjectId() !== pid ? pid : undefined) + setHistoryProject(scoped ? pid : undefined) + setHistory(true) if (scoped) { // Activating the target project first lets the shared session store and // the pick routing operate in that project only. @@ -315,8 +320,6 @@ const AgentManagerContent: Component = () => { target: { projectId: pid, kind: "local" }, } as never) } - setHistoryProject(scoped ? pid : undefined) - setHistory(true) } const [sidePanel, setSidePanel] = createSignal(null) const diffOpen = () => sidePanel() === SidePanel.Diff @@ -765,7 +768,7 @@ const AgentManagerContent: Component = () => { const pid = historyProject() if (!pid || !multiProject()) return undefined const sessions = projectSessionsLive()[pid] - if (!sessions) return undefined + if (!sessions) return new Set() return new Set(sessions.filter(isKnownRootSession).map((s) => s.id)) }) @@ -1161,7 +1164,7 @@ const AgentManagerContent: Component = () => { first: () => undefined, close: () => setReviewActive(false), hide: () => setSidePanel(null), - history: () => closeHistory(), + history: () => (historySwitch() === state.projectId ? setHistorySwitch(undefined) : closeHistory()), reset: subagents.reset, }) }