Skip to content

wave: shared scaffolding for cloud-agents / integrations / proactive-agents#7

Merged
khaliqgant merged 3 commits into
mainfrom
wave/pear-shared-scaffold
May 21, 2026
Merged

wave: shared scaffolding for cloud-agents / integrations / proactive-agents#7
khaliqgant merged 3 commits into
mainfrom
wave/pear-shared-scaffold

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

Foundational IPC + preload + store + lib changes shared by every per-spec feature branch in the cloud-agents wave. Per-spec feature branches (ricky/wave-pear-cloud-agents/{01,02,03,05}) stack on top of this PR.

Key piece: src/main/auth.ts adds resolveCloudAuth() + CloudAuth + accountKey, the unified credential resolver used by cloud-agent, proactive-agent, and integrations — mirrors how ../workforce and ../cloud read CLOUD_API_* env / ~/.agent-relay/cloud-auth.json.

🤖 Generated with Claude Code

…agents

Foundational changes that every feature branch in the cloud-agents wave shares —
IPC handlers, preload namespaces, store schema extensions, App.tsx dialog
wiring, lib/ipc types, broker / index / auth helpers, and the renderer plumbing
the per-spec features all depend on.

Notable:
- src/main/auth.ts: add resolveCloudAuth() + CloudAuth + accountKey, the
  unified credential resolver used by cloud-agent, proactive-agent, and
  integrations (mirrors how ../workforce + ../cloud read CLOUD_API_*).
- src/main/ipc-handlers.ts / src/preload/index.ts: wire cloud-agent:*,
  integrations:*, proactive-agent:* channels and the corresponding renderer-side
  namespaces.
- src/main/store.ts: extend Project schema with cloudAgent, integrations[],
  proactiveAgents[].
- src/renderer/src/App.tsx + lib/ipc.ts + stores: glue for the per-spec UIs.

Per-spec feature work lands on top of this in dedicated branches:
ricky/wave-pear-cloud-agents/{01-cloud-agents,02-integrations,03-proactive-agents,
05-integrations-connect}.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: c2911399-1adb-43f8-8467-f1abe81b6c9c

📥 Commits

Reviewing files that changed from the base of the PR and between 14b7db5 and 9f70672.

📒 Files selected for processing (16)
  • src/main/auth.ts
  • src/main/broker.ts
  • src/main/filesystem.ts
  • src/main/index.ts
  • src/main/ipc-handlers.ts
  • src/main/store.ts
  • src/preload/index.ts
  • src/renderer/src/App.tsx
  • src/renderer/src/components/common/AppTopBar.tsx
  • src/renderer/src/components/common/CommandMenu.tsx
  • src/renderer/src/lib/diff-selection.ts
  • src/renderer/src/lib/ipc.ts
  • src/renderer/src/stores/agent-store.ts
  • src/renderer/src/stores/ui-store.ts
  • tsconfig.web.json
  • vitest.config.mjs

📝 Walkthrough

Walkthrough

This PR integrates cloud agent and proactive agent management into Pear by adding cloud authentication, sandbox lifecycle handling in the broker, workspace persistence, expanded IPC surface for agent operations, and new UI views for account settings with unified agent spawning dialogs.

Changes

Cloud Agent & Proactive Agent Integration

