Skip to content

feat: daemon-based spawning for spawn CLI command#361

Merged
khaliqgant merged 4 commits into
mainfrom
spawn-separation
Feb 2, 2026
Merged

feat: daemon-based spawning for spawn CLI command#361
khaliqgant merged 4 commits into
mainfrom
spawn-separation

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

@khaliqgant khaliqgant commented Feb 2, 2026

The spawn command now communicates directly with the daemon via socket protocol, removing the requirement for the dashboard to be running. Falls back to HTTP API (dashboard) if daemon socket is unavailable.

This simplifies agent orchestration - just agent-relay up is sufficient for spawn/release operations.


Open with Devin

The `spawn` command now communicates directly with the daemon via
socket protocol, removing the requirement for the dashboard to be
running. Falls back to HTTP API (dashboard) if daemon socket is
unavailable.

This simplifies agent orchestration - just `agent-relay up` is
sufficient for spawn/release operations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When socket is in /tmp, use /tmp/agent-relay-state/ subdirectory
for teamDir. Fixes atomic write failures on macOS where temp file
cleanup can remove files between write and rename operations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View issue and 4 additional flags in Devin Review.

Open in Devin Review

Comment thread src/cli/index.ts
Shadow options (shadowMode, shadowAgent, shadowTriggers) were silently
dropped when spawning via the daemon socket path. The HTTP API fallback
passed all fields via JSON.stringify, but the SDK path used explicit
field listing that missed these three fields.

Thread shadowMode, shadowAgent, and shadowTriggers through:
- Protocol SpawnPayload type
- SDK client.spawn() options and envelope payload
- Daemon SpawnManager.handleSpawn() pass-through
- CLI client.spawn() call

Add SDK test verifying shadow options appear in sent envelope.
Add sdk-daemon-parity Claude rule to prevent future field omissions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View issues and 7 additional flags in Devin Review.

Open in Devin Review

Comment thread src/cli/index.ts Outdated
Comment thread src/cli/index.ts Outdated
Move daemonSpawnSucceeded and daemonReleaseSucceeded flags to before
the disconnect() call. This ensures that if disconnect() throws an
error, successful spawns/releases are still correctly reported rather
than being treated as failures.

Addresses Devin review comments on PR #361.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@khaliqgant khaliqgant merged commit 0cddabc into main Feb 2, 2026
7 of 29 checks passed
@khaliqgant khaliqgant deleted the spawn-separation branch February 2, 2026 19:34
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View issue and 8 additional flags in Devin Review.

Open in Devin Review

Comment thread src/cli/index.ts
Comment on lines +2447 to 2456
} catch (daemonErr: any) {
// If daemon connection failed, try HTTP API as fallback
if (daemonErr.message?.includes('ENOENT') || daemonErr.message?.includes('ECONNREFUSED') || daemonErr.code === 'ENOENT') {
// Socket doesn't exist or daemon not running - try HTTP API
} else if (!daemonSpawnSucceeded) {
// Other error during spawn - report it
console.error(`Failed to spawn ${name}: ${daemonErr.message}`);
process.exit(1);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Duplicate spawn/release attempts after disconnect error

When the daemon spawn/release operation succeeds but client.disconnect() throws an error, the code falls through to the HTTP API fallback and attempts the operation again.

Click to expand

How the bug is triggered

  1. await client.spawn() succeeds at src/cli/index.ts:2418-2430
  2. daemonSpawnSucceeded = true is set at line 2433
  3. await client.disconnect() throws an error at line 2434
  4. The catch block is entered at line 2447
  5. The error message doesn't include 'ENOENT' or 'ECONNREFUSED' (it's a disconnect error)
  6. !daemonSpawnSucceeded is false, so the exit at line 2454 is skipped
  7. Code falls through to HTTP API fallback at line 2458
  8. HTTP API tries to spawn the same agent again!

Actual vs Expected

Actual: If disconnect fails after a successful spawn, the code will attempt to spawn the agent again via HTTP API, potentially causing duplicate agents or errors.

Expected: If the spawn operation succeeded, the command should exit successfully regardless of disconnect errors.

Impact

This can cause:

  • Duplicate spawn attempts for the same agent name
  • Confusing error messages if the second spawn fails because the agent already exists
  • Resource waste from unnecessary HTTP API calls

The same bug exists in the release command at lines 2518-2538.

Recommendation: Add process.exit(0) after the catch block when daemonSpawnSucceeded is true, or restructure the error handling to exit early when the operation succeeded. For example:

} catch (daemonErr: any) {
  if (daemonSpawnSucceeded) {
    // Spawn succeeded, disconnect error is non-fatal
    process.exit(0);
  }
  // ... rest of error handling
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant