conductor-oss / conductor-oss/csharp-sdk

`AgentResult.Events` is never assigned, and every non-worker tool is named after its task type

Open
#173 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
54
Forks
23
Avg merge
4d 21h
Merged PRs (30d)
2

Description

## Bug Description

`AgentResult.Events` is `null` after `WaitAsync`, so enumerating it throws. The property is
effectively dead: nothing in the library ever assigns it.

`ToolCalls` is wrong for every tool kind except plain workers. An HTTP tool is named `"HTTP"`, an
MCP tool `"CALL_MCP_TOOL"`, an agent used as a tool `"SUB_WORKFLOW"`, a human tool `"HUMAN"`, an
image tool `"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

**`Events` omitted.** Declared at `Conductor.AI/Result.cs:156`. The object initializer in
`BuildResult` (`:539-548`) sets `ExecutionId`, `Status`, `Output`, `Error`, `ToolCalls`,
`TokenUsage` and `FinishReason`, and omits it. `Events` appears nowhere else in `Conductor.AI`, and
`AgentResult` is not deserialized from a payload that would supply it, so it is always `null`
rather than empty. `StreamAsync` (`:331`) yields events but never builds a result from them.

**Name taken from the task type.** `Result.cs:569`:

```csharp
var tc = new Dictionary { ["name"] = task!["taskType"]?.GetValue() ?? "" };
```

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 discarded at `:576-577`. A tool task's `inputData`:

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

`method`, `_agent_tool_name` and `taskDefName` all carry it. None is read.

**Selection keys on the provider's tool-call ID.** `Result.cs:566` requires
`refName.StartsWith("call_")`. The server does not add that 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-type filter, so selection rests entirely on the reference-name prefix.

## Steps to Reproduce

```csharp
var result = await handle.WaitAsync();
foreach (var e in result.Events) { } // NullReferenceException
result.ToolCalls[0]["name"]; // "HTTP" for an HTTP tool
```

## Expected Behavior

Assign `Events` in `BuildResult`, or drop the property if it is not intended to be populated on
that path.

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
`taskDefName`. Never from the reference name, which carries provider-controlled data.

Two corrections to the above, found while implementing it:

- `TYPE_MAP` is not the whole allowlist. It has no `generate_pdf` entry, and media tools take their
task type from `toolType.toUpperCase()` instead (`ToolCompiler.java:427-436`), so a `generate_pdf`
tool compiles to `GENERATE_PDF` and has to be added by hand.
- `_agent_tool_name` is set for every tool kind only on the static dispatch path
(`JavaScriptBuilder.enrichToolsScript`, `:797`). The dynamic-tools path
(`enrichToolsScriptDynamic`, `:1607-1609`) sets no `_agent_tool_name` at all and sets
`_agent_state` only on `SIMPLE` tasks, so a non-worker tool dispatched there carries no marker.

## Additional Notes

Verified against `origin/main` @ `b5d7f22`.

`AgentStatusMappingTests.cs:164` covers the worker case and passes for the right reason. Its fixture
(`:173-179`) omits `method` and `_agent_tool_name`, and its reference name (`call_echo_1`) lacks the
fork index and loop suffix that real payloads carry, so neither name resolution nor detection is
genuinely exercised. Nothing covers `Events`.

Same family of defects filed against `python-sdk`, `javascript-sdk` and `java-sdk`. This
implementation is a port of the Java one (`Result.cs:552` names
`AgentHandle.extractFromTasks`), itself a port of Python's.

A related server-side issue is filed against `conductor-oss/conductor`:
`AgentEventListener.isToolTask` excludes `HTTP`, `CALL_MCP_TOOL`, `SUB_WORKFLOW` and `HUMAN`, so
those kinds emit no tool events server-side either. Fixing this SDK gets their names right in
`ToolCalls`.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in Conductor.AI/Result.cs, especially BuildResult around lines 539-577, and review AgentStatusMappingTests.cs around lines 164-179. Run the existing tests first, then cover Events population, tool-kind detection, provider-independent selection, and inputData name resolution. Done means non-null Events and correct ToolCalls names for worker and non-worker tools across provider ID formats.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.