agentscope-ai / agentscope-ai/agentscope-java
[Bug]:Subagent completion notification lacks global progress; SubagentsMiddleware.wireMessageBus is unreachable code
- Lenguaje dominante
- Java
- Estrellas
- 5.6k
- Forks
- 1.3k
- Merge medio
- 4 d 12 h
- PR fusionados (30 d)
- 77
Descripción
**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
```java
// 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
```
2. 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.
3. 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`)
Guía de contribución
Evaluación
Este issue todavía no se ha evaluado.