fix: scope recall sessions to project worktrees

Keep recall and the all-sessions picker focused on the current repo while still surfacing sessions from sibling worktrees.
This commit is contained in:
Alex Alecu
2026-04-03 22:07:11 +03:00
parent e9fbf6b72a
commit 5ddd2167fc
12 changed files with 339 additions and 65 deletions
+111
View File
@@ -0,0 +1,111 @@
import { afterEach, describe, expect, mock, test } from "bun:test"
import { $ } from "bun"
import path from "path"
import { Instance } from "../../src/project/instance"
import { Config } from "../../src/config/config"
import { RecallTool } from "../../src/tool/recall"
import { resetDatabase } from "../fixture/db"
import { tmpdir } from "../fixture/fixture"
import type { Tool } from "../../src/tool/tool"
mock.module("@/kilo-sessions/remote-sender", () => ({
RemoteSender: {
create() {
return {
handle() {},
dispose() {},
}
},
},
}))
const ctx: Tool.Context = {
sessionID: "ses_test",
messageID: "msg_test",
callID: "call_test",
agent: "code",
abort: AbortSignal.any([]),
messages: [],
metadata: () => {},
ask: async () => {},
}
afterEach(async () => {
await resetDatabase()
})
describe("tool.recall", () => {
test("search is limited to the current project worktrees", async () => {
await using first = await tmpdir({ git: true })
await using second = await tmpdir({ git: true })
const worktree = path.join(first.path, "..", path.basename(first.path) + "-worktree")
try {
await $`git worktree add ${worktree} -b test-branch-${Date.now()}`.cwd(first.path).quiet()
const share = Config.get
Config.get = async () => ({ share: "manual" }) as Awaited<ReturnType<typeof Config.get>>
try {
const { Session } = await import("../../src/session/index")
await Instance.provide({
directory: first.path,
fn: async () => Session.create({ title: "search-target root" }),
})
await Instance.provide({
directory: worktree,
fn: async () => Session.create({ title: "search-target worktree" }),
})
await Instance.provide({
directory: second.path,
fn: async () => Session.create({ title: "search-target other" }),
})
const result = await Instance.provide({
directory: first.path,
fn: async () => {
const tool = await RecallTool.init()
return tool.execute({ mode: "search", query: "search-target" }, ctx)
},
})
expect(result.output).toContain("search-target root")
expect(result.output).toContain("search-target worktree")
expect(result.output).not.toContain("search-target other")
} finally {
Config.get = share
}
} finally {
await $`git worktree remove ${worktree}`.cwd(first.path).quiet().nothrow()
}
})
test("read rejects sessions from another project", async () => {
await using first = await tmpdir({ git: true })
await using second = await tmpdir({ git: true })
const share = Config.get
Config.get = async () => ({ share: "manual" }) as Awaited<ReturnType<typeof Config.get>>
try {
const { Session } = await import("../../src/session/index")
const session = await Instance.provide({
directory: second.path,
fn: async () => Session.create({ title: "other-project-session" }),
})
const err = await Instance.provide({
directory: first.path,
fn: async () => {
const tool = await RecallTool.init()
return tool.execute({ mode: "read", sessionID: session.id }, ctx).catch((error) => error as Error)
},
})
expect(err).toBeInstanceOf(Error)
expect((err as Error).message).toContain("belongs to a different project")
} finally {
Config.get = share
}
})
})