Skip to content

Conversation

@IgorWarzocha
Copy link
Owner

@IgorWarzocha IgorWarzocha commented Jan 7, 2026

Summary

  • implement async subagents via Task tool (blocking vs async), with background execution, live tool-call summaries, and completion/error notifications
  • deliver subtask results back to the main session, including system-context injection and UI reminders aligned to Task tool output
  • refine TUI UX for async agents: reminder rendering, background task list placement, and sidebar fold behavior
  • clarify Task tool/subagent usage and async parameter guidance for models

Testing

  • bun run --cwd /home/igorw/Work/opencode/packages/opencode typecheck

Test User and others added 30 commits January 3, 2026 22:19
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
@IgorWarzocha IgorWarzocha changed the base branch from dev to howcode January 7, 2026 04:00
@gemini-code-assist
Copy link

Summary of Changes

Hello @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

  • Asynchronous Subtask Management: Introduced an 'async' flag to the task tool, allowing subagents to run in the background without blocking the main agent. This includes concurrency limits and improved notification mechanisms for completion or failure.
  • Enhanced User Experience for Subtasks: Improved the TUI (Terminal User Interface) to better display and manage asynchronous subtasks. This includes a new 'Background Tasks' section in the sidebar, toast notifications for task status changes, and clickable subtask reminders that link to the subagent session.
  • Git Worktree Integration: Added functionality to create and manage Git worktrees directly from the application, enabling users to start new sessions within isolated development environments. This is reflected in the UI for new session creation and project selection.
  • Refined Permission Handling: Updated the permission system to allow users to provide specific feedback when rejecting a tool call, enhancing the agent's ability to learn and correct its behavior.
  • Internal Configuration and Dependency Updates: Removed several old .github and .opencode configuration files and scripts, updated @opentui/core and @opentui/solid dependencies, and enhanced Kotlin LSP root detection logic.
Ignored Files
  • Ignored by pattern: .github/workflows/** (18)
    • .github/workflows/deploy.yml
    • .github/workflows/docs-update.yml
    • .github/workflows/duplicate-issues.yml
    • .github/workflows/generate.yml
    • .github/workflows/notify-discord.yml
    • .github/workflows/opencode.yml
    • .github/workflows/publish-github-action.yml
    • .github/workflows/publish-vscode.yml
    • .github/workflows/publish.yml
    • .github/workflows/release-github-action.yml
    • .github/workflows/review.yml
    • .github/workflows/stale-issues.yml
    • .github/workflows/stats.yml
    • .github/workflows/sync-zed-extension.yml
    • .github/workflows/test.yml
    • .github/workflows/triage.yml
    • .github/workflows/typecheck.yml
    • .github/workflows/update-nix-hashes.yml
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@IgorWarzocha IgorWarzocha changed the title fix(async-agents): improve subtask UX and guidance feat: async agents with prompting & /finished Jan 7, 2026
@IgorWarzocha IgorWarzocha changed the title feat: async agents with prompting & /finished feat: async agents with prompting & additional slash commands Jan 7, 2026
Copy link

@gemini-code-assist gemini-code-assist bot left a 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.

Comment on lines 279 to 305
.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,
},
],
})
})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

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,
                },
              ],
            })
          })

Comment on lines 1500 to 1521
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}`,
},
],
})
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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,
            },
          ],
        })
      }
    }

Comment on lines 1323 to 1329
<For each={props.parts}>
{(part) => (
<Show when={part.type === "subtask"}>
<SubtaskPartComp last={false} part={part as any} message={undefined as any} />
</Show>
)}
</For>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

@IgorWarzocha
Copy link
Owner Author

/oc review this and address Gemini's comments - must address all of them.

Co-authored-by: IgorWarzocha <IgorWarzocha@users.noreply.github.com>
@opencode-agent
Copy link

opencode-agent bot commented Jan 7, 2026

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:

  • Changed status: "completed"status: "error"
  • Replaced undefined summary: text with error: errMessage

2. Consistency Improvement (packages/opencode/src/session/prompt.ts:1515-1520)

Updated /finished command to use subtask part instead of text part:

  • Maintains consistency with async task notifications
  • Provides structured communication between parent and child sessions

3. Type Safety Fix (packages/opencode/src/cli/cmd/tui/routes/session/index.tsx:1525, 1326, 1479-1480)

Removed as any casts and improved SubtaskPartComp:

  • Changed required message: AssistantMessage prop → optional indented?: boolean prop
  • Updated component to use paddingLeft={props.indented ? 3 : 0} for styling
  • Updated all call sites to use explicit indented prop
  • Dynamic component now conditionally passes correct props based on component type

Please run bun run typecheck to verify all changes are type-safe.

New%20session%20-%202026-01-07T04%3A06%3A07.980Z
opencode session  |  github run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants