mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
fix(agent-manager): handle activity review edge cases
This commit is contained in:
@@ -1436,6 +1436,7 @@ export class AgentManagerProvider implements Disposable {
|
||||
|
||||
/** Push empty state when the folder is not a git repo or has no folder open. */
|
||||
private pushEmptyState(): void {
|
||||
void this.activity.sync()
|
||||
this.staleWorktreeIds.clear()
|
||||
this.postToWebview({
|
||||
type: "agentManager.state",
|
||||
@@ -1616,6 +1617,7 @@ export class AgentManagerProvider implements Disposable {
|
||||
|
||||
private pushProjects(): void {
|
||||
const projects = this.contexts.snapshots()
|
||||
void this.activity.sync()
|
||||
this.postToWebview({
|
||||
type: "agentManager.projects",
|
||||
multiProject: this.host.multiProject(),
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { KiloConnectionService } from "../services/cli-backend"
|
||||
type Snapshot = {
|
||||
statuses: Record<string, { type: string }>
|
||||
permissions: Array<{ id: string; sessionID: string }>
|
||||
questions: Array<{ id: string; sessionID: string }>
|
||||
questions: Array<{ id: string; sessionID: string; blocking?: boolean }>
|
||||
}
|
||||
|
||||
type Change =
|
||||
@@ -234,7 +234,7 @@ export class WorktreeActivity {
|
||||
}
|
||||
state.questions = new Map()
|
||||
for (const item of snapshot.questions ?? []) {
|
||||
if (typeof item?.id === "string" && typeof item.sessionID === "string")
|
||||
if (item?.blocking !== false && typeof item?.id === "string" && typeof item.sessionID === "string")
|
||||
state.questions.set(item.id, item.sessionID)
|
||||
}
|
||||
for (const change of req.events) this.apply(state, change)
|
||||
@@ -282,6 +282,7 @@ export class WorktreeActivity {
|
||||
const id = string(value.id)
|
||||
const sessionID = string(value.sessionID)
|
||||
if (!id || !sessionID) return undefined
|
||||
if (type === "question.asked" && value.blocking === false) return { kind: "question.remove", id, sessionID }
|
||||
return type === "permission.asked"
|
||||
? { kind: "permission.add", id, sessionID }
|
||||
: { kind: "question.add", id, sessionID }
|
||||
|
||||
@@ -30,6 +30,19 @@ describe("createSessionBusy", () => {
|
||||
expect(busy({ working: { type } }).agent("wt-working")).toBe(true)
|
||||
})
|
||||
|
||||
it("keeps running for non-blocking questions", () => {
|
||||
const questions: { sessionID: string; blocking?: boolean }[] = [{ sessionID: "working", blocking: false }]
|
||||
const state = createSessionBusy({
|
||||
...options({ working: { type: "busy" } }),
|
||||
questions: () => questions,
|
||||
})
|
||||
expect(state.agent("wt-working")).toBe(true)
|
||||
questions[0].blocking = true
|
||||
expect(state.agent("wt-working")).toBe(false)
|
||||
delete questions[0].blocking
|
||||
expect(state.agent("wt-working")).toBe(false)
|
||||
})
|
||||
|
||||
it("does not keep a spinner for an offline session", () => {
|
||||
const state = busy({ working: { type: "offline" }, unknown: { type: "offline" } })
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createWorktreeActivity, WorktreeActivity } from "../../src/agent-manage
|
||||
type Snapshot = {
|
||||
statuses: Record<string, { type: string }>
|
||||
permissions: Array<{ id: string; sessionID: string }>
|
||||
questions: Array<{ id: string; sessionID: string }>
|
||||
questions: Array<{ id: string; sessionID: string; blocking?: boolean }>
|
||||
}
|
||||
|
||||
function defer<T>() {
|
||||
@@ -137,6 +137,23 @@ describe("WorktreeActivity", () => {
|
||||
expect(test.posted.at(-1)).toEqual([])
|
||||
})
|
||||
|
||||
it("ignores non-blocking questions in snapshots and live events", async () => {
|
||||
const test = setup(["/repo"], async () => ({
|
||||
...snapshot({ child: "busy" }),
|
||||
questions: [{ id: "note", sessionID: "child", blocking: false }],
|
||||
}))
|
||||
await test.activity.sync()
|
||||
expect(test.posted.at(-1)).toEqual(["/repo"])
|
||||
|
||||
const question = asked("question", "live", "child")
|
||||
test.activity.event({ ...question, properties: { ...question.properties, blocking: false } }, "/repo")
|
||||
expect(test.posted.at(-1)).toEqual(["/repo"])
|
||||
test.activity.event(question, "/repo")
|
||||
expect(test.posted.at(-1)).toEqual([])
|
||||
test.activity.event({ ...question, properties: { ...question.properties, blocking: false } }, "/repo")
|
||||
expect(test.posted.at(-1)).toEqual(["/repo"])
|
||||
})
|
||||
|
||||
it("clears sessions on completion, deletion, errors, and offline status", async () => {
|
||||
const test = setup(["/repo"], async () => snapshot())
|
||||
await test.activity.sync()
|
||||
@@ -237,6 +254,8 @@ describe("WorktreeActivity", () => {
|
||||
dirs.length = 0
|
||||
await test.activity.sync()
|
||||
expect(test.posted.at(-1)).toEqual([])
|
||||
test.activity.event(status("s1", "busy"), "/repo")
|
||||
expect(test.posted.at(-1)).toEqual([])
|
||||
gate.resolve(snapshot({ s1: "busy" }))
|
||||
await pending
|
||||
expect(test.posted.at(-1)).toEqual([])
|
||||
|
||||
@@ -12,6 +12,7 @@ interface Status {
|
||||
|
||||
interface Prompt {
|
||||
sessionID: string
|
||||
blocking?: boolean
|
||||
}
|
||||
|
||||
export function createSessionBusy(opts: {
|
||||
@@ -26,7 +27,11 @@ export function createSessionBusy(opts: {
|
||||
const any = (ids: string[]) => {
|
||||
if (ids.length === 0) return false
|
||||
const statuses = opts.statuses()
|
||||
const blocked = new Set([...opts.permissions(), ...opts.questions()].map((item) => item.sessionID))
|
||||
const blocked = new Set(
|
||||
[...opts.permissions(), ...opts.questions().filter((item) => item.blocking !== false)].map(
|
||||
(item) => item.sessionID,
|
||||
),
|
||||
)
|
||||
return ids.some((id) => {
|
||||
const status = statuses[id]
|
||||
return (status?.type === "busy" || status?.type === "retry") && !blocked.has(id)
|
||||
|
||||
Reference in New Issue
Block a user