Skip to content

[Bug]:Subagent completion notification lacks global progress; SubagentsMiddleware.wireMessageBus is unreachable code #2792

Description

@sailor-smile

Describe the bug

Two related problems. The second one is what blocks fixing the first.

Problem 1 — completion notifications carry no global progress.
After spawning N async subagents, each completion pushes its own notification. The notification
only states that a task finished; it carries no "M of N terminal" information. On receiving the
first notification the LLM cannot tell whether other tasks are still running, treats it as an
overall completion signal, wraps up early and reports back.

Problem 2 — the extension point for adding progress is unreachable.
SubagentsMiddleware.wireMessageBus looks like the public entry point for registering the
completion notification callback, but it is never invoked by the SDK, so overriding or replacing it
has no effect whatsoever.

To Reproduce

  1. You code
// Problem 1
HarnessAgent supervisor = HarnessAgent.builder()
        .model(model)
        .messageBus(messageBus)
        .subagentFactory("worker", name -> workerAgent)
        .build();

// Spawn 5+ async subagents (background task mode) with clearly different durations,
// e.g. the first one much faster than the rest.
supervisor.streamEvents(new UserMessage(promptThatSpawnsManyBackgroundTasks), ctx).subscribe();

// Problem 2: try to customise the completion notification through the public method.
subagentsMiddleware.wireMessageBus(messageBus);  // never called by the SDK -> no effect
  1. How to execute

For problem 1, run the flow and watch what the LLM does right after the first completion
notification arrives, while other tasks are still running.

For problem 2, register a custom completion callback via wireMessageBus and check whether it is
ever invoked during a run that includes async subagents.

  1. See error

Problem 1: the LLM finishes and reports results while other subagents are still executing; their
results are lost — they do complete later and push notifications, but nothing is waiting for them
by then.

Problem 2: the callback registered through wireMessageBus is never invoked, and the notification
format is unchanged.

Expected behavior

  1. Completion notifications should carry enough information to judge overall progress (for example
    [3/8 tasks terminal]), so the LLM can distinguish "one of them finished" from "all finished".
  2. As a public method, wireMessageBus should be on the SDK's actual execution path; otherwise it
    should not exist in the form of a public extension point.

Error messages

No exception in either case; both failures are silent.

For problem 2 in particular, the silence is the harmful part: HarnessAgent$Builder.build()
performs the registration through its own private static method
wireTaskRepositoryMessageBus, a different path entirely. Code that patches wireMessageBus
compiles and appears correct, yet the notification format never changes at runtime and nothing
indicates why.

Environment (please complete the following information):

  • AgentScope-Java Version: 2.0.0 (agentscope-harness 2.0.0)
  • Java Version: 17
  • OS: macos

Additional context

Root cause.

Problem 1: notification text is built from the perspective of a single task, while
TaskRepository actually holds every task of the session together with its status — global
progress is computable at notification time. The information is available, just unused.

Problem 2: the public wireMessageBus coexists with the private
wireTaskRepositoryMessageBus that actually takes effect, and the public one is unreachable.
This is both dead code and an API contract problem: the public signature implies an extension
point that is not one.

The only workaround available externally is to reflectively read messageBus and
taskRepository after build() and re-register through
WorkspaceTaskRepository.setCompletionCallback, relying on the undocumented
"later registration wins" behaviour. That path couples to private field names and can break
silently on every SDK upgrade.

Suggested fix.

Problem 1: when building the completion notification, count terminal and total tasks via
TaskRepository.listTasks(ctx, sessionId, null) and include progress in the text, e.g.

Subagent task {taskId} completed [3/8 tasks terminal]: ...

TaskStatus.isTerminal() already exists, so no new API is needed.

Problem 2: either

  1. have build() go through the public wireMessageBus so it becomes a real extension point
    (preferred — customising the notification format is a legitimate need), or
  2. remove wireMessageBus if opening it up is not intended, so the public signature stops
    implying otherwise.

Either way, consider offering a supported public route for customising completion notifications,
for example exposing the completion callback on the builder, so integrators do not need reflection
over private fields.

Affected classes:

  • io.agentscope.harness.agent.middleware.SubagentsMiddleware
  • io.agentscope.harness.agent.HarnessAgent$Builder (wireTaskRepositoryMessageBus)
  • io.agentscope.harness.agent.subagent.task.WorkspaceTaskRepository (setCompletionCallback)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions