fix(background-agent): use session status check and prompt() for visible notification
- Replace promptAsync() with session.prompt() for visible TUI updates - Add main session check to skip subagent sessions - Add session status idle check before sending prompt - Add 200ms debounce with re-check to prevent race conditions - Fallback to pending queue when session is busy 🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
@@ -4,6 +4,7 @@ import type {
|
|||||||
LaunchInput,
|
LaunchInput,
|
||||||
} from "./types"
|
} from "./types"
|
||||||
import { log } from "../../shared/logger"
|
import { log } from "../../shared/logger"
|
||||||
|
import { getMainSessionID } from "../claude-code-session-state"
|
||||||
|
|
||||||
type OpencodeClient = PluginInput["client"]
|
type OpencodeClient = PluginInput["client"]
|
||||||
|
|
||||||
@@ -215,47 +216,96 @@ export class BackgroundManager {
|
|||||||
private notifyParentSession(task: BackgroundTask): void {
|
private notifyParentSession(task: BackgroundTask): void {
|
||||||
const duration = this.formatDuration(task.startedAt, task.completedAt)
|
const duration = this.formatDuration(task.startedAt, task.completedAt)
|
||||||
const toolCalls = task.progress?.toolCalls ?? 0
|
const toolCalls = task.progress?.toolCalls ?? 0
|
||||||
|
|
||||||
const message = `[BACKGROUND TASK COMPLETED] Task "${task.description}" finished in ${duration} with ${toolCalls} tool calls. Use background_result tool with taskId="${task.id}" to retrieve the result.`
|
|
||||||
|
|
||||||
log("[background-agent] Sending async message to parent:", task.parentSessionID)
|
log("[background-agent] notifyParentSession called for task:", task.id)
|
||||||
|
|
||||||
|
// 1. Toast 알림 (즉각적 피드백 - 항상 실행)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const tuiClient = this.client as any
|
const tuiClient = this.client as any
|
||||||
if (tuiClient.tui?.showToast) {
|
if (tuiClient.tui?.showToast) {
|
||||||
tuiClient.tui.showToast({
|
tuiClient.tui.showToast({
|
||||||
body: {
|
body: {
|
||||||
title: "Background Task Completed",
|
title: "Background Task Completed",
|
||||||
message: `Task "${task.description}" finished.`,
|
message: `Task "${task.description}" finished in ${duration}.`,
|
||||||
variant: "success",
|
variant: "success",
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
},
|
},
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.client.session.promptAsync({
|
// 2. Main session 확인
|
||||||
path: { id: task.parentSessionID },
|
const mainSessionID = getMainSessionID()
|
||||||
body: {
|
const isMainSession = task.parentSessionID === mainSessionID
|
||||||
parts: [{ type: "text", text: message }],
|
|
||||||
},
|
if (!isMainSession) {
|
||||||
}).then((result) => {
|
log("[background-agent] Parent is not main session, relying on pending queue")
|
||||||
log("[background-agent] promptAsync result:", { error: result.error, response: result.response?.status })
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Session status 확인 + 200ms debounce 후 prompt() 호출
|
||||||
|
this.schedulePromptToParent(task, duration, toolCalls)
|
||||||
|
}
|
||||||
|
|
||||||
|
private schedulePromptToParent(task: BackgroundTask, duration: string, toolCalls: number): void {
|
||||||
|
this.client.session.status().then((statusResult) => {
|
||||||
|
const allStatuses = (statusResult.data ?? {}) as Record<string, { type: string }>
|
||||||
|
const parentStatus = allStatuses[task.parentSessionID]
|
||||||
|
|
||||||
|
if (!parentStatus || parentStatus.type !== "idle") {
|
||||||
|
log("[background-agent] Parent session is busy, relying on pending queue")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 200ms debounce 후 prompt() 호출
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (tuiClient.tui?.submitPrompt) {
|
this.sendPromptToParent(task, duration, toolCalls)
|
||||||
log("[background-agent] Triggering submitPrompt to force TUI update")
|
}, 200)
|
||||||
tuiClient.tui.submitPrompt({
|
|
||||||
query: { directory: this.directory }
|
|
||||||
}).catch((err: unknown) => {
|
|
||||||
log("[background-agent] submitPrompt failed:", String(err))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, 100)
|
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
log("[background-agent] promptAsync exception:", String(error))
|
log("[background-agent] Failed to check session status:", String(error))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sendPromptToParent(task: BackgroundTask, duration: string, toolCalls: number): void {
|
||||||
|
// Re-check status after delay (race condition 방지)
|
||||||
|
this.client.session.status().then((recheck) => {
|
||||||
|
const recheckStatuses = (recheck.data ?? {}) as Record<string, { type: string }>
|
||||||
|
const recheckStatus = recheckStatuses[task.parentSessionID]
|
||||||
|
|
||||||
|
if (!recheckStatus || recheckStatus.type !== "idle") {
|
||||||
|
log("[background-agent] Parent session no longer idle after delay, skipping prompt")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = this.buildNotificationMessage(task, duration, toolCalls)
|
||||||
|
|
||||||
|
this.client.session.prompt({
|
||||||
|
path: { id: task.parentSessionID },
|
||||||
|
body: {
|
||||||
|
parts: [{ type: "text", text: message }],
|
||||||
|
},
|
||||||
|
query: { directory: this.directory },
|
||||||
|
}).then(() => {
|
||||||
|
this.clearNotificationsForTask(task.id)
|
||||||
|
log("[background-agent] Successfully sent prompt to parent session")
|
||||||
|
}).catch((error) => {
|
||||||
|
// 실패해도 pending queue가 있으므로 다음 user 입력 시 전달됨
|
||||||
|
log("[background-agent] Failed to send prompt, relying on pending queue:", String(error))
|
||||||
|
})
|
||||||
|
}).catch((error) => {
|
||||||
|
log("[background-agent] Failed to recheck session status:", String(error))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildNotificationMessage(task: BackgroundTask, duration: string, toolCalls: number): string {
|
||||||
|
return `[BACKGROUND TASK COMPLETED]
|
||||||
|
|
||||||
|
Task "${task.description}" has finished.
|
||||||
|
Duration: ${duration}
|
||||||
|
Tool calls: ${toolCalls}
|
||||||
|
|
||||||
|
Use \`background_result\` tool with taskId="${task.id}" to retrieve the full result.`
|
||||||
|
}
|
||||||
|
|
||||||
private formatDuration(start: Date, end?: Date): string {
|
private formatDuration(start: Date, end?: Date): string {
|
||||||
const duration = (end ?? new Date()).getTime() - start.getTime()
|
const duration = (end ?? new Date()).getTime() - start.getTime()
|
||||||
const seconds = Math.floor(duration / 1000)
|
const seconds = Math.floor(duration / 1000)
|
||||||
|
|||||||
Reference in New Issue
Block a user