feat(athena): make council member background tasks visible in UI
Council member tasks were launched via BackgroundManager but lacked the ctx.metadata() call that links background sessions to the tool call in the OpenCode TUI. Users couldn't click to inspect individual member outputs. - Add session-waiter.ts to poll for session creation on launched tasks - Call ctx.metadata() for each council member with sessionId linkage - Matches the pattern used by delegate-task/background-task.ts Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
52
src/tools/athena-council/session-waiter.ts
Normal file
52
src/tools/athena-council/session-waiter.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import type { BackgroundManager } from "../../features/background-agent"
|
||||
import type { CouncilLaunchedMember } from "../../agents/athena/types"
|
||||
|
||||
const WAIT_INTERVAL_MS = 100
|
||||
const WAIT_TIMEOUT_MS = 30_000
|
||||
|
||||
interface CouncilSessionInfo {
|
||||
taskId: string
|
||||
memberName: string
|
||||
model: string
|
||||
sessionId: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for background sessions to be created for launched council members.
|
||||
* Returns session info for each member whose session became available within the timeout.
|
||||
*/
|
||||
export async function waitForCouncilSessions(
|
||||
launched: CouncilLaunchedMember[],
|
||||
manager: BackgroundManager,
|
||||
abort?: AbortSignal
|
||||
): Promise<CouncilSessionInfo[]> {
|
||||
const results: CouncilSessionInfo[] = []
|
||||
const pending = new Map(
|
||||
launched.map((entry) => [entry.taskId, entry])
|
||||
)
|
||||
|
||||
const deadline = Date.now() + WAIT_TIMEOUT_MS
|
||||
|
||||
while (pending.size > 0 && Date.now() < deadline) {
|
||||
if (abort?.aborted) break
|
||||
|
||||
for (const [taskId, entry] of pending) {
|
||||
const task = manager.getTask(taskId)
|
||||
if (task?.sessionID) {
|
||||
results.push({
|
||||
taskId,
|
||||
memberName: entry.member.name ?? entry.member.model,
|
||||
model: entry.member.model,
|
||||
sessionId: task.sessionID,
|
||||
})
|
||||
pending.delete(taskId)
|
||||
}
|
||||
}
|
||||
|
||||
if (pending.size > 0) {
|
||||
await new Promise((resolve) => setTimeout(resolve, WAIT_INTERVAL_MS))
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import type { BackgroundManager } from "../../features/background-agent"
|
||||
import { ATHENA_COUNCIL_TOOL_DESCRIPTION_TEMPLATE } from "./constants"
|
||||
import { createCouncilLauncher } from "./council-launcher"
|
||||
import { isCouncilRunning, markCouncilDone, markCouncilRunning } from "./session-guard"
|
||||
import { waitForCouncilSessions } from "./session-waiter"
|
||||
import type { AthenaCouncilLaunchResult, AthenaCouncilToolArgs } from "./types"
|
||||
|
||||
function isCouncilConfigured(councilConfig: CouncilConfig | undefined): councilConfig is CouncilConfig {
|
||||
@@ -112,6 +113,31 @@ export function createAthenaCouncilTool(args: {
|
||||
parentAgent: toolContext.agent,
|
||||
})
|
||||
|
||||
// Wait for sessions to be created so we can register metadata for UI visibility.
|
||||
// This makes council member tasks clickable in the OpenCode TUI, matching the
|
||||
// behavior of the task tool (delegate-task/background-task.ts).
|
||||
const metadataFn = (toolContext as Record<string, unknown>).metadata as
|
||||
| ((input: { title?: string; metadata?: Record<string, unknown> }) => Promise<void>)
|
||||
| undefined
|
||||
if (metadataFn && execution.launched.length > 0) {
|
||||
const sessions = await waitForCouncilSessions(
|
||||
execution.launched,
|
||||
backgroundManager,
|
||||
toolContext.abort
|
||||
)
|
||||
for (const session of sessions) {
|
||||
await metadataFn({
|
||||
title: `Council: ${session.memberName}`,
|
||||
metadata: {
|
||||
sessionId: session.sessionId,
|
||||
agent: "council-member",
|
||||
model: session.model,
|
||||
description: `Council member: ${session.memberName}`,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const launchResult: AthenaCouncilLaunchResult = {
|
||||
launched: execution.launched.length,
|
||||
members: execution.launched.map((entry) => ({
|
||||
|
||||
Reference in New Issue
Block a user