A delegation that never started answers the model
What went wrong
delegate_to_agent is exempt from the per-tool timeout, because a delegation legitimately runs for
minutes. Its own bounds are meant to end it: the create fails within the request timeout, and a
dispatched sub-thread is read for at most ten minutes (WaitForDelegationResult).
There was one path with no bound at all. When the lifecycle stream is wired (every production chat),
the call resolves on exactly two events for its call id: Dispatched and Failed. The launch
(ChatClientAgentFactory.ExecuteDelegationAsync) emits one of them once it has tried to create the
sub-thread. But three refusals return BEFORE that, as a plain text chunk, and then complete:
- the agent name does not resolve (
Agent '…' not found); - there is no execution context;
- the delegation depth cap is reached.
The tool ignored the launch's completion on purpose (a successful launch also completes at once, and the sub-thread's summary arrives later through the lifecycle path). So for a refused launch nothing was left that could settle the call. It waited until the round cap.
The incident
Measured on the control instance, response cell
Hosting/Triage/_Thread/bug-systemorph-meshweaver-5910/e693ac26 (2026-09-30):
- The round's last tool call was
delegate_to_agentwithagentName: Feedback Agent. That is the agent's display NAME. Its id isfeedback-agent, which the model had just read from a search result. - The call's recorded duration is 1,414,845 ms (23.6 minutes), ending when the 30-minute round cap aborted the round.
- No sub-thread exists for that call. The cell has exactly one child thread, from the earlier
delegation to
Worker, last written three minutes before this call started.
So the call was never waiting on a sub-agent. It was waiting on an event that could not come. The round's work up to that point was lost with it.
The fix
The launch emits its start event before it completes, on the same thread, and the lifecycle stream
delivers synchronously. Therefore a launch that completes with no start event seen for this call
started nothing. DelegationTool now settles the call at that moment with what the launch said, as a
failed tool result (DelegationTool.NotStartedResult):
Error: delegation to Feedback Agent was not started — Agent 'Feedback Agent' not found.
delegate_to_agent takes an agent ID, not a display name; the agents this thread can delegate to are: …
The "not found" refusal now lists the ids the thread can delegate to, so the model can correct the call in the same round.
This adds no timer and changes no bound. The round cap stays the outer authority for a round; what changed is that this tool no longer has a path on which only the round cap can end it.
Tests
DelegationNotStartedTest:
ALaunchThatRefusesWithoutAStartEvent_SettlesTheCallWithTheRefusalis the incident. With the fix disabled the call never completes and the test fails on its 30-second timeout.ALaunchThatEndsSilentlyWithoutAStartEvent_SettlesTheCallcovers a launch that says nothing.ALaunchThatReportedItsStart_IsSettledByThatEvent_NotByItsCompletionis the control: the production create-failure ordering (aFailedevent, then the error chunk, then completion) still reports the create failure, not "was not started".
What this does not cover
The same log line (No model call was in flight) has a second cause that this change leaves alone.
One later recurrence was read, response cell
Hosting/Triage/_Thread/bug-systemorph-meshweaver-plugins-2819/1a003c7b (2026-10-05):
- the first model call started nine minutes after the cell was created (the cell text ends with
Agent initialization stalled); - two model calls then took 731 s and 736 s, each ending at exactly 65,536 output tokens;
- the last
delegate_to_agent, to a valid agent id, was 51 seconds old when the cap fired.
Nothing hung there. The round was out of time and the cap cut a healthy delegation, which is the cap doing its job. Whether a round should stop starting a delegation it has no time left to wait for is an open design question.
What this does not establish
- The other two recurrences (2026-10-05 and 2026-10-07) were not read, so which of the two causes each had is not shown.
send_to_sub_threadstill answersQueuedwithout waiting for the write's verdict.