Skip to content

waitForResult() returns no events, and every non-worker tool is named after its task type #170

Description

@ambiorix2099

Bug Description

Two problems in what AgentResult reports after waitForResult():

  • getEvents() is always an empty list. Events are only populated on the streaming path.
  • Tool names come from the task type, so every tool kind except plain workers is mislabelled: an
    HTTP tool is reported as "HTTP", an MCP tool as "CALL_MCP_TOOL", an agent used as a tool as
    "SUB_WORKFLOW", a human tool as "HUMAN", and an image tool as "GENERATE_IMAGE".

Tool calls are also missed entirely for non-OpenAI providers, because selection keys on the
provider's tool-call ID format.

Root Cause

All in conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/AgentHandle.java
unless noted.

Events dropped. :367 (in buildResult) and :469 (in fromWorkflow) pass null for the
constructor's events parameter, and AgentResult.java:48 normalizes it to an empty list, so
nothing signals that nothing was collected. Both are reached from waitForResult() (:82).
AgentStream.java:321 populates it properly.

Name taken from the task type. :406, tc.put("name", taskType). That is correct only for a
worker tool, because Conductor sets an executed SIMPLE task's taskType to the task's own name
(SimpleTaskMapper.java:86 in conductor-oss/conductor). Every other tool kind carries its
system task type there.

The real name is available and thrown away. A tool task's inputData:

{"_agent_tool_name": "get_weather", "_agent_state": {},
 "method": "get_weather", "city": "San Francisco"}

:412-418 strips method as an internal key, and neither _agent_tool_name nor
getTaskDefName() is read on this path.

The streaming path does not have this defect: it takes the name from the server
(AgentEvent.java:198, data.get("toolName")). So for the tool kinds the server emits events for,
the two paths report the same call under different names.

Selection keys on the provider's tool-call ID. :402-404:

if (refName != null && refName.startsWith("call_") && outputData != null) {

The server does not add a call_ prefix. It seeds the reference from the provider's
toolCall.id(), falling back to a UUID, then appends the fork index and loop iteration, giving
call_PMnNIdOPvm9EQ8e6tn2kbxPY_0__1 for OpenAI. Anthropic IDs start toolu_ and are not matched.
Adding toolu_ is not a fix, since the next provider picks its own format.

There is also no system-task filter beyond LLM_CHAT_COMPLETE (:392-398), so selection rests
entirely on the reference-name prefix.

Steps to Reproduce

AgentResult result = handle.waitForResult();
result.getEvents();      // [] always, whatever the agent did

For naming, register an agent with one HTTP tool and check
result.getToolCalls().get(0).get("name"), which is "HTTP". For detection, run any agent against
an Anthropic-backed model and getToolCalls() is empty.

Expected Behavior

Populate events on the non-streaming path, or make the empty case distinguishable from "no events
occurred".

Identify a tool task by task type, allowlisting off the server's ToolCompiler.TYPE_MAP plus the
worker case, and resolve the name from inputData._agent_tool_name, then inputData.method, then
getTaskDefName(). Never from the reference name, which carries provider-controlled data.
_agent_tool_name is the only key the server sets for every tool kind.

Both paths should report the same name for the same call.

Additional Notes

Verified against origin/main @ eda9c6a3f.

extractFromTasks (:382) has no unit test, and the e2e suite verifies that tools run via a
side-effect flag inside the tool body rather than by asserting on getToolCalls(), so the
extraction is not covered at either level.

A related server-side issue is filed against conductor-oss/conductor:
AgentEventListener.isToolTask excludes HTTP, CALL_MCP_TOOL, SUB_WORKFLOW and HUMAN, so
those four kinds emit no tool events on the streaming path either. Fixing this SDK gets their names
right in getToolCalls(). Getting them onto the stream needs that server fix as well, and for HTTP
and agent-as-tool it needs a third change, since those complete as async system tasks that never
notify the task listener.

Same family of defects filed against python-sdk, javascript-sdk and csharp-sdk.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions