-
Notifications
You must be signed in to change notification settings - Fork 0
feat: async agents with prompting & additional slash commands #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: howcode
Are you sure you want to change the base?
Conversation
Co-authored-by: IgorWarzocha <IgorWarzocha@users.noreply.github.com>
Expand AGENTS.md with comprehensive coding and operational protocols. Implement logic to terminate subagent sessions and notify parents, while preventing the removal of primary sessions.
Replace the 'kill' command with 'wipe' to remove an idle subagent session. Add 'wipeout' to remove all idle subagent sessions at once. Delete obsolete agent and command definitions in .opencode.
- replace existing /kill with /wipe (idle subagents only, no parent notification) - add /wipeout to delete all idle child sessions from parent - fix race condition by navigating before deletion in TUI - update app.tsx to navigate to parent session on deletion - improve autocomplete logic to prevent duplicate commands and handle session status
Add async mode to task tool allowing agents to spawn background subtasks without blocking. Subtasks notify parent on completion or failure via synthetic messages. Core features: - Add and parameters to TaskTool schema - Add config (default: 3) - Add helper to filter busy/retry children - Implement fire-and-forget execution with .then()/.catch() callbacks - Send <background-task-completed> notifications on success - Send <background-task-failed> notifications with retry guidance - Enforce concurrency limit before starting async tasks Safety & lifecycle: - Block /new command when async children are running (TUI) - Include running task summaries in compaction prompt - Add GET /session/:sessionID/children/running endpoint - Validate parent exists before notification (handles deleted sessions) - Regenerate SDK with new endpoint Spec: @specs/async-agents.md
…eservation - Add Background Tasks section to sidebar showing all child sessions - Show status colors: yellow (running), red (retrying), green (completed) - Add toast notifications when background tasks complete - Fix agent/model preservation when async task notifies parent - Fix /finished command to also preserve parent's agent/model - Update spec to mark Phase 3 complete
…pletion UI - Enable live tool call updates for async tasks by directly updating parent tool part metadata - Extend SubtaskPart with status, error, and session linking fields - Implement SubtaskPart component in TUI for nice, clickable completion notifications - Standardize subagent notification UI to mimic synchronous agents while staying non-interactive for LLM
…ressions - Refine SubtaskPartComp to use BlockTool with standard agent header style - Fix UserMessage to render subtask parts and show boxes for synthetic notifications - Restore live tool call updates for async tasks by correctly merging metadata - Ensure original task tool call and completion notifications are fully functional and clickable
… ui broken, task tool not issuing return messages to main agent
…m notification format for model context
Summary of ChangesHello @IgorWarzocha, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly improves the user experience and underlying mechanics for managing asynchronous subtasks and new session creation. It introduces a 'fire-and-forget' capability for subagents, allowing for parallel execution of tasks, alongside robust UI enhancements for monitoring and interacting with these background processes. Additionally, it integrates Git worktree management for flexible session environments and refines permission feedback mechanisms. Highlights
Ignored Files
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces significant enhancements for managing asynchronous subagent tasks, including new commands (/finished, /wipe, /wipeout) and improved UI/UX for subtask visibility and control. Key changes involve updating the TaskTool to support asynchronous execution with concurrency limits, adding new subtask message parts for structured communication between parent and child sessions, and refining the TUI to display background task statuses in the sidebar, show toast notifications for task completion/failure, and allow navigation to subagent sessions directly from task summaries. The PR also refactors client creation logic and updates agent development guidelines. Review comments highlight a bug in an error handling catch block that incorrectly notifies success instead of failure, and suggest standardizing the /finished command to use subtask parts for consistency with async task notifications, while also recommending improved type safety in SubtaskPartComp by replacing as any with explicit props.
| .catch(async (error) => { | ||
| asyncUnsub() | ||
| // Validate parent still exists before notifying | ||
| const parent = await Session.get(parentSessionID).catch(() => null) | ||
| if (!parent) return | ||
|
|
||
| const errMessage = error instanceof Error ? error.message : String(error) | ||
|
|
||
| // Send simple failure notification (synthetic, user-visible only) | ||
| await SessionPrompt.prompt({ | ||
| sessionID: parentSessionID, | ||
| agent: parentAgent, | ||
| model: parentModel, | ||
| noReply: false, | ||
| parts: [ | ||
| { | ||
| type: "subtask", | ||
| subagentSessionID: session.id, | ||
| description: taskName, | ||
| status: "completed", | ||
| prompt: params.prompt, | ||
| agent: agent.name, | ||
| summary: text, | ||
| }, | ||
| ], | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There appears to be a bug in this catch block. It seems to be a copy-paste from the then block above it. It attempts to notify the parent of a success (status: "completed") and uses a text variable that is not defined in this scope, which will cause a runtime error. This block should handle the error case and notify the parent of a failure.
.catch(async (error) => {
asyncUnsub()
// Validate parent still exists before notifying
const parent = await Session.get(parentSessionID).catch(() => null)
if (!parent) return
const errMessage = error instanceof Error ? error.message : String(error)
// Notify error
await SessionPrompt.prompt({
sessionID: parentSessionID,
agent: parentAgent,
model: parentModel,
noReply: true,
parts: [
{
type: "subtask",
subagentSessionID: session.id,
description: taskName,
status: "error",
error: errMessage,
prompt: params.prompt,
agent: agent.name,
},
],
})
})| if (input.command === "finished") { | ||
| const session = await Session.get(input.sessionID) | ||
| if (session.parentID) { | ||
| const text = result.parts.findLast((x) => x.type === "text")?.text ?? "" | ||
| // Get parent's last user message to preserve agent and model | ||
| const parentMessages = await Session.messages({ sessionID: session.parentID, limit: 10 }) | ||
| const parentLastUser = parentMessages.findLast((m) => m.info.role === "user")?.info as | ||
| | MessageV2.User | ||
| | undefined | ||
| await prompt({ | ||
| sessionID: session.parentID, | ||
| agent: parentLastUser?.agent, | ||
| model: parentLastUser?.model, | ||
| parts: [ | ||
| { | ||
| type: "text", | ||
| text: `Subtask finished with the following summary:\n\n${text}\n\nSession: ${session.id}`, | ||
| }, | ||
| ], | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation for the /finished command creates a new text part in the parent session. This is inconsistent with how async tasks notify their parent upon completion, which uses a subtask part that gets converted into a system message. Using a subtask part provides a more structured and consistent way for sub-agents to report back to their parents. It also avoids sending a user-like message to the parent agent, which can be confusing for the model.
if (input.command === "finished") {
const session = await Session.get(input.sessionID)
if (session.parentID) {
const text = result.parts.findLast((x) => x.type === "text")?.text ?? ""
// Get parent's last user message to preserve agent and model
const parentMessages = await Session.messages({ sessionID: session.parentID, limit: 10 })
const parentLastUser = parentMessages.findLast((m) => m.info.role === "user")?.info as
| MessageV2.User
| undefined
await prompt({
sessionID: session.parentID,
agent: parentLastUser?.agent,
model: parentLastUser?.model,
parts: [
{
type: "subtask",
subagentSessionID: session.id,
description: session.title,
status: "completed",
agent: result.info.agent,
summary: text,
},
],
})
}
}| <For each={props.parts}> | ||
| {(part) => ( | ||
| <Show when={part.type === "subtask"}> | ||
| <SubtaskPartComp last={false} part={part as any} message={undefined as any} /> | ||
| </Show> | ||
| )} | ||
| </For> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of as any for the part and message props when calling SubtaskPartComp undermines type safety. The message prop seems to be used only for conditional padding. Consider passing an explicit prop like indented to SubtaskPartComp to control styling, which would make the component's interface clearer and remove the need for undefined as any.
|
/oc review this and address Gemini's comments - must address all of them. |
Co-authored-by: IgorWarzocha <IgorWarzocha@users.noreply.github.com>
|
All three issues from Gemini's review have been addressed: 1. Critical Bug Fix (packages/opencode/src/tool/task.ts:279-305)Fixed the catch block that incorrectly reported success on error:
2. Consistency Improvement (packages/opencode/src/session/prompt.ts:1515-1520)Updated
3. Type Safety Fix (packages/opencode/src/cli/cmd/tui/routes/session/index.tsx:1525, 1326, 1479-1480)Removed
Please run |

Summary
Testing