Initial project scaffold with uv

This commit is contained in:
2026-04-01 15:52:58 -07:00
commit f5ac0e04de
18 changed files with 1402 additions and 0 deletions

View File

@@ -0,0 +1,621 @@
import { tool } from "@opencode-ai/plugin"
import { rm, writeFile } from "node:fs/promises"
import path from "node:path"
import {
buildWorkerPrompt,
createTaskID,
extractTextFromParts,
formatTaskLine,
now,
projectRootFromContext,
artifactPathForDisplay,
artifactRoot,
addTask,
readTask,
updateTask,
loadRegistry,
recordTaskError,
recordTaskCompletion,
errorMessage,
sleep,
} from "../lib/team-utils.js"
const DEFAULT_WAIT_TIMEOUT_MS = 5 * 60 * 1000
const POLL_INTERVAL_MS = 1000
async function latestAssistantSummary(client, sessionID, directory) {
const result = await client.session.messages({
path: { id: sessionID },
query: { directory },
})
const messages = result.data || []
for (let index = messages.length - 1; index >= 0; index -= 1) {
const message = messages[index]
if (message.info.role !== "assistant") continue
const text = extractTextFromParts(message.parts)
if (!text) continue
return {
messageID: message.info.id,
text,
}
}
return {
messageID: undefined,
text: "",
}
}
async function writeArtifact(worktree, taskID, text) {
if (!text.trim()) return undefined
const file = path.join(artifactRoot(worktree), `${taskID}.md`)
await writeFile(file, `${text.trim()}\n`, "utf8")
return file
}
async function getLiveSessionStatus(client, sessionID, directory) {
const result = await client.session.status({
query: { directory },
})
return result.data?.[sessionID] || { type: "idle" }
}
async function waitForIdle(client, sessionID, directory, timeoutMs, shouldStop) {
const deadline = Date.now() + timeoutMs
let lastStatus = { type: "idle" }
while (Date.now() < deadline) {
const stop = await shouldStop?.()
if (stop) {
return {
completed: false,
cancelled: true,
status: lastStatus,
}
}
lastStatus = await getLiveSessionStatus(client, sessionID, directory)
if (!lastStatus || lastStatus.type === "idle") {
return { completed: true, status: lastStatus || { type: "idle" } }
}
const sleepMs = Math.min(POLL_INTERVAL_MS, Math.max(0, deadline - Date.now()))
if (sleepMs > 0) {
await sleep(sleepMs)
}
}
return { completed: false, cancelled: false, status: lastStatus }
}
async function dispatchPrompt(client, directory, sessionID, body) {
await client.session.promptAsync({
path: { id: sessionID },
query: { directory },
body,
})
}
async function awaitTaskResult(client, context, projectRoot, taskID, timeoutMs) {
const task = await readTask(projectRoot, taskID)
if (!task) {
throw new Error(`Unknown task_id: ${taskID}`)
}
if (task.status === "cancelled") {
return {
status: "cancelled",
task,
}
}
if (task.status === "error") {
return {
status: "error",
task,
}
}
if (task.status === "done") {
return {
status: "done",
task,
latest: {
messageID: task.lastMessageID,
text: task.summary || "",
},
artifactPath: task.artifactPath,
}
}
const waited = await waitForIdle(
client,
task.childSessionID,
context.directory,
timeoutMs,
async () => {
const currentTask = await readTask(projectRoot, taskID)
return currentTask?.status === "cancelled"
},
)
if (waited.cancelled) {
const cancelledTask = await readTask(projectRoot, taskID)
return {
status: "cancelled",
task: cancelledTask || task,
}
}
if (!waited.completed) {
return {
status: "running",
task,
liveStatus: waited.status.type,
waitedMs: timeoutMs,
}
}
const latest = await latestAssistantSummary(client, task.childSessionID, context.directory)
if (!latest.messageID || latest.messageID === task.lastMessageID) {
return {
status: "ready",
task,
}
}
const artifactPath = await writeArtifact(projectRoot, task.id, latest.text)
const completion = await recordTaskCompletion(projectRoot, task.id, latest, artifactPath)
if (completion.cancelled) {
if (artifactPath) {
await rm(artifactPath, { force: true })
}
return {
status: "cancelled",
task: completion.task,
}
}
return {
status: "done",
task: completion.task,
latest,
artifactPath,
}
}
export const TeamOrchestratorPlugin = async ({ client }) => {
return {
tool: {
team_delegate: tool({
description: "Spawn a delegated worker session for a subtask and optionally wait for the result",
args: {
title: tool.schema.string().describe("Short task title"),
agent: tool.schema.string().describe("Subagent to run, such as explore, general, plan, review, or verify"),
prompt: tool.schema.string().describe("The delegated instruction for the worker"),
goal: tool.schema.string().optional().describe("Optional concrete success criteria"),
files: tool.schema.array(tool.schema.string()).optional().describe("Relevant files or directories"),
wait: tool.schema.boolean().optional().describe("Wait for the worker to finish before returning"),
timeout_ms: tool.schema.number().int().positive().optional().describe("Wait timeout in milliseconds when wait is true"),
},
async execute(args, context) {
const projectRoot = projectRootFromContext(context)
const wait = args.wait ?? false
const timeoutMs = args.timeout_ms ?? DEFAULT_WAIT_TIMEOUT_MS
const files = args.files ?? []
const taskID = createTaskID()
const createdAt = now()
const created = await client.session.create({
body: {
parentID: context.sessionID,
title: args.title,
},
query: {
directory: context.directory,
},
})
if (!created.data) {
throw new Error(`Failed to create child session for \"${args.title}\"`)
}
const childSessionID = created.data.id
const task = {
id: taskID,
title: args.title,
agent: args.agent,
goal: args.goal,
files,
prompt: args.prompt,
parentSessionID: context.sessionID,
childSessionID,
status: "running",
createdAt,
updatedAt: createdAt,
artifactPath: undefined,
lastMessageID: undefined,
summary: undefined,
error: undefined,
}
await addTask(projectRoot, task)
context.metadata({
title: `Delegated ${args.agent}: ${args.title}`,
metadata: {
taskID,
childSessionID,
agent: args.agent,
},
})
const handoffPrompt = buildWorkerPrompt({
taskID,
title: args.title,
goal: args.goal,
files,
prompt: args.prompt,
})
try {
await dispatchPrompt(client, context.directory, childSessionID, {
agent: args.agent,
parts: [{ type: "text", text: handoffPrompt }],
})
} catch (error) {
await recordTaskError(projectRoot, taskID, error)
throw error
}
if (!wait) {
return [
`▸ Delegated ${args.agent}: ${args.title}`,
` task: ${taskID}`,
` session: ${childSessionID}`,
"",
"Use `team_wait` to collect the result or `team_message` to continue.",
].join("\n")
}
const outcome = await awaitTaskResult(client, context, projectRoot, taskID, timeoutMs)
if (outcome.status === "running") {
return [
`▸ ${args.title} (still running)`,
` task: ${taskID}`,
` agent: ${args.agent}`,
` session: ${childSessionID}`,
` waited: ${timeoutMs}ms`,
"",
"Use `team_wait` to collect the result later.",
].join("\n")
}
if (outcome.status === "cancelled") {
return [
`○ ${args.title} (cancelled)`,
` task: ${taskID}`,
` session: ${childSessionID}`,
].join("\n")
}
if (outcome.status === "error") {
return [
`✗ ${args.title} (error)`,
` task: ${taskID}`,
` session: ${childSessionID}`,
` error: ${outcome.task.error || "unknown error"}`,
].join("\n")
}
if (outcome.status === "ready") {
return [
`◦ ${args.title} (idle, no new result)`,
` task: ${taskID}`,
` session: ${childSessionID}`,
"",
"Use `team_wait` to collect the result or `team_message` to continue.",
].join("\n")
}
const artifactDisplay = artifactPathForDisplay(projectRoot, outcome.artifactPath)
return [
`✓ ${args.title}`,
` task: ${taskID}`,
` session: ${childSessionID}`,
artifactDisplay ? ` artifact: ${artifactDisplay}` : "",
"",
outcome.latest.text || "The worker finished without a text summary.",
].filter(Boolean).join("\n")
},
}),
team_message: tool({
description: "Send a follow-up message to an existing delegated worker session",
args: {
task_id: tool.schema.string().describe("Task ID from team_delegate"),
message: tool.schema.string().describe("Follow-up instruction for the existing worker"),
wait: tool.schema.boolean().optional().describe("Wait for the worker to finish this follow-up"),
timeout_ms: tool.schema.number().int().positive().optional().describe("Wait timeout in milliseconds when wait is true"),
},
async execute(args, context) {
const projectRoot = projectRootFromContext(context)
const wait = args.wait ?? false
const timeoutMs = args.timeout_ms ?? DEFAULT_WAIT_TIMEOUT_MS
const task = await readTask(projectRoot, args.task_id)
if (!task) {
throw new Error(`Unknown task_id: ${args.task_id}`)
}
if (task.parentSessionID !== context.sessionID) {
throw new Error(`Task ${args.task_id} belongs to a different coordinator session`)
}
if (task.status === "cancelled") {
throw new Error(`Cannot continue cancelled task: ${args.task_id}`)
}
context.metadata({
title: `Message worker ${args.task_id}`,
metadata: {
taskID: args.task_id,
childSessionID: task.childSessionID,
},
})
await updateTask(projectRoot, args.task_id, currentTask => {
if (currentTask.status === "cancelled") {
throw new Error(`Cannot continue cancelled task: ${args.task_id}`)
}
currentTask.status = "running"
currentTask.updatedAt = now()
currentTask.summary = undefined
currentTask.artifactPath = undefined
currentTask.lastMessageID = undefined
delete currentTask.error
return { ...currentTask }
})
try {
await dispatchPrompt(client, context.directory, task.childSessionID, {
agent: task.agent,
parts: [{ type: "text", text: args.message }],
})
} catch (error) {
await recordTaskError(projectRoot, args.task_id, error)
throw error
}
if (!wait) {
return [
`▸ Follow-up sent to ${args.task_id}`,
` session: ${task.childSessionID}`,
` agent: ${task.agent}`,
"",
"Use `team_wait` to collect the updated result.",
].join("\n")
}
const outcome = await awaitTaskResult(client, context, projectRoot, args.task_id, timeoutMs)
if (outcome.status === "running") {
return [
`▸ ${args.task_id} (still running)`,
` session: ${task.childSessionID}`,
` agent: ${task.agent}`,
` waited: ${timeoutMs}ms`,
"",
"Use `team_wait` to collect the result later.",
].join("\n")
}
if (outcome.status === "cancelled") {
return [
`○ ${args.task_id} (cancelled)`,
` session: ${task.childSessionID}`,
].join("\n")
}
if (outcome.status === "error") {
return [
`✗ ${args.task_id} (error)`,
` session: ${task.childSessionID}`,
` error: ${outcome.task.error || "unknown error"}`,
].join("\n")
}
if (outcome.status === "ready") {
return [
`◦ ${args.task_id} (idle, no new result)`,
` session: ${task.childSessionID}`,
"",
"Use `team_message` to send a follow-up or `team_wait` again later.",
].join("\n")
}
const artifactDisplay = artifactPathForDisplay(projectRoot, outcome.artifactPath)
return [
`✓ ${args.task_id} responded`,
` session: ${task.childSessionID}`,
artifactDisplay ? ` artifact: ${artifactDisplay}` : "",
"",
outcome.latest.text || "The worker finished without a text summary.",
].filter(Boolean).join("\n")
},
}),
team_wait: tool({
description: "Wait for a delegated worker session to become idle and fetch its latest summary",
args: {
task_id: tool.schema.string().describe("Task ID from team_delegate"),
timeout_ms: tool.schema.number().int().positive().optional().describe("Maximum time to wait in milliseconds"),
},
async execute(args, context) {
const projectRoot = projectRootFromContext(context)
const timeoutMs = args.timeout_ms ?? DEFAULT_WAIT_TIMEOUT_MS
const task = await readTask(projectRoot, args.task_id)
if (!task) {
throw new Error(`Unknown task_id: ${args.task_id}`)
}
if (task.parentSessionID !== context.sessionID) {
throw new Error(`Task ${args.task_id} belongs to a different coordinator session`)
}
context.metadata({
title: `Wait for ${args.task_id}`,
metadata: {
taskID: args.task_id,
childSessionID: task.childSessionID,
},
})
const outcome = await awaitTaskResult(client, context, projectRoot, args.task_id, timeoutMs)
if (outcome.status === "running") {
return [
`▸ ${args.task_id} is running`,
` session: ${task.childSessionID}`,
` status: ${outcome.liveStatus}`,
` waited: ${timeoutMs}ms`,
].join("\n")
}
if (outcome.status === "cancelled") {
return [
`○ ${args.task_id} was cancelled`,
` session: ${task.childSessionID}`,
].join("\n")
}
if (outcome.status === "error") {
return [
`✗ ${args.task_id} is in an error state`,
` session: ${task.childSessionID}`,
` error: ${outcome.task.error || "unknown error"}`,
].join("\n")
}
if (outcome.status === "ready") {
return [
`◦ ${args.task_id} is idle (no new result)`,
` session: ${task.childSessionID}`,
"",
"Use `team_message` to send a follow-up or `team_wait` again later.",
].join("\n")
}
const artifactDisplay = artifactPathForDisplay(projectRoot, outcome.artifactPath)
return [
`✓ ${args.task_id} is idle`,
` session: ${task.childSessionID}`,
artifactDisplay ? ` artifact: ${artifactDisplay}` : "",
"",
outcome.latest.text || "The worker is idle, but no assistant summary was found yet.",
].filter(Boolean).join("\n")
},
}),
team_list: tool({
description: "List delegated worker tasks for the current coordinator session",
args: {
status: tool.schema.enum(["running", "ready", "done", "cancelled", "error"]).optional().describe("Optional status filter"),
},
async execute(args, context) {
const projectRoot = projectRootFromContext(context)
const registry = await loadRegistry(projectRoot)
const scopedTasks = registry.tasks.filter(task => task.parentSessionID === context.sessionID)
const lines = []
const statusCounts = {}
for (const task of scopedTasks) {
let liveStatus = task.status
if (task.status === "running") {
const sessionStatus = await getLiveSessionStatus(client, task.childSessionID, context.directory)
if (sessionStatus.type === "idle") {
liveStatus = task.summary ? "done" : "ready"
} else {
liveStatus = "running"
}
}
statusCounts[liveStatus] = (statusCounts[liveStatus] || 0) + 1
if (args.status && liveStatus !== args.status) {
continue
}
lines.push(formatTaskLine(task, liveStatus))
}
if (lines.length === 0) {
return args.status
? `No tasks matched status \"${args.status}\" for this coordinator session.`
: "No delegated tasks found for this coordinator session."
}
const summaryParts = Object.entries(statusCounts)
.sort(([,a], [,b]) => b - a)
.map(([s, c]) => `${c} ${s}`)
.join(", ")
const shown = args.status ? `${lines.length} of ${scopedTasks.length}` : `${lines.length}`
const summaryLabel = args.status ? "All tasks:" : ""
const summaryLine = summaryLabel ? `${summaryLabel} ${summaryParts}` : summaryParts
return [`Delegated tasks (${shown}):`, ...lines, "", summaryLine].join("\n")
},
}),
team_cancel: tool({
description: "Abort a delegated worker session and mark the task as cancelled",
args: {
task_id: tool.schema.string().describe("Task ID from team_delegate"),
},
async execute(args, context) {
const projectRoot = projectRootFromContext(context)
const task = await readTask(projectRoot, args.task_id)
if (!task) {
throw new Error(`Unknown task_id: ${args.task_id}`)
}
if (task.parentSessionID !== context.sessionID) {
throw new Error(`Task ${args.task_id} belongs to a different coordinator session`)
}
if (task.status === "done" || task.status === "cancelled") {
const icon = task.status === "done" ? "✓" : "○"
return `${icon} ${args.task_id} is already ${task.status}; not cancelling.`
}
context.metadata({
title: `Cancel ${args.task_id}`,
metadata: {
taskID: args.task_id,
childSessionID: task.childSessionID,
},
})
const liveStatus = await getLiveSessionStatus(client, task.childSessionID, context.directory)
if (liveStatus.type === "idle" && task.summary) {
return `✓ ${args.task_id} is already idle with a completed result; not cancelling.`
}
await client.session.abort({
path: { id: task.childSessionID },
query: { directory: context.directory },
})
const cancelResult = await updateTask(projectRoot, args.task_id, currentTask => {
if (currentTask.status === "done" || currentTask.status === "cancelled") {
return { alreadyTerminal: true, task: currentTask }
}
currentTask.status = "cancelled"
currentTask.updatedAt = now()
delete currentTask.error
return { alreadyTerminal: false, task: { ...currentTask } }
})
if (cancelResult.alreadyTerminal) {
const icon = cancelResult.task.status === "done" ? "✓" : "○"
return `${icon} ${args.task_id} is already ${cancelResult.task.status}; no action taken.`
}
return [
`○ Cancelled ${args.task_id}`,
` session: ${task.childSessionID}`,
` title: ${task.title}`,
].join("\n")
},
}),
},
}
}