Layer / File(s) Summary
Token Expiry & Cloud Auth Resolution
src/main/auth.ts
Adds TOKEN_EXPIRY_BUFFER_MS constant, exports isTokenExpired() to check token expiry with buffer, readStoredAuth() to load encrypted tokens, and resolveCloudAuth() to resolve Pear or SDK credentials; implements ensureAuthenticated(apiUrl?) to return valid credentials, update metadata when logged in, or start login flow when needed.
Cloud Sandbox Attachment & Event Broadcasting
src/main/broker.ts
Exports CloudAgentSandboxHandle interface; adds onBrokerEvent() to register event observers; implements attachCloudSandbox() to validate inputs, create AgentRelayClient, await session, attach client, stream events, and fan them to observers; implements detachCloudSandbox() to disconnect; refactors connectCloud() to delegate SDK setup to attachCloudSandbox(); fixes shutdown iteration to use snapshots.
Relay Workspace Store & Application Shutdown
src/main/store.ts, src/main/index.ts
Adds ProjectCloudAgent, RelayWorkspace, RelayWorkspaceRecord types and relay workspace persistence helpers (getRelayWorkspace, setRelayWorkspace, clearRelayWorkspace, etc.); updates app shutdown to invoke cloudAgentManager.shutdownAll() alongside broker shutdown.
IPC Handler Wiring for Cloud & Proactive Agents
src/main/ipc-handlers.ts
Updates auth:login to call ensureAuthenticated() with optional apiUrl and reset relay workspace when authenticated; adds auth:logout workspace reset; registers proactive agent event listener to broadcast events to all renderer windows; introduces handlers for cloud agents (list/create/delete/attach/detach/status), proactive agents (list/create/update/deploy/pause/resume/undeploy/runs/transcript), and integrations (catalog/list/connect/scope/disconnect).
Preload & Client-side IPC API Contracts
src/preload/index.ts, src/renderer/src/lib/ipc.ts
Extends ViewMode to include 'account-settings'; updates auth.login() to accept optional { apiUrl? } input; adds comprehensive type definitions for CloudAgentRecord, CloudAgentStatus, ProactiveAgentDraft, ProactiveAgentRun, IntegrationAdapter, and event unions; extends PearAPI with cloudAgent, proactiveAgent, and integrations namespaces including list/create/update/deploy/attach/detach operations and event subscriptions.
UI Support for Account Settings & Agent Spawning
src/renderer/src/App.tsx, src/renderer/src/stores/ui-store.ts, src/renderer/src/components/common/CommandMenu.tsx
Adds account-settings tab view in App routing to AccountSettings; switches sidebar from ProjectSidebar to unified Sidebar; refactors agent spawning dialogs: spawn-agent now shows AddAgentDialog with onSelectLocal and onSelectCloud callbacks that open spawn-local-agent and cloud-agent dialog types; extends ui-store ViewMode and DialogType with account-settings/spawn-local-agent/cloud-agent support; updates CommandMenu to distinguish local vs cloud agents.
Minor Refactoring & Type Improvements
src/renderer/src/components/common/AppTopBar.tsx, src/renderer/src/lib/diff-selection.ts, src/main/filesystem.ts, src/renderer/src/stores/agent-store.ts
Adds fallback empty-string return in tabSubtitle(); refactors buildHunkPatch() to use firstDelete/firstInsert temporaries for line-number computation; updates listDirectory() callback typing to explicitly return ExplorerEntry; fixes agent-store.ts timer type from ReturnType<typeof setTimeout> to number; extracts broker event fields to local variables for clearer message handling.
TypeScript & Test Configuration
tsconfig.web.json, vitest.config.mjs
Explicitly targets ES2020 with DOM/ES2020 libraries in tsconfig; configures Vitest to discover test files under src/main/**/*.test.ts and packages/**/*.test.ts, excluding node_modules/dist/out.

🎯 4 (Complex) | ⏱️ ~60 minutes


🐰 A cloud of agents now bloom,
With tokens checked and workspaces made,
Sandbox sessions attach with zoom,
While dialogs spawn in the masquerade.


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.

Copy link
Copy Markdown

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

Open in Devin Review

