mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
52447ed2af
The RunScriptManager keys entries by worktreeId and the webview sends "local" for whichever project's local context is selected, so in multi-project mode two projects' local runs collided in one entry: a second project's Run button stopped the first project's script, and the state payload spread every project's run statuses into the active project's push, rendering foreign local rows as running. The provider now namespaces the local key as <projectId>:local at the message boundary (ambient project scope, no webview protocol change), RunController treats the qualified suffix as a local run (repo root cwd, "local" branch label), each project's state payload is filtered to its own worktrees plus its un-namespaced local key, and runStatus emissions are routed with a projectId stamp so background-project statuses land in their own webview store. ScriptTerminalManager keys inherit the same qualification, and its view mapping already namespaces run terminals to the matching project bucket.
92 lines
3.9 KiB
TypeScript
92 lines
3.9 KiB
TypeScript
import { describe, expect, it, mock } from "bun:test"
|
|
import { AgentManagerProvider } from "../../src/agent-manager/AgentManagerProvider"
|
|
import type { ProjectContext } from "../../src/agent-manager/project/context"
|
|
import type { RunStatus } from "../../src/agent-manager/run/manager"
|
|
import type { AgentManagerOutMessage } from "../../src/agent-manager/types"
|
|
|
|
type Internals = {
|
|
run: { state: () => { runStatuses: RunStatus[]; runScriptConfigured: boolean } }
|
|
contexts: {
|
|
resolve: (id: string) => { id: string } | undefined
|
|
byWorktree: (id: string) => { id: string } | undefined
|
|
}
|
|
postToWebview: (message: AgentManagerOutMessage) => void
|
|
runStateFor: (ctx: ProjectContext) => { runStatuses: RunStatus[] }
|
|
postRunMessage: (message: AgentManagerOutMessage) => void
|
|
}
|
|
|
|
function ctx(id: string, pinned: boolean, worktreeIds: string[]): ProjectContext {
|
|
return {
|
|
id,
|
|
pinned,
|
|
peekState: () => ({ getWorktrees: () => worktreeIds.map((wt) => ({ id: wt })) }),
|
|
} as unknown as ProjectContext
|
|
}
|
|
|
|
function provider(runStatuses: RunStatus[]) {
|
|
const instance = Object.create(AgentManagerProvider.prototype) as Internals
|
|
instance.run = { state: () => ({ runStatuses, runScriptConfigured: true }) }
|
|
return instance
|
|
}
|
|
|
|
describe("Agent Manager run status project scoping", () => {
|
|
it("filters the state payload to the target project and un-namespaces its local key", () => {
|
|
const instance = provider([
|
|
{ worktreeId: "wt-a1", state: "running" },
|
|
{ worktreeId: "prj-a:local", state: "running" },
|
|
{ worktreeId: "wt-b1", state: "running" },
|
|
{ worktreeId: "prj-b:local", state: "stopping" },
|
|
])
|
|
|
|
const a = instance.runStateFor(ctx("prj-a", false, ["wt-a1"]))
|
|
expect(a.runStatuses).toEqual([
|
|
{ worktreeId: "wt-a1", state: "running" },
|
|
{ worktreeId: "local", state: "running" },
|
|
])
|
|
|
|
const b = instance.runStateFor(ctx("prj-b", false, ["wt-b1"]))
|
|
expect(b.runStatuses).toEqual([
|
|
{ worktreeId: "wt-b1", state: "running" },
|
|
{ worktreeId: "local", state: "stopping" },
|
|
])
|
|
})
|
|
|
|
it("keeps legacy unqualified local entries on the pinned project only", () => {
|
|
const instance = provider([{ worktreeId: "local", state: "running" }])
|
|
|
|
expect(instance.runStateFor(ctx("prj-pinned", true, [])).runStatuses).toEqual([
|
|
{ worktreeId: "local", state: "running" },
|
|
])
|
|
expect(instance.runStateFor(ctx("prj-other", false, [])).runStatuses).toEqual([])
|
|
})
|
|
|
|
it("stamps the owning project on emissions and un-namespaces qualified local keys", () => {
|
|
const instance = provider([])
|
|
const posted: AgentManagerOutMessage[] = []
|
|
instance.contexts = {
|
|
resolve: (id) => (id === "prj-a" ? { id: "prj-a" } : undefined),
|
|
byWorktree: (id) => (id === "wt-a1" ? { id: "prj-a" } : undefined),
|
|
}
|
|
instance.postToWebview = mock((message: AgentManagerOutMessage) => void posted.push(message))
|
|
|
|
instance.postRunMessage({ type: "agentManager.runStatus", worktreeId: "prj-a:local", state: "running" })
|
|
instance.postRunMessage({ type: "agentManager.runStatus", worktreeId: "wt-a1", state: "running" })
|
|
instance.postRunMessage({ type: "agentManager.runStatus", worktreeId: "local", state: "running" })
|
|
|
|
expect(posted).toEqual([
|
|
{ type: "agentManager.runStatus", worktreeId: "local", state: "running", projectId: "prj-a" },
|
|
{ type: "agentManager.runStatus", worktreeId: "wt-a1", state: "running", projectId: "prj-a" },
|
|
{ type: "agentManager.runStatus", worktreeId: "local", state: "running" },
|
|
])
|
|
})
|
|
|
|
it("passes non-runStatus messages through untouched", () => {
|
|
const instance = provider([])
|
|
const posted: AgentManagerOutMessage[] = []
|
|
instance.postToWebview = mock((message: AgentManagerOutMessage) => void posted.push(message))
|
|
|
|
instance.postRunMessage({ type: "agentManager.localStats", stats: undefined } as never)
|
|
expect(posted).toEqual([{ type: "agentManager.localStats", stats: undefined }])
|
|
})
|
|
})
|