refactor(background-agent): remove file persistence, use memory-only
- Remove background_tasks.json persistence (race condition with multiple instances) - Pure memory-based task management - Add logging for promptAsync errors - Remove unused persist/restore methods 🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
import type { PluginInput } from "@opencode-ai/plugin"
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
import type {
|
import type {
|
||||||
BackgroundTask,
|
BackgroundTask,
|
||||||
BackgroundTaskStatus,
|
|
||||||
LaunchInput,
|
LaunchInput,
|
||||||
} from "./types"
|
} from "./types"
|
||||||
|
import { log } from "../../shared/logger"
|
||||||
|
|
||||||
type OpencodeClient = PluginInput["client"]
|
type OpencodeClient = PluginInput["client"]
|
||||||
|
|
||||||
@@ -34,15 +34,12 @@ export class BackgroundManager {
|
|||||||
private tasks: Map<string, BackgroundTask>
|
private tasks: Map<string, BackgroundTask>
|
||||||
private notifications: Map<string, BackgroundTask[]>
|
private notifications: Map<string, BackgroundTask[]>
|
||||||
private client: OpencodeClient
|
private client: OpencodeClient
|
||||||
private storePath: string
|
|
||||||
private persistTimer?: Timer
|
|
||||||
private pollingInterval?: Timer
|
private pollingInterval?: Timer
|
||||||
|
|
||||||
constructor(client: OpencodeClient, storePath: string) {
|
constructor(client: OpencodeClient) {
|
||||||
this.tasks = new Map()
|
this.tasks = new Map()
|
||||||
this.notifications = new Map()
|
this.notifications = new Map()
|
||||||
this.client = client
|
this.client = client
|
||||||
this.storePath = storePath
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async launch(input: LaunchInput): Promise<BackgroundTask> {
|
async launch(input: LaunchInput): Promise<BackgroundTask> {
|
||||||
@@ -75,7 +72,6 @@ export class BackgroundManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.tasks.set(task.id, task)
|
this.tasks.set(task.id, task)
|
||||||
this.persist()
|
|
||||||
this.startPolling()
|
this.startPolling()
|
||||||
|
|
||||||
this.client.session.promptAsync({
|
this.client.session.promptAsync({
|
||||||
@@ -85,12 +81,12 @@ export class BackgroundManager {
|
|||||||
parts: [{ type: "text", text: input.prompt }],
|
parts: [{ type: "text", text: input.prompt }],
|
||||||
},
|
},
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
log("[background-agent] promptAsync error:", error)
|
||||||
const existingTask = this.findBySession(sessionID)
|
const existingTask = this.findBySession(sessionID)
|
||||||
if (existingTask) {
|
if (existingTask) {
|
||||||
existingTask.status = "error"
|
existingTask.status = "error"
|
||||||
existingTask.error = String(error)
|
existingTask.error = String(error)
|
||||||
existingTask.completedAt = new Date()
|
existingTask.completedAt = new Date()
|
||||||
this.persist()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -142,7 +138,6 @@ export class BackgroundManager {
|
|||||||
task.progress.toolCalls += 1
|
task.progress.toolCalls += 1
|
||||||
task.progress.lastTool = partInfo.tool
|
task.progress.lastTool = partInfo.tool
|
||||||
task.progress.lastUpdate = new Date()
|
task.progress.lastUpdate = new Date()
|
||||||
this.persist()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +154,6 @@ export class BackgroundManager {
|
|||||||
task.status = "completed"
|
task.status = "completed"
|
||||||
task.completedAt = new Date()
|
task.completedAt = new Date()
|
||||||
this.markForNotification(task)
|
this.markForNotification(task)
|
||||||
this.persist()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,8 +172,6 @@ export class BackgroundManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.tasks.delete(task.id)
|
this.tasks.delete(task.id)
|
||||||
this.persist()
|
|
||||||
|
|
||||||
this.clearNotificationsForTask(task.id)
|
this.clearNotificationsForTask(task.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -237,13 +229,17 @@ export class BackgroundManager {
|
|||||||
|
|
||||||
Use \`background_result\` tool with taskId="${task.id}" to retrieve the full result.`
|
Use \`background_result\` tool with taskId="${task.id}" to retrieve the full result.`
|
||||||
|
|
||||||
|
log("[background-agent] Notifying parent session:", task.parentSessionID)
|
||||||
|
|
||||||
this.client.session.promptAsync({
|
this.client.session.promptAsync({
|
||||||
path: { id: task.parentSessionID },
|
path: { id: task.parentSessionID },
|
||||||
body: {
|
body: {
|
||||||
parts: [{ type: "text", text: message }],
|
parts: [{ type: "text", text: message }],
|
||||||
},
|
},
|
||||||
}).catch(() => {
|
}).then(() => {
|
||||||
void 0
|
log("[background-agent] Parent session notified successfully")
|
||||||
|
}).catch((error) => {
|
||||||
|
log("[background-agent] Failed to notify parent session:", error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +279,6 @@ Use \`background_result\` tool with taskId="${task.id}" to retrieve the full res
|
|||||||
task.status = "error"
|
task.status = "error"
|
||||||
task.error = "Session not found"
|
task.error = "Session not found"
|
||||||
task.completedAt = new Date()
|
task.completedAt = new Date()
|
||||||
this.persist()
|
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -294,7 +289,6 @@ Use \`background_result\` tool with taskId="${task.id}" to retrieve the full res
|
|||||||
task.status = "completed"
|
task.status = "completed"
|
||||||
task.completedAt = new Date()
|
task.completedAt = new Date()
|
||||||
this.markForNotification(task)
|
this.markForNotification(task)
|
||||||
this.persist()
|
|
||||||
this.notifyParentSession(task)
|
this.notifyParentSession(task)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -341,77 +335,4 @@ Use \`background_result\` tool with taskId="${task.id}" to retrieve the full res
|
|||||||
this.stopPolling()
|
this.stopPolling()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
persist(): void {
|
|
||||||
if (this.persistTimer) {
|
|
||||||
clearTimeout(this.persistTimer)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.persistTimer = setTimeout(() => {
|
|
||||||
const data = Array.from(this.tasks.values())
|
|
||||||
const serialized = data.map((task) => ({
|
|
||||||
...task,
|
|
||||||
startedAt: task.startedAt.toISOString(),
|
|
||||||
completedAt: task.completedAt?.toISOString(),
|
|
||||||
progress: task.progress
|
|
||||||
? {
|
|
||||||
...task.progress,
|
|
||||||
lastUpdate: task.progress.lastUpdate.toISOString(),
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
}))
|
|
||||||
Bun.write(this.storePath, JSON.stringify(serialized, null, 2)).catch(() => {
|
|
||||||
void 0
|
|
||||||
})
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
|
|
||||||
async restore(): Promise<void> {
|
|
||||||
try {
|
|
||||||
const file = Bun.file(this.storePath)
|
|
||||||
const exists = await file.exists()
|
|
||||||
if (!exists) return
|
|
||||||
|
|
||||||
const content = await file.text()
|
|
||||||
const data = JSON.parse(content) as Array<{
|
|
||||||
id: string
|
|
||||||
sessionID: string
|
|
||||||
parentSessionID: string
|
|
||||||
parentMessageID: string
|
|
||||||
description: string
|
|
||||||
agent: string
|
|
||||||
status: BackgroundTaskStatus
|
|
||||||
startedAt: string
|
|
||||||
completedAt?: string
|
|
||||||
result?: string
|
|
||||||
error?: string
|
|
||||||
progress?: {
|
|
||||||
toolCalls: number
|
|
||||||
lastTool?: string
|
|
||||||
lastUpdate: string
|
|
||||||
}
|
|
||||||
}>
|
|
||||||
|
|
||||||
for (const item of data) {
|
|
||||||
const task: BackgroundTask = {
|
|
||||||
...item,
|
|
||||||
startedAt: new Date(item.startedAt),
|
|
||||||
completedAt: item.completedAt ? new Date(item.completedAt) : undefined,
|
|
||||||
progress: item.progress
|
|
||||||
? {
|
|
||||||
...item.progress,
|
|
||||||
lastUpdate: new Date(item.progress.lastUpdate),
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
}
|
|
||||||
this.tasks.set(task.id, task)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.hasRunningTasks()) {
|
|
||||||
this.startPolling()
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
void 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,11 +164,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
|
|
||||||
updateTerminalTitle({ sessionId: "main" });
|
updateTerminalTitle({ sessionId: "main" });
|
||||||
|
|
||||||
const backgroundManager = new BackgroundManager(
|
const backgroundManager = new BackgroundManager(ctx.client);
|
||||||
ctx.client,
|
|
||||||
path.join(ctx.directory, ".opencode", "background-tasks.json")
|
|
||||||
);
|
|
||||||
await backgroundManager.restore();
|
|
||||||
|
|
||||||
const backgroundNotificationHook = createBackgroundNotificationHook(backgroundManager);
|
const backgroundNotificationHook = createBackgroundNotificationHook(backgroundManager);
|
||||||
const backgroundTools = createBackgroundTools(backgroundManager, ctx.client);
|
const backgroundTools = createBackgroundTools(backgroundManager, ctx.client);
|
||||||
|
|||||||
@@ -207,8 +207,6 @@ Only running tasks can be cancelled.`
|
|||||||
task.status = "cancelled"
|
task.status = "cancelled"
|
||||||
task.completedAt = new Date()
|
task.completedAt = new Date()
|
||||||
|
|
||||||
manager.persist()
|
|
||||||
|
|
||||||
return `✅ Task cancelled successfully
|
return `✅ Task cancelled successfully
|
||||||
|
|
||||||
Task ID: ${task.id}
|
Task ID: ${task.id}
|
||||||
|
|||||||
Reference in New Issue
Block a user