Skip to content

Reuse existing local broker on startup#12

Merged
khaliqgant merged 1 commit into
mainfrom
codex/reuse-existing-broker
May 21, 2026
Merged

Reuse existing local broker on startup#12
khaliqgant merged 1 commit into
mainfrom
codex/reuse-existing-broker

Conversation

@khaliqgant

Copy link
Copy Markdown
Member

Summary

  • Reuse a valid persisted .agent-relay/connection.json broker before spawning a new local broker.
  • Coalesce concurrent BrokerManager.start() calls for the same project so startup does not race into duplicate broker spawns.
  • Preserve channel sync and broker-initialized event publishing for reused local brokers.

Verification

  • git diff --check passed.
  • npm run build blocked on current origin/main: Rollup cannot resolve @agentworkforce/deploy from src/main/proactive-agent.bundle.ts.
  • npm run typecheck is not defined on current origin/main; fallback npx tsc -b --noEmit is blocked by existing unrelated cloud/proactive/store/diff/graph/type errors.

Runtime note

Before opening the PR, this change was verified in the active local Pear worktree by launching with an existing broker present. Startup logged Reusing existing broker .../.agent-relay/connection.json and stopped emitting the repeated another broker instance is already running failure.

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Rate limit exceeded

@khaliqgant has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 16 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 576160a3-5ecb-4ad2-b7c8-225ba49f331a

📥 Commits

Reviewing files that changed from the base of the PR and between 61a73db and 4c83710.

📒 Files selected for processing (1)
  • src/main/broker.ts
📝 Walkthrough

Walkthrough

BrokerManager.start(...) now deduplicates concurrent startup calls per project using an in-flight promise map. Concurrent invocations await the existing promise, reuse and update the session, and return early rather than spawning duplicate broker instances. Startup errors are reported to the renderer before rethrowing, and connectExistingBroker attempts SDK session reuse from .agent-relay/connection.json.

Changes

Concurrent Broker Startup Deduplication with Error Reporting

Layer / File(s) Summary
Deduplication infrastructure, entry-point guard, and lifecycle management
src/main/broker.ts
New startPromises map tracks per-project in-flight startup operations. Concurrent start(...) calls check for and await existing promises, updating the session window/cwd/name and returning early. Startup errors are forwarded to the renderer status channel, and promise entries are cleaned up in finally to prevent stale state. connectExistingBroker attempts to reuse SDK-connected sessions from connection.json before falling back to normal spawn.

🎯 2 (Simple) | ⏱️ ~12 minutes

🐰 A broker that once raced now waits,
No more duplicate starts in the vats!
Errors flow gently to windows bright,
Concurrent calls handled just right,
Promise deduplication takes flight! 🚀


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

Comment @coderabbitai help to get the list of available commands and usage tips.

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

Copy link
Copy Markdown

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 3 additional findings in Devin Review.

Open in Devin Review

Comment thread src/main/broker.ts Outdated
Comment on lines +538 to +549
if (inFlight) {
await inFlight
const started = this.sessions.get(normalizedProjectId)
if (!started) {
throw new Error(`Broker start completed without a session for project ${normalizedProjectId}`)
}
started.window = win
started.cwd = cwd
started.name = name
await this.syncChannels(normalizedProjectId, nextChannels)
this.sendStatus(normalizedProjectId, 'connected')
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Concurrent start caller's window never receives error status when in-flight start fails

When a second start() call piggybacks on an in-flight start via await inFlight at line 539, and the in-flight promise rejects, the rejection propagates out of start() without calling sendStatusToWindow(win, ...) for the second caller's BrowserWindow. The primary caller's catch block at src/main/broker.ts:623-626 sends the error status to its own window, but the second caller's window is never notified. This leaves the second caller's renderer in a stale "starting" state since it never receives the broker:status error event.

Suggested change
if (inFlight) {
await inFlight
const started = this.sessions.get(normalizedProjectId)
if (!started) {
throw new Error(`Broker start completed without a session for project ${normalizedProjectId}`)
}
started.window = win
started.cwd = cwd
started.name = name
await this.syncChannels(normalizedProjectId, nextChannels)
this.sendStatus(normalizedProjectId, 'connected')
return
if (inFlight) {
try {
await inFlight
} catch (err) {
this.sendStatusToWindow(win, normalizedProjectId, 'error', String(err))
throw err
}
const started = this.sessions.get(normalizedProjectId)
if (!started) {
throw new Error(`Broker start completed without a session for project ${normalizedProjectId}`)
}
started.window = win
started.cwd = cwd
started.name = name
await this.syncChannels(normalizedProjectId, nextChannels)
this.sendStatus(normalizedProjectId, 'connected')
return
Open in Devin Review

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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 4c83710. The in-flight startup branch now wraps the awaited shared promise and follow-up session sync in a try/catch, sends broker:status error to the piggybacking caller window, then rethrows.

@khaliqgant khaliqgant force-pushed the codex/reuse-existing-broker branch from 61a73db to 4c83710 Compare May 21, 2026 15:17
@khaliqgant khaliqgant merged commit e74c264 into main May 21, 2026
1 check passed
miyaontherelay added a commit that referenced this pull request Jun 8, 2026
Fix #9: track last-sent rows/cols and skip duplicate resizePty IPC. The
ResizeObserver fires on every dragged pixel; the cell grid only changes
at discrete steps.

Fix #12: refuse a second concurrent mount of the same runtime into a
different container. Currently chunkAgents doesn't trigger this, but
silently reparenting would tear xterm out from under the original owner.

Fix #15: post-font-settle metrics may differ from the pre-settle ones
the predictor was built with. Call predictiveEcho.onResize after the
refit so column wraps line up with the real grid.

Also adds refreshOnShow() and setInputSrttGetter() to the runtime
interface in preparation for use-terminal wiring.
miyaontherelay added a commit that referenced this pull request Jun 8, 2026
…+ #14)

Replace Fix #12's second-mount refusal guard and Fix #14's lastMountedContainer detach guard with a single token ownership model. Each mount receives a symbol token, stale detach calls no-op, and the latest mount can silently reparent the terminal host during React cross-tree handoff.
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