feat(cloud-agents): pear-side cloud-agent feature (manager + UI scaffolding)#8
Conversation
|
Warning Rate limit exceeded
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 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (8)
Note 🎁 Summarized by CodeRabbit FreeYour 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 |
| function isAuthRequiredError(error: unknown): boolean { | ||
| const code = getErrorCode(error) | ||
| if (code === 'AUTH_REQUIRED') return true | ||
| return /auth|required|sign in|login/i.test(getErrorMessage(error)) |
There was a problem hiding this comment.
🟡 Overly broad regex in isAuthRequiredError causes false-positive auth redirects
The regex /auth|required|sign in|login/i at CloudAgentPicker.tsx:34 matches any error message containing the word "required" or "auth", not just actual authentication errors. This means API errors like "subscription required", "field X is required", "authorization scope insufficient", or even the main process validation errors ("Cloud agent name is required" from src/main/cloud-agent.ts:143) would be misidentified as auth failures. When triggered, the user is redirected to the sign-in flow (onAuthRequired?.()) instead of seeing the actual error message, causing confusing UX.
| return /auth|required|sign in|login/i.test(getErrorMessage(error)) | |
| return /cloud-auth-required|sign in|login required/i.test(getErrorMessage(error)) | |
Was this helpful? React with 👍 or 👎 to provide feedback.
| const workspaceId = await this.requireAccountWorkspaceId() | ||
| const setup = new RelayfileSetup({ | ||
| cloudApiUrl: auth.apiUrl, | ||
| accessToken: () => auth.accessToken |
There was a problem hiding this comment.
🔴 Stale access token closure in startMount defeats SDK's token refresh mechanism
In startMount, the accessToken parameter is passed as a function () => auth.accessToken, but auth is a local variable captured from a single requireCloudAuth() call. The RelayfileSetup SDK accepts a function (not a plain string) precisely so it can call it repeatedly to obtain fresh tokens during the mount's lifetime. This implementation always returns the same token value. For long-running mounts (e.g., hours without a conflict policy change that would trigger startMount again), the token will expire and the mount will fail to authenticate its requests silently.
Prompt for agents
The issue is in startMount at line 734 in src/main/cloud-agent.ts. The accessToken parameter passed to RelayfileSetup captures a single resolved token value in a closure. Since the SDK accepts a function callback (rather than a plain string), it likely calls this function repeatedly to get fresh tokens during the mount lifecycle.
The fix should make the accessToken callback resolve a fresh token each time it's called. One approach: change the callback to call resolveCloudAuth() (or a similar method) each time. However, you need to check whether the SDK's accessToken callback supports async functions or only sync. If sync-only, you might need to maintain a cached token that gets refreshed periodically in the background. If async is supported, the fix could be:
accessToken: async () => {
const freshAuth = await resolveCloudAuth()
return freshAuth?.accessToken ?? auth.accessToken
}
The relevant function is requireCloudAuth (line 633) which calls resolveCloudAuth from src/main/auth.ts.
Was this helpful? React with 👍 or 👎 to provide feedback.
| return () => { | ||
| cancelled = true | ||
| } | ||
| }, [onAuthRequired]) |
There was a problem hiding this comment.
🟡 Inline onAuthRequired arrow function causes useEffect to re-run on every parent re-render
In CloudAgentPicker.tsx:139, the useEffect depends on [onAuthRequired]. In the parent CloudAgentDialog.tsx:100, this prop is passed as an inline arrow function () => setState('auth-required'), which creates a new function reference on every render of CloudAgentDialog. Whenever CloudAgentDialog re-renders (e.g., due to store changes from useUIStore or useProjectStore), the effect re-runs, causing the agent list to be re-fetched from the API unnecessarily.
Prompt for agents
The problem is that the useEffect in CloudAgentPicker (line 106-139) has [onAuthRequired] as a dependency, but onAuthRequired is passed as an inline arrow function from CloudAgentDialog.tsx:100 which creates a new reference on each render.
Two approaches to fix:
1. In CloudAgentDialog.tsx, wrap the onAuthRequired callback in useCallback: const handleAuthRequired = useCallback(() => setState('auth-required'), [])
2. In CloudAgentPicker.tsx, use a ref to track onAuthRequired and remove it from the useEffect dependency array, so the effect only runs on mount. The ref approach: store onAuthRequired in a useRef and update it on each render, then call the ref.current in the effect's async function.
Was this helpful? React with 👍 or 👎 to provide feedback.
…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>
…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>
e8081e4 to
66bdd6f
Compare
Spec 03 remaining work — the unentangled portion. Adds the standalone artifacts; UI mounting (App.tsx/ui-store.ts/ProjectSettings.tsx) is deliberately left for a follow-up PR because those files have edits from other specs mixed in on the working branch. - scripts/sync-runtime-types.mjs: writes vendor/agentworkforce-runtime-types.d.ts from @agentworkforce/persona-kit at build time; wired via prebuild script. - vendor/agentworkforce-runtime-types.d.ts: vendored persona/runtime types that the editor type-checks the user's handler source against. - package.json: + @monaco-editor/react ^4.7.0, + prebuild + sync:runtime-types scripts. - test/proactive-agent.smoke.ts: vitest smoke that mocks @agentworkforce/deploy + cloud HTTP and walks create → deploy → simulated change event → run row succeeded, per spec acceptance gate #8. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per the v1 consolidation discussion on AgentWorkforce/cloud#912 → AgentWorkforce/cloud#919 (draft), the parallel cloud proactive-runtime modules the smoke test imported (proactive-trigger-router, proactive-runner) have been dropped in favour of reusing the existing agents + deployments + integration-watch-dispatcher pipeline. The smoke test was tightly coupled to those modules (relative imports from ../../cloud/packages/core/src/runtime/*) and to the /proactive-personas/* route surface that no longer exists. Retargeting it to the v1 surface would require a different test harness (the v1 dispatcher imports from cloud's web package, which pulls in Daytona + relayauth infrastructure not easily smoke-testable from pear). Pear's part of spec-03 acceptance gate #8 (deploy → trigger → succeeded) is more honestly covered by an end-to-end test in workforce (packages/deploy/src/modes/cloud.test.ts now asserts proactive personas flow through /deployments correctly) plus cloud's existing integration-watch-dispatcher tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fix #8: previously listeners received the full (post-trim) buffer and sliced from their captured writtenChunks. At the 10k cap the trim shifts the window, so writtenChunks > buffer.length and the slice drops the freshly-added chunks. Listeners now receive only the newly-queued chunks ("tail"). The terminal runtime does the snapshot-aware initial replay manually against getPtyChunks before subscribing, and AgentNode re-pulls the canonical buffer on each notification so its preview honours the trim cap.
Spec 01 scaffolding. Builds on #7 (shared scaffold); review after #7 lands.
src/main/cloud-agent.ts+ types:CloudAgentManager(attach/detach/restore/list/create/delete, warm-box polling, relayfile mount, 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.Remaining gaps (restore-on-launch wiring + relayfile
--conflict-policyCLI flag) tracked in slim specspecs/01-cloud-agents.mdfrom #5. The cloud-side box endpoints are remediation spec 04 (cloud worktree).🤖 Generated with Claude Code