Merge pull request #4350 from Yeachan-Heo/omc-team/you-are-one-of-5-parallel-work/worker-3
fix: TUI subagent session entry stuck spinning + unclickable (#4252)
This commit is contained in:
@@ -94,6 +94,13 @@ export function createBackgroundTask(
|
|||||||
await delay(WAIT_FOR_SESSION_INTERVAL_MS)
|
await delay(WAIT_FOR_SESSION_INTERVAL_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Capture late-arriving sessionId between the wait-loop exit and
|
||||||
|
// metadata publish so the OpenCode TUI subagent entry has a navigable
|
||||||
|
// target (issue #4252).
|
||||||
|
if (!sessionId) {
|
||||||
|
sessionId = manager.getTask(task.id)?.sessionId
|
||||||
|
}
|
||||||
|
|
||||||
const bgMeta = {
|
const bgMeta = {
|
||||||
title: args.description,
|
title: args.description,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|||||||
@@ -160,6 +160,15 @@ export async function executeBackgroundTask(
|
|||||||
return `Task failed to start (status: ${updatedTask.status}).\n\nTask ID: ${task.id}`
|
return `Task failed to start (status: ${updatedTask.status}).\n\nTask ID: ${task.id}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Capture late-arriving sessionId from the race window between wait-loop
|
||||||
|
// exit and metadata publish. Without this, a session that gets created
|
||||||
|
// moments after the wait loop returns leaves metadata.sessionId undefined,
|
||||||
|
// which makes the OpenCode TUI render the subagent entry as a perpetual
|
||||||
|
// spinner with no clickable navigation target (issue #4252).
|
||||||
|
if (!sessionId && updatedTask?.sessionId) {
|
||||||
|
sessionId = updatedTask.sessionId
|
||||||
|
}
|
||||||
|
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
registerBackgroundSessionContext({
|
registerBackgroundSessionContext({
|
||||||
sessionId,
|
sessionId,
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
const { describe, test, expect } = require("bun:test")
|
||||||
|
|
||||||
|
import type { DelegateTaskArgs, ToolContextWithMetadata } from "./types"
|
||||||
|
import type { ParentContext } from "./executor-types"
|
||||||
|
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||||
|
|
||||||
|
const MODEL = { providerID: "anthropic", modelID: "claude-sonnet-4-6" }
|
||||||
|
|
||||||
|
function makeMockCtx(): ToolContextWithMetadata & { captured: any[] } {
|
||||||
|
const captured: any[] = []
|
||||||
|
return {
|
||||||
|
sessionID: "ses_parent",
|
||||||
|
messageID: "msg_parent",
|
||||||
|
agent: "sisyphus",
|
||||||
|
abort: new AbortController().signal,
|
||||||
|
callID: "call_001",
|
||||||
|
metadata: async (input: any) => { captured.push(input) },
|
||||||
|
captured,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentContext: ParentContext = {
|
||||||
|
sessionID: "ses_parent",
|
||||||
|
messageID: "msg_parent",
|
||||||
|
agent: "sisyphus",
|
||||||
|
model: MODEL,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("background-task late sessionId capture (issue #4252)", () => {
|
||||||
|
test("#given launch returns no sessionId and getTask returns one #when publishing metadata #then sessionId is captured so TUI entry is clickable", async () => {
|
||||||
|
const { executeBackgroundTask } = require("./background-task")
|
||||||
|
const ctx = makeMockCtx()
|
||||||
|
const args: DelegateTaskArgs = {
|
||||||
|
description: "deferred task",
|
||||||
|
prompt: "do it",
|
||||||
|
load_skills: [],
|
||||||
|
run_in_background: true,
|
||||||
|
subagent_type: "explore",
|
||||||
|
}
|
||||||
|
|
||||||
|
// launch returns a pending task with no sessionId; getTask returns the
|
||||||
|
// same task with sessionId populated *after* the wait loop exits. This
|
||||||
|
// simulates the race where the subagent session is created moments after
|
||||||
|
// we stop polling, and is the exact condition that left the OpenCode TUI
|
||||||
|
// session entry stuck spinning with no click target on v4.2.3.
|
||||||
|
await executeBackgroundTask(args, ctx, unsafeTestValue({
|
||||||
|
manager: {
|
||||||
|
launch: async () => ({
|
||||||
|
id: "bg_late",
|
||||||
|
description: "deferred task",
|
||||||
|
agent: "explore",
|
||||||
|
status: "pending",
|
||||||
|
}),
|
||||||
|
getTask: () => ({
|
||||||
|
id: "bg_late",
|
||||||
|
description: "deferred task",
|
||||||
|
agent: "explore",
|
||||||
|
status: "running",
|
||||||
|
sessionId: "ses_late",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}), parentContext, "explore", MODEL, undefined)
|
||||||
|
|
||||||
|
const meta = ctx.captured.find((m: any) => m.metadata?.sessionId)
|
||||||
|
expect(meta).toBeDefined()
|
||||||
|
expect(meta.metadata.sessionId).toBe("ses_late")
|
||||||
|
expect(meta.metadata.taskId).toBe("ses_late")
|
||||||
|
expect(meta.metadata.backgroundTaskId).toBe("bg_late")
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user