feat(worker-mcp): raise wait_for_task_change timeout, expose queue slot state

MaxTimeoutSeconds was 170s against runs that take tens of minutes, forcing
a dozen full-context wait rounds per long-running batch. Raise it to 900s
and raise MCP_TOOL_TIMEOUT in lockstep (ClaudeProcess + every
InteractiveLaunchSpecService launch spec) to 930000ms so the client
connection actually stays open that long instead of aborting first.

Add get_queue_state (QueueStateMcpTools): configured vs. effective
parallel-slot count (via QueueService.GetSlotCountsAsync, extracted from
the former GetEffectiveMaxParallelAsync), active slots with taskId +
startedAt including the run_task_now override slot, and queued tasks in
pick order -- so a caller can observe queue occupancy instead of inferring
it from maxParallelExecutions.
This commit is contained in:
mika kuns
2026-08-05 20:47:57 +02:00
parent bdee731376
commit d43b5fcefc
10 changed files with 299 additions and 36 deletions
+11 -6
View File
@@ -12,10 +12,13 @@ public sealed record WaitForTaskChangeResult(IReadOnlyList<TaskStatusChangeDto>
[McpServerToolType]
public sealed class TaskWaitMcpTools
{
// InteractiveLaunchSpecService sets MCP_TOOL_TIMEOUT=200000ms for the list handler
// session; this cap leaves a ~30s margin so the tool itself reports TimedOut instead
// of racing the client's own abort.
internal const int MaxTimeoutSeconds = 170;
// Every ClaudeDo-owned launcher (ClaudeProcess for headless runs, InteractiveLaunchSpecService
// for ConPTY sessions) sets MCP_TOOL_TIMEOUT=930000ms on the claude CLI process; this cap
// leaves a ~30s margin under that so the tool itself reports TimedOut instead of racing the
// client's own abort. A caller running claude with a different MCP_TOOL_TIMEOUT (or none --
// the CLI default is 60s) will see its own client-side timeout fire first; this tool has no
// way to detect or compensate for that from the server side.
internal const int MaxTimeoutSeconds = 900;
private static readonly TimeSpan PollInterval = TimeSpan.FromMilliseconds(500);
private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
@@ -27,9 +30,11 @@ public sealed class TaskWaitMcpTools
[McpServerTool, Description(
"Blocks until at least one of the given tasks leaves Queued/Running, or until timeoutSeconds elapses " +
"(clamped server-side to 170s). Returns immediately if any task is already outside Queued/Running " +
"(clamped server-side to 900s). Returns immediately if any task is already outside Queued/Running " +
"when called (an unknown id is reported as status \"NotFound\" and counts as changed). Use this instead " +
"of polling get_task in a loop. Result: { changed: [{ taskId, status }], timedOut }.")]
"of polling get_task in a loop. Requires the calling claude process to run with " +
"MCP_TOOL_TIMEOUT >= 930000 (ms) for a long wait to actually be held open -- ClaudeDo's own " +
"launchers already set this. Result: { changed: [{ taskId, status }], timedOut }.")]
public async Task<WaitForTaskChangeResult> WaitForTaskChange(
string[] taskIds, int timeoutSeconds = 60, CancellationToken cancellationToken = default)
{