Comment thread src/main/broker.ts
Comment on lines 589 to 591
} catch (err) {
console.error(`[broker] Failed to connect cloud broker for project ${normalizedProjectId}:`, err)
this.sendStatusToWindow(win, normalizedProjectId, 'error', String(err))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 connectCloud wraps attachCloudSandbox causing duplicate error logging and IPC status messages

When connectCloud delegates to attachCloudSandbox and the inner method fails (e.g., client.getSession() at src/main/broker.ts:520 throws), both methods catch the error and independently call console.error and this.sendStatusToWindow. The error is logged twice and two identical broker:status IPC messages with status: 'error' are sent to the renderer window. While the agent store's handleBrokerStatus deduplicates error recording, the duplicate IPC round-trip and console output are wasteful. The outer connectCloud catch at line 589-593 is redundant for errors originating inside attachCloudSandbox.

(Refers to lines 589-593)

Prompt for agents
In connectCloud (src/main/broker.ts around line 549), the catch block at lines 589-593 duplicates the error handling already performed by attachCloudSandbox's own catch block (lines 537-541). When attachCloudSandbox throws, both methods log the error with console.error and call this.sendStatusToWindow with the same arguments, sending two broker:status IPC messages to the renderer.

The fix is to remove the redundant sendStatusToWindow and console.error from connectCloud's catch, since attachCloudSandbox already handles those. connectCloud's catch should still re-throw the error so the IPC handler can report it. One approach is to just re-throw without the duplicate side effects, or to distinguish between errors from sandbox creation API calls (which need local handling) and errors from attachCloudSandbox (which are already handled).
Open in Devin Review

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

khaliqgant and others added 2 commits May 21, 2026 15:49
Split connectCloud's try/catch so it only catches errors from sandbox
provisioning (POST /sandboxes, /terminal). attachCloudSandbox already
console.error's and emits broker:status error for its own failures, so
wrapping it in the outer catch caused duplicate console output and two
identical broker:status IPC messages reaching the renderer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…fold

# Conflicts:
#	package-lock.json
#	package.json
#	src/main/auth.ts
#	src/main/broker.ts
#	src/main/index.ts
#	src/main/ipc-handlers.ts
#	src/main/store.ts
#	src/preload/index.ts
#	src/renderer/src/App.tsx
#	src/renderer/src/components/broker/BrokerDetailsPage.tsx
#	src/renderer/src/components/diff/DiffPane.tsx
#	src/renderer/src/lib/ipc.ts
#	src/renderer/src/lib/syntax-highlighter.tsx
#	src/renderer/src/stores/ui-store.ts
@khaliqgant khaliqgant merged commit d66ef8b into main May 21, 2026
1 of 2 checks passed
khaliqgant added a commit that referenced this pull request May 21, 2026
WIP scaffolding for Spec 05's account-wide RelayWorkspaceManager — the single
account-level relayfile workspace pear uses for the relayfile mount + integrations
connect. Replaces the random per-project crypto.randomUUID() relayWorkspaceId in
store.ts.

- src/main/relay-workspace.ts: provisions/joins the account workspace via
  RelayfileSetup, caches the WorkspaceHandle.
- src/main/relay-workspace.types.ts: persisted shape.
- src/main/relay-workspace.test.ts: unit smoke.

Remaining work (full Spec 05 implementation): wire the integrations connect
flow to use handle.requestJson against connect-session / integrations/{provider}/status /
integrations/{provider} (bypassing the SDK's whitelist-gated connectIntegration),
replace project.relayWorkspaceId usages with RelayWorkspaceManager.getWorkspaceId(),
add the per-project visibility section. See specs/05-integrations-connect.md.

Builds on shared scaffolding (#7).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
khaliqgant added a commit that referenced this pull request May 21, 2026
…olding)

Spec 01 scaffolding for cloud agents in pear:
- src/main/cloud-agent.ts + cloud-agent.types.ts: CloudAgentManager
  (attach/detach/restore/list/create/delete, warm box polling, mount session
  via @relayfile/sdk, conflict-policy launcher, git sync-back).
- components/agents/: AddAgentDialog (local vs cloud picker),
  CloudAgentDialog, CloudAgentPicker, CloudAuthRequired.
- hooks/use-cloud-agent.ts + stores/cloud-agent-store.ts: renderer-side
  binding + catalog state.

Builds on the shared scaffolding (#7). The remaining gaps —
restore-on-launch in index.ts and the relayfile --conflict-policy CLI flag — are
covered in the slimmed spec at specs/01-cloud-agents.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
khaliqgant added a commit that referenced this pull request May 21, 2026
… + Nango logos)

Spec 02 scaffolding plus the catalog/logo work done this session:
- src/main/integrations.ts: IntegrationsManager. listCatalog filters to
  ACTIVE_PROVIDERS (the non-deprecated relayfile providers in
  cloud/packages/web/lib/integrations/providers.ts). authHeaders is async and
  routes through resolveCloudAuth. loadStaticCatalog is a static import (not a
  dynamic variable import, which the bundler couldn't resolve and which left
  the static fallback empty at runtime).
- src/main/integrations.types.ts: IntegrationAdapter + visibility types.
- src/main/integrations.catalog.ts: 36 generated adapters with iconUrl pointing
  at https://app.nango.dev/images/template-logos/{slug}.svg.
- scripts/build-integrations-catalog.mjs: regenerates the catalog from
  ../relayfile-adapters/packages. Adds NANGO_LOGO_SLUG override map
  (gmail→google-mail, teams→microsoft-teams, x→twitter) and emits iconUrl per
  entry.
- src/renderer/src/components/settings/AccountSettings.tsx: <IntegrationLogo>
  component with onError fallback to the generic plug icon (light tile makes
  the dark/monochrome SVGs legible), used in both the CATALOG grid and the
  CONNECTED list.

Builds on shared scaffolding (#7). Remaining gaps — scope-picker components +
per-project visibility section + ../relayfile-cloud workspace-integrations
route — are tracked in specs/02-integrations.md. The Connect-flow rework is
spec 05.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
khaliqgant added a commit that referenced this pull request May 21, 2026
…UI files)

Spec 03 main-process + renderer scaffolding:
- src/main/proactive-agent.ts + .bundle.ts + .types.ts: ProactiveAgentManager
  (list/create/update/deploy/pause/resume/undeploy + run-transcript), bundle
  stager for handler source, route through resolveCloudAuth.
- components/proactive/: ProactiveAgentsSection, ProactiveAgentCard,
  ProactiveAgentEditor.
- hooks/use-proactive-agent.ts.

Builds on shared scaffolding (#7). NOTE: the UI is not wired yet —
ProactiveAgentsSection is defined but never rendered, the AppTabKind union does
not include 'proactive-agent-editor', App.tsx does not render the editor, and
@monaco-editor/react is not in package.json. Those gaps, plus vendored runtime
types and the cloud proactive-personas backend (routes/schema/runtime), are
tracked in specs/03-proactive-agents.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
khaliqgant added a commit that referenced this pull request May 21, 2026
…11)

* feat(relay-workspace): RelayWorkspaceManager scaffolding for Spec 05

WIP scaffolding for Spec 05's account-wide RelayWorkspaceManager — the single
account-level relayfile workspace pear uses for the relayfile mount + integrations
connect. Replaces the random per-project crypto.randomUUID() relayWorkspaceId in
store.ts.

- src/main/relay-workspace.ts: provisions/joins the account workspace via
  RelayfileSetup, caches the WorkspaceHandle.
- src/main/relay-workspace.types.ts: persisted shape.
- src/main/relay-workspace.test.ts: unit smoke.

Remaining work (full Spec 05 implementation): wire the integrations connect
flow to use handle.requestJson against connect-session / integrations/{provider}/status /
integrations/{provider} (bypassing the SDK's whitelist-gated connectIntegration),
replace project.relayWorkspaceId usages with RelayWorkspaceManager.getWorkspaceId(),
add the per-project visibility section. See specs/05-integrations-connect.md.

Builds on shared scaffolding (#7).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(relay-workspace): redirect stale bootstrap rejections to current handle

createGuardedBootstrap only handled the success path of a superseded
bootstrap (redirecting to getCurrentOrNextHandle inside .then). When a stale
bootstrap rejected — e.g. a network blip on joinWorkspace after auth changed
and a newer bootstrap already succeeded — the error propagated to the
original caller even though a valid handle existed. Add a .catch that
applies the same redirect logic before the .finally cleanup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
khaliqgant added a commit that referenced this pull request May 21, 2026
…olding) (#8)

* feat(cloud-agents): pear-side cloud-agent feature (manager + UI scaffolding)

Spec 01 scaffolding for cloud agents in pear:
- src/main/cloud-agent.ts + cloud-agent.types.ts: CloudAgentManager
  (attach/detach/restore/list/create/delete, warm box polling, mount session
  via @relayfile/sdk, conflict-policy launcher, git sync-back).
- components/agents/: AddAgentDialog (local vs cloud picker),
  CloudAgentDialog, CloudAgentPicker, CloudAuthRequired.
- hooks/use-cloud-agent.ts + stores/cloud-agent-store.ts: renderer-side
  binding + catalog state.

Builds on the shared scaffolding (#7). The remaining gaps —
restore-on-launch in index.ts and the relayfile --conflict-policy CLI flag — are
covered in the slimmed spec at specs/01-cloud-agents.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cloud-agents): tighten auth-required regex, refresh mount token, memoize callbacks

- CloudAgentPicker.isAuthRequiredError: match only explicit auth signals
  (cloud-auth-required / sign in to / login required / not logged in) instead
  of /auth|required|sign in|login/ which fired on innocuous messages like
  'Cloud agent name is required' or 'authorization scope insufficient' and
  misrouted users to the sign-in screen.
- cloud-agent.startMount: pass an async accessToken callback that resolves
  fresh credentials via resolveCloudAuth on each call. The previous closure
  captured a single token value, defeating RelayfileSetup's refresh mechanism
  and breaking long-running mounts when the token expired.
- CloudAgentDialog: wrap onAuthenticated/onAuthRequired in useCallback so the
  picker's useEffect doesn't refire on every parent re-render and re-fetch
  the agent list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
khaliqgant added a commit that referenced this pull request May 21, 2026
… + Nango logos) (#9)

* feat(integrations): pear-side integrations feature (manager + catalog + Nango logos)

Spec 02 scaffolding plus the catalog/logo work done this session:
- src/main/integrations.ts: IntegrationsManager. listCatalog filters to
  ACTIVE_PROVIDERS (the non-deprecated relayfile providers in
  cloud/packages/web/lib/integrations/providers.ts). authHeaders is async and
  routes through resolveCloudAuth. loadStaticCatalog is a static import (not a
  dynamic variable import, which the bundler couldn't resolve and which left
  the static fallback empty at runtime).
- src/main/integrations.types.ts: IntegrationAdapter + visibility types.
- src/main/integrations.catalog.ts: 36 generated adapters with iconUrl pointing
  at https://app.nango.dev/images/template-logos/{slug}.svg.
- scripts/build-integrations-catalog.mjs: regenerates the catalog from
  ../relayfile-adapters/packages. Adds NANGO_LOGO_SLUG override map
  (gmail→google-mail, teams→microsoft-teams, x→twitter) and emits iconUrl per
  entry.
- src/renderer/src/components/settings/AccountSettings.tsx: <IntegrationLogo>
  component with onError fallback to the generic plug icon (light tile makes
  the dark/monochrome SVGs legible), used in both the CATALOG grid and the
  CONNECTED list.

Builds on shared scaffolding (#7). Remaining gaps — scope-picker components +
per-project visibility section + ../relayfile-cloud workspace-integrations
route — are tracked in specs/02-integrations.md. The Connect-flow rework is
spec 05.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(integrations): keep persistIntegration's load→modify→save synchronous

Resolve the async displayNameForProvider lookup BEFORE loadStore() so no
await sits between load and save. With the async gap in place, any concurrent
IPC handler (project:update, project:add-channel, etc.) that wrote to
projects.json during the awaited catalog refresh would have its changes
silently overwritten when we saved the stale data we loaded earlier.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
khaliqgant added a commit that referenced this pull request May 21, 2026
…UI files) (#10)

* feat(proactive-agents): pear-side proactive-agent feature (manager + UI files)

Spec 03 main-process + renderer scaffolding:
- src/main/proactive-agent.ts + .bundle.ts + .types.ts: ProactiveAgentManager
  (list/create/update/deploy/pause/resume/undeploy + run-transcript), bundle
  stager for handler source, route through resolveCloudAuth.
- components/proactive/: ProactiveAgentsSection, ProactiveAgentCard,
  ProactiveAgentEditor.
- hooks/use-proactive-agent.ts.

Builds on shared scaffolding (#7). NOTE: the UI is not wired yet —
ProactiveAgentsSection is defined but never rendered, the AppTabKind union does
not include 'proactive-agent-editor', App.tsx does not render the editor, and
@monaco-editor/react is not in package.json. Those gaps, plus vendored runtime
types and the cloud proactive-personas backend (routes/schema/runtime), are
tracked in specs/03-proactive-agents.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(proactive-agents): map 'cancelled' run-handle status to 'error'

CloudRunHandleStatus includes 'cancelled' but mapDeployStatus had no branch
for it, so a cancelled deployment fell through to the default 'active' and
was reported as a successful one (or produced a contradictory
{ status: 'active', error: '…' } pair).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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