[feat]: add clipboard API#2171
Conversation
🦋 Changeset detectedLatest commit: d7103c2 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
2 issues found across 7 files
Confidence score: 3/5
- There is some moderate merge risk because both reported issues are medium severity (6/10) with high confidence, including a concrete cleanup gap and potential instrumentation inconsistency.
- The most impactful issue is in
packages/core/examples/clipboard.ts: missingstagehand.close()in atry/finallycan leak Browserbase sessions when users follow this example, which can cause resource/cost problems. - In
packages/core/lib/v3/types/public/index.ts, new publicclipboardunderstudy methods appear not to be fully wired throughflowLogger, creating observability/tracing gaps for those APIs. - Pay close attention to
packages/core/examples/clipboard.tsandpackages/core/lib/v3/types/public/index.ts- ensure session cleanup is guaranteed and new public methods are consistently instrumented.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/core/lib/v3/types/public/index.ts">
<violation number="1" location="packages/core/lib/v3/types/public/index.ts:8">
P2: Custom agent: **Ensure all public methods added to the stagehand class, agent, or understudy (page, locator, etc.) interfaces are properly instrumented with the flowLogger**
New `clipboard` public understudy methods lack flowLogger instrumentation</violation>
</file>
Architecture diagram
sequenceDiagram
participant Client as User Code / Test
participant Ctx as V3Context
participant Clip as ContextClipboard
participant Page as Page (target)
participant CDP as CDP Session
participant Browser as Browser Process
Note over Client,Browser: context.clipboard - Write / Read Text Flow
Client->>Ctx: clipboard.writeText(text, options?)
Ctx->>Clip: resolvePage(options?.page)
alt Explicit page provided
Clip->>Ctx: params.resolvePage(page)
Ctx-->>Clip: specific Page
else Default to active page
Clip->>Ctx: params.resolvePage(undefined)
Ctx->>Ctx: activePage()
Ctx-->>Clip: active Page
end
Clip->>Page: ensurePageFocused(page)
Page->>Page: context.setActivePage(this)
Page->>CDP: Page.bringToFront
Page->>CDP: Runtime.evaluate("window.focus()")
Clip->>Page: grantClipboardPermissions(page)
Page->>Page: originForPage() - parse URL
alt Valid http/https origin
Page->>CDP: Browser.grantPermissions
CDP-->>Page: OK
else Invalid origin (about://, data:)
Page-->>Clip: skip permission grant
end
Clip->>Page: Runtime.enable (catch errors)
Clip->>Page: Runtime.evaluate
Note over Clip,Page: navigator.clipboard.writeText(text) or readText()
Page->>CDP: Forward evaluation
CDP->>Browser: Execute in page context
Browser-->>CDP: Result / Error
CDP-->>Page: EvaluateResponse
Page-->>Clip: Response
alt Evaluation succeeded
Clip->>Clip: throwIfEvaluationFailed() - no exception
Clip-->>Ctx: void / text string
Ctx-->>Client: Result
else Exception in evaluation
Clip->>Clip: throwIfEvaluationFailed() - extract exception
Clip-->>Ctx: throw StagehandEvalError
Ctx-->>Client: Error
end
Note over Client,Browser: context.clipboard - Paste / Copy / Cut Flow
Client->>Ctx: clipboard.paste(options?)
Ctx->>Clip: resolvePage(options?.page)
Ctx-->>Clip: target Page
Clip->>Page: ensurePageFocused(page)
Page->>CDP: Page.bringToFront
Page->>Page: window.focus()
Clip->>Page: keyPress(options?.shortcut ?? "ControlOrMeta+V")
Page->>CDP: Input.dispatchKeyEvent
CDP->>Browser: Keyboard shortcut
Browser-->>CDP: Key processed
CDP-->>Page: OK
Clip-->>Ctx: void
Ctx-->>Client: void
Note over Client,Browser: context.clipboard - Clear (delegates to writeText)
Client->>Ctx: clipboard.clear(options?)
Ctx->>Clip: writeText("", options)
Clip->>Page: ensurePageFocused + grantPermissions
Clip->>Page: Runtime.evaluate("navigator.clipboard.writeText('')")
Page->>CDP: Forward
CDP->>Browser: Execute
Browser-->>CDP: Result
Clip-->>Ctx: void
Ctx-->>Client: void
Note over Client,Browser: Integration Test - Active Page vs Explicit Page
Client->>Ctx: clipboard.writeText("active page text")
Ctx->>Clip: resolvePage(undefined)
Ctx-->>Clip: page2 (active)
Clip->>Page: writeText + paste on page2
Ctx-->>Client: OK
Client->>Page: textareaValue(page1) = ""
Client->>Page: textareaValue(page2) = "active page text"
Client->>Ctx: clipboard.writeText("explicit page text", { page: page1 })
Ctx->>Clip: resolvePage(page1)
Clip->>Page: writeText on page1
Ctx-->>Client: OK
Client->>Ctx: clipboard.paste({ page: page1 })
Ctx->>Clip: resolvePage(page1)
Clip->>Page: paste on page1 (even when page2 is active)
Ctx-->>Client: OK
Client->>Page: textareaValue(page1) = "explicit page text"
Client->>Page: textareaValue(page2) = ""
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/core/lib/v3/types/public/index.ts">
<violation number="1" location="packages/core/lib/v3/types/public/index.ts:8">
P2: Custom agent: **Ensure all public methods added to the stagehand class, agent, or understudy (page, locator, etc.) interfaces are properly instrumented with the flowLogger**
New `clipboard` public understudy methods lack flowLogger instrumentation</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.
Re-trigger cubic
|
@seanmcguire12 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 7 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Architecture diagram
sequenceDiagram
participant Client as Client Code
participant Clipboard as ContextClipboard
participant V3Context as V3Context
participant FlowLogger as FlowLogger
participant Page as Target Page
participant CDP as CDP/Browser
Note over Client,CDP: Clipboard API Flow
Client->>V3Context: access clipboard property
V3Context->>Clipboard: NEW: create ContextClipboard instance
alt readText / writeText
Client->>Clipboard: readText() / writeText(text)
Clipboard->>Clipboard: resolvePage(page?)
Clipboard->>V3Context: resolvePage(optional page)
V3Context-->>Clipboard: return Page instance
Clipboard->>Page: ensurePageFocused()
Page->>Page: setActivePage(page)
Page->>CDP: Page.bringToFront
Page->>CDP: Runtime.evaluate(window.focus())
Clipboard->>Page: grantClipboardPermissions()
Page->>V3Context: get origin from page.url()
V3Context->>CDP: Browser.grantPermissions(origin, [clipboardReadWrite, clipboardSanitizedWrite])
Clipboard->>Page: sendCDP("Runtime.evaluate", navigator.clipboard.writeText/readText)
Page->>CDP: Runtime.evaluate with awaitPromise
CDP-->>Page: evaluation result
Page-->>Clipboard: response with value/exception
alt Evaluation Failed
Clipboard->>Clipboard: throwIfEvaluationFailed() -> StagehandEvalError
else Success
Clipboard-->>Client: return text value
end
else paste / copy / cut
Client->>Clipboard: paste() / copy() / cut()
Clipboard->>Clipboard: resolvePage(page?)
Clipboard->>Page: ensurePageFocused()
Page->>CDP: Page.bringToFront + window.focus()
Clipboard->>Page: keyPress(shortcut)
Page->>CDP: keyboard shortcut (ControlOrMeta+V/C/X)
Clipboard-->>Client: void
else clear
Client->>Clipboard: clear()
Note over Clipboard: delegates to writeTextInternal("")
Clipboard->>Page: writeTextInternal("")
Page->>CDP: Runtime.evaluate(navigator.clipboard.writeText(""))
Clipboard-->>Client: void
end
Note over FlowLogger: All public methods decorated with @FlowLogger.wrapWithLogging
FlowLogger->>FlowLogger: emit event type (ClipboardReadText/WriteText/etc.)
There was a problem hiding this comment.
You should add a test for clicking an element that triggers copy to clipboard in JS. Then confirm users can read the copied value via the new APIs. Chrome uses user-initiated input actions to gate clipboard behavior, so it's important to test it works with their gating system.
e.g.
var copyTextareaBtn = document.querySelector('.js-textareacopybtn');
copyTextareaBtn.addEventListener('click', function(event) {
var copyTextarea = document.querySelector('.js-copytextarea');
copyTextarea.focus();
copyTextarea.select();
try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
console.log('Copying text command was ' + msg);
} catch (err) {
console.log('Oops, unable to copy');
}
});<p>
<button class="js-textareacopybtn" style="vertical-align:top;">Copy Textarea</button>
<textarea class="js-copytextarea">Hello I'm some text</textarea>
</p>| copy(options?: ClipboardOptions): Promise<void>; | ||
| cut(options?: ClipboardOptions): Promise<void>; |
There was a problem hiding this comment.
should cut/copy return the text? prevents having to read after
There was a problem hiding this comment.
initially i had them returning a string, but then this kind of paints the API into a corner. the thinking was to keepcopy & cut generic APIs, so we can support more content types in the future
| readText(options?: ClipboardOptions): Promise<string>; | ||
| writeText(text: string, options?: ClipboardOptions): Promise<void>; |
There was a problem hiding this comment.
is Text explicit because this doesn't support images?
There was a problem hiding this comment.
yeah also clipboard supports all kinds of extra interesting things, like multiple mimetype representations for the same text that consumers can choose between, images, etc.
we may want to think about how to design it to not preclude supporting those things in the future with optional args.
There was a problem hiding this comment.
yeah, I intentionally made Text explicit so that we can add future read/write functions for the various content types, or add generic read/write APIs which return an object containing nullable fields of all the possible types.
why
what changed
context.clipboardwhich has the following methods:readText,writeText,clear,paste,copy, &cutcontext.activePage()by default, but callers can pass{ page }to target a specificPagetest plan
clipboard.spec.tsto coverreadText,writeText,paste,copy, &cut{ page }targets that specificPage, even when another page is activeSummary by cubic
Adds a native clipboard API to the context for read/write and cut/copy/paste. Defaults to the active page, handles permissions, focuses the target page, and logs actions via FlowLogger without duplicates.
New Features
context.clipboardwithreadText,writeText,clear,paste,copy, andcut.context.activePage()by default; pass{ page }to target a specificPage.pastesupports ashortcutoverride.BrowserClipboard, options), and includes an example and integration tests verifying active vs explicit page behavior.Bug Fixes
Written for commit d7103c2. Summary will update on new commits.
Review in cubic