Staging#498
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
…ions Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Fail fast when the extension is loaded without esbuild env injection instead of falling back to REPLACE_* placeholders. Co-authored-by: Cursor <cursoragent@cursor.com>
Align security rules with the Phase 2 approval flow where mobile writes approvalToken alongside status and approvedAt. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Validate scheduler actions with shared DSL schemas, verify approval tokens before resuming, and harden test cleanup and error propagation. Co-authored-by: Cursor <cursoragent@cursor.com>
feat(bridge): Phase 3 proactive scheduler and async Expo Push completion
…n bridge
Full pipeline validated: Cloud Scheduler → Firestore session → extension polls
and connects → task executes → result written to Firestore → scheduler returns 200.
Key fixes:
- offscreen/auth.ts: await onAuthStateChanged before serving GET_ID_TOKEN to
avoid race where auth.currentUser is null on fresh offscreen doc creation
- offscreen/auth.ts: use firebase/auth/web-extension (IndexedDB) in both panel
and offscreen doc to prevent persistence migration that deleted IndexedDB entry
- content-bridge.ts: switch from executeScript({func}) to files+sendMessage
pattern; func serializes only the function body losing module-scope deps
- content/bridge-listener.ts: new IIFE content script entry exposing CLANKER_RUN_ACTION
message handler with idempotency guard (window.__clankerInjected)
- esbuild.mjs: build content/executor.js as IIFE (not ESM) so Chrome can inject
it into the page isolated world via executeScript({files})
- manifest.json: add tabs permission (needed for tab.url); move <all_urls> to
host_permissions (optional_host_permissions requires user gesture to grant)
- panel.ts: remove tryUpgradeToGcm; register immediately with polling: token
- service-worker.ts: alarm-based polling every 1 min as primary wake mechanism
- schedulerTriggerHandler.ts: FCM wake non-fatal; skip entirely for polling: tokens
- firestoreSession.ts: add getPendingSchedulerSession for pending-session endpoint
- index.ts: add GET /agent/browser/pending-session route; schedulerTimeoutMs 90s
- firestore.indexes.json: add sessions(status ASC, createdAt ASC) composite index
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
runInActiveTab now injects executor.js then sends CLANKER_RUN_ACTION via chrome.tabs.sendMessage; mock that API in tests and chrome-stub. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Return only sessionId from the API, skip expired/taskless pending sessions, dedupe concurrent wake-ups, and harden extension sign-in UX. Co-authored-by: Cursor <cursoragent@cursor.com>
feat(bridge): Phase 4 — MV3 Chrome extension bridge E2E validated
Restore CWS-friendly optional_host_permissions, register GCM on install, and remove the unused alarm/pending-session fallback. Add browser-bridge guide, update C4 phasing and cross-refs, and delete the dead GET route. Co-authored-by: Cursor <cursoragent@cursor.com>
fix(bridge): align extension manifest, docs, and remove pending-session poll
Covers 5 targeted changes (innerHTML XSS fix, explicit CSP, manifest key, offscreen justification, icons field), copy-paste store submission artifacts, and MV3 policy rationale for each permission. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…se label Add missing tabs to manifest snippet (used by chrome.tabs.query/create/update in content-bridge.ts). Remove stale (Phase 2+) label from Expo Push row in Infrastructure Prerequisites — approval flow shipped in bridge Phase 2 commit (feat(bridge/p2)) and is correctly scoped as Phase 1 in the phasing table. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add dedicated section heading (required for CWS manual reviewer to find it), Limited Use Disclosure clause (CWS hard requirement), and explicit no-data-sale statement. Previous draft was missing the Limited Use language. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds CWS-required browser extension section including Limited Use Disclosure clause (Chrome Web Store User Data Policy hard requirement). Placed after Cookies section to group platform-specific data handling disclosures. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- auth-bridge.ts line ref 6 → 8 (justification is on line 8, not 6) - Store description: (iOS) → (iOS and Android) — app.config.ts has both platforms - §1.1a: add action.default_icon 128 alongside top-level icons field Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add manifest icons and CSP, safe DOM renderLog, offscreen justification, CWS submission artifacts doc, and mark the readiness spec implemented. Co-authored-by: Cursor <cursoragent@cursor.com>
Align offscreen reason with justification, test production log renderer, sync privacy copy with CWS artifacts, and fix doc lint/grep portability. Co-authored-by: Cursor <cursoragent@cursor.com>
feat(extension): CWS submission readiness
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 7 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughAdds a Cloud Scheduler-triggered browser-task endpoint with secret auth, credit handling, Firestore task coordination, and Expo Push completion. Also updates the extension for MV3 submission readiness, including manifest, content-bridge, auth, side-panel, and privacy/documentation changes. ChangesCloud Agent — Phase 3 Proactive Scheduler
Extension CWS Readiness
Sequence Diagram(s)sequenceDiagram
participant CloudScheduler
participant indexts as index.ts
participant SchedulerHandler as schedulerTriggerHandler
participant Firestore
participant FcmDispatcher
participant ExpoPush
CloudScheduler->>indexts: POST /agent/browser/scheduler-trigger (Bearer secret)
indexts->>SchedulerHandler: authorize and create handler
SchedulerHandler->>SchedulerHandler: validate request, block host, check approval
SchedulerHandler->>SchedulerHandler: spendCredit
SchedulerHandler->>Firestore: create session and task intent
SchedulerHandler->>FcmDispatcher: wake browser bridge
SchedulerHandler->>Firestore: wait for terminal task
SchedulerHandler->>ExpoPush: sendProactive on completion
SchedulerHandler->>indexts: 200 or 504 JSON response
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
Comment |
Move scheduler secret verification into middleware mounted after the rate limiter so CodeQL no longer flags missing rate limiting on the handler and its unit-test route setup. Co-authored-by: Cursor <cursoragent@cursor.com>
/fix-pr follow-upCommit: Review resolution
Verification
|
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (2)
extension/src/background/service-worker.ts (1)
36-38: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSilent failure on GCM registration error.
The combined guard
if (chrome.runtime.lastError || !registrationToken) returnnow drops the error without any logging (the prior implementation loggedlastError). A failed registration will leave the device silently unregistered with no diagnostic signal. Consider loggingchrome.runtime.lastErrorbefore returning.🔧 Suggested logging
- chrome.gcm.register([FIREBASE_SENDER_ID], (registrationToken) => { - if (chrome.runtime.lastError || !registrationToken) return - void chrome.storage.local.set({ gcmToken: registrationToken }).then(() => registerDevice(registrationToken)) + chrome.gcm.register([FIREBASE_SENDER_ID], (registrationToken) => { + if (chrome.runtime.lastError || !registrationToken) { + console.error('GCM register failed', chrome.runtime.lastError?.message) + return + } + void chrome.storage.local.set({ gcmToken: registrationToken }).then(() => registerDevice(registrationToken))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@extension/src/background/service-worker.ts` around lines 36 - 38, The GCM registration path in service-worker.ts is swallowing failures because the callback in chrome.gcm.register returns early when chrome.runtime.lastError is set without recording it. Update the chrome.gcm.register callback to log chrome.runtime.lastError before the return, then keep the existing registrationToken handling and registerDevice flow so failures are visible for debugging.extension/src/ui/side-panel/panel.ts (1)
23-23: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider clearing
gcmTokenon sign-out too.Sign-out removes
deviceIdbut leaves the cachedgcmTokeninchrome.storage.local. After signing in as a different account,registerThisDevice()mints a freshdeviceIdbut reuses the stalegcmToken. Likely harmless since the token is device-bound, but clearing both keeps storage consistent.♻️ Optional cleanup
-$('signout').addEventListener('click', () => { void signOut(auth); void chrome.storage.local.remove('deviceId') }) +$('signout').addEventListener('click', () => { void signOut(auth); void chrome.storage.local.remove(['deviceId', 'gcmToken']) })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@extension/src/ui/side-panel/panel.ts` at line 23, Signing out in the side panel currently clears only `deviceId` and leaves `gcmToken` behind, so update the click handler in `panel.ts` to remove both keys from `chrome.storage.local` when `signOut(auth)` runs. Use the existing `signOut` listener on `$('signout')` as the entry point and extend the storage cleanup alongside the current `deviceId` removal.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cloud-agent/src/handlers/schedulerTriggerHandler.ts`:
- Around line 27-32: The scheduler trigger flow currently accepts any valid
request and can create new IDs and spend credit repeatedly, so add an
idempotency key to schedulerBodySchema and validate it in
schedulerTriggerHandler before any credit spend or task creation. Reserve/check
the scheduler run key transactionally in the same path that handles the request,
then short-circuit duplicate retries so the later task-enqueue and credit logic
only runs once for each run key.
In `@cloud-agent/src/handlers/wsLiveAgentHandler.ts`:
- Around line 397-416: The live tool delivery path in wsLiveAgentHandler should
not stop on a failed sendToolResponse call, because the current try/catch in the
sessionOpen branch swallows the error and returns before the Expo Push fallback
can run. Update the browser_action handling so sendToolResponse failures are
detected and the code continues into the existing fallback flow that uses
bridgeBase.fcmDispatcher, firebaseUid, and getExpoPushToken; only return early
when delivery truly succeeds.
In `@docs/browser-bridge.md`:
- Around line 51-60: The fenced block in the browser bridge docs is missing a
language tag, so update the markdown fence to use an appropriate label such as
text. Make the change in the unlabeled code block that shows the FCM
WAKE_AND_CONNECT flow so the markdown stays lint-clean and renders correctly.
In `@docs/superpowers/plans/2026-06-29-phase3-proactive-scheduler.md`:
- Around line 963-977: Add the missing Content-Type header to the Cloud
Scheduler example for the scheduler-trigger POST so the request is explicitly
sent as JSON. Update the example near the scheduler-trigger job details to
include Content-Type: application/json alongside the existing Authorization
header, matching the req.body JSON validation expected by the handler.
In `@src/config/privacyConfig.ts`:
- Around line 72-95: Render the privacy copy as markdown instead of plain text:
the content in privacyConfig is written with heading and bold syntax, but
app/privacy.tsx passes PRIVACY.privacy directly into Text, so it should be
parsed/rendered with a markdown-aware component or converted before display.
Update the privacy screen rendering path in app/privacy.tsx (and any related
privacy text component) so the existing markdown in PRIVACY.privacy is formatted
correctly without changing the copy itself.
---
Nitpick comments:
In `@extension/src/background/service-worker.ts`:
- Around line 36-38: The GCM registration path in service-worker.ts is
swallowing failures because the callback in chrome.gcm.register returns early
when chrome.runtime.lastError is set without recording it. Update the
chrome.gcm.register callback to log chrome.runtime.lastError before the return,
then keep the existing registrationToken handling and registerDevice flow so
failures are visible for debugging.
In `@extension/src/ui/side-panel/panel.ts`:
- Line 23: Signing out in the side panel currently clears only `deviceId` and
leaves `gcmToken` behind, so update the click handler in `panel.ts` to remove
both keys from `chrome.storage.local` when `signOut(auth)` runs. Use the
existing `signOut` listener on `$('signout')` as the entry point and extend the
storage cleanup alongside the current `deviceId` removal.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 45a5e37e-8c78-44c2-9e3a-5e39264bb8f3
📒 Files selected for processing (54)
AGENTS.mdCONTRIBUTING.mdREADME.mdcloud-agent/scripts/deploy.shcloud-agent/src/handlers/authApprovalObserver.test.tscloud-agent/src/handlers/authApprovalObserver.tscloud-agent/src/handlers/schedulerTriggerHandler.test.tscloud-agent/src/handlers/schedulerTriggerHandler.tscloud-agent/src/handlers/wsBrowserAgentHandler.test.tscloud-agent/src/handlers/wsBrowserAgentHandler.tscloud-agent/src/handlers/wsLiveAgentHandler.test.tscloud-agent/src/handlers/wsLiveAgentHandler.tscloud-agent/src/index.test.tscloud-agent/src/index.tscloud-agent/src/services/fcmDispatcher.test.tscloud-agent/src/services/fcmDispatcher.tscloud-agent/src/services/liveToolAdapter.tscloud-agent/src/tools/browserAction.test.tscloud-agent/src/tools/browserAction.tsdocs/ai-and-chat.mddocs/architecture-and-data.mddocs/billing-and-credits.mddocs/browser-bridge.mddocs/cws-submission-artifacts.mddocs/edge-agent.mddocs/flowcharts/README.mddocs/flowcharts/c4/containers.mddocs/flowcharts/c4/system-context.mddocs/real-time-voice-chat.mddocs/superpowers/plans/2026-06-29-phase3-proactive-scheduler.mddocs/superpowers/plans/2026-06-30-cws-extension-readiness.mddocs/superpowers/specs/2026-06-29-mv3-browser-extension-bridge-design.mddocs/superpowers/specs/2026-06-30-cws-extension-readiness-design.mdextension/esbuild.mjsextension/manifest.jsonextension/src/background/auth-bridge.tsextension/src/background/content-bridge.test.tsextension/src/background/content-bridge.tsextension/src/background/service-worker.tsextension/src/background/task-dispatcher.test.tsextension/src/background/ws-client.test.tsextension/src/content/bridge-listener.tsextension/src/content/executor.test.tsextension/src/env.tsextension/src/offscreen/auth.tsextension/src/ui/side-panel/index.htmlextension/src/ui/side-panel/panel.test.tsextension/src/ui/side-panel/panel.tsextension/src/ui/side-panel/render-log.tsextension/test/chrome-stub.tsfirestore.indexes.jsonfirestore.rulessrc/config/privacyConfig.tstsconfig.json
…backs Add scheduler runKey reservation before credit spend, fall back to Expo Push when live tool delivery fails, render privacy markdown, and fix doc lint gaps. Co-authored-by: Cursor <cursoragent@cursor.com>
/fix-pr follow-upCommit: Review resolution
Verification
|
Description
Prepares the Clanker Desktop Bridge extension for Chrome Web Store review: manifest hardening, safe DOM rendering in the side panel, clearer offscreen document justification, privacy policy addendum, and copy-paste CWS dashboard artifacts.
Spec: CWS Extension Readiness Design Spec
Type of Change
Related Issue
Fixes #
Changes Made
icons,action.default_icon.128, andcontent_security_policytoextension/manifest.json(omitkeyuntil 1Password.pemis available)innerHTMLwrite in side-panelrenderLogwith safe DOM insertion; addpanel.test.tsauth-bridge.tsfor CWS reviewersdocs/cws-submission-artifacts.mdwith store listing copy, permission justifications, and preflight checklistsrc/config/privacyConfig.ts(v1.5)Testing
Platforms Tested
Test Steps
cd extension && npm run typecheck && npm testcd extension && npm run builddist/manifest.jsonhas icons, default_icon.128, and CSP; verifydist/ui/side-panel/panel.jshas noinnerHTMLassignmentsScreenshots
Before
N/A — manifest and side-panel log rendering changes only.
After
N/A
Checklist
Additional Notes
keyfield: Not yet inmanifest.json. Retrieve the signing.pemfrom 1Password and add the base64 public key before uploading to CWS (see preflight checklist indocs/cws-submission-artifacts.md).dist/: Gitignored; runnpm run buildinextension/before packaging for CWS.clanker-ai.com/privacyrenders the## Clanker Browser Extension Data Usagesection before submission.Made with Cursor