Skip to content

Applies to Cowork 0.1.41 · For everyone who uses Cowork

Browser ​

With browser in dozin.capabilities, a Connector or task gets ctx.browser to read and act on web pages in Cowork tabs. Only tabs the person hands over can be used, and when dozin.origins is given, only pages on those sites.

ctx.browser.adoptTab(tabId) ​

Takes control of a tab. Cowork hands the tabId to the Extension when the person asks it to take over a page; an Extension cannot pick a tab by itself.

NameTypeRequiredDescription
tabIdstringYesThe tab Cowork handed over

Returns Promise<{ tabId, url, documentId }>. documentId changes each time the page loads a new document. At most 8 tabs at a time.

js
const tab = await ctx.browser.adoptTab(tabId);
ctx.log(`watching ${tab.url}`);

ctx.browser.releaseTab(tabId) ​

Gives the tab back. Its injected scripts and audio stop.

NameTypeRequiredDescription
tabIdstringYesA tab this Extension adopted

Returns Promise<void>.

js
await ctx.browser.releaseTab(tabId);

ctx.browser.evaluate(tabId, script) ​

Runs a script once in the page and returns its result.

NameTypeRequiredDescription
tabIdstringYesAn adopted tab
script.sourcestringYesA function source, at most 64 KB; it receives args
script.argsunknownNoPassed as JSON, not pasted into the source

Returns Promise<unknown>: the result, as JSON, at most 64 KB.

js
const title = await ctx.browser.evaluate(tabId, { source: '(args) => document.title' });

ctx.browser.inject(tabId, script) ​

Installs a script that stays in the page and can send messages back.

NameTypeRequiredDescription
tabIdstringYesAn adopted tab
script.idstringYes1–80 letters, digits, _ or -; unique in this tab
script.sourcestringYesA function (args, { emit, onDispose }) => cleanup?, at most 64 KB; emit(data) arrives as a message event
script.argsunknownNoPassed as JSON

Returns Promise<void>. At most 16 scripts per tab.

js
await ctx.browser.inject(tabId, {
  id: 'chat-watch',
  source: `(args, { emit }) => {
    const observer = new MutationObserver(() => emit({ count: document.querySelectorAll(args.selector).length }));
    observer.observe(document.body, { childList: true, subtree: true });
    return () => observer.disconnect();
  }`,
  args: { selector: '.message' }
});

ctx.browser.uninject(tabId, injectionId) ​

Removes an injected script and runs its cleanup.

NameTypeRequiredDescription
tabIdstringYesAn adopted tab
injectionIdstringYesThe id given to inject

Returns Promise<void>.

js
await ctx.browser.uninject(tabId, 'chat-watch');

ctx.browser.captureAudio(tabId) ​

Needs audio too. Starts capturing the tab's sound.

NameTypeRequiredDescription
tabIdstringYesAn adopted tab that has finished loading

Returns Promise<AudioStream>:

FieldTypeDescription
tabId · url · documentIdThe tab, as in adoptTab
streamIdstringThis capture
framesAsyncIterable<{ streamId, sequence, timestamp, sampleRate, channels, pcm: Float32Array }>Sound, chunk by chunk
endedPromiseSettles when the capture ends
stop()() => Promise<void>Stop it

One capture per tab at a time.

js
const stream = await ctx.browser.captureAudio(tabId);
for await (const chunk of stream.frames) handle(chunk.pcm, chunk.sampleRate);

ctx.browser.stopAudio(tabId) ​

Stops the tab's capture.

NameTypeRequiredDescription
tabIdstringYesA tab with a running capture

Returns Promise<void>.

js
await ctx.browser.stopAudio(tabId);

ctx.browser.onEvent(listener) ​

NameTypeRequiredDescription
listener(event) => voidYesCalled for every event of the Extension's tabs

Returns () => void to stop listening. Every event carries tabId, url and documentId, and a type:

typeExtra fieldsWhen
url-changed · navigation-start · ready—The page moves or finishes loading
closed · crashed—The tab is gone
injectedinjectionIdA script was installed
messageinjectionId, dataAn injected script called emit(data)
errorcodeSomething failed in the tab
audio-endedstreamId, lastSequenceA capture ended
js
const off = ctx.browser.onEvent((event) => {
  if (event.type === 'message') ctx.emit({ text: String(event.data.count) });
  if (event.type === 'closed') off();
});

Errors ​

The functions reject with one of these codes:

CodeMeaning
capability_deniedbrowser (or audio) not declared
invalid_tab · tab_not_granted · tab_not_ownedBad tab id, Cowork did not hand this tab over, or it was released
origin_not_allowedThe page's site is not in dozin.origins
tab_limit · injection_limitMore than 8 tabs or 16 scripts
tab_unavailable · document_not_ready · stale_documentThe tab closed, is still loading, or loaded another page
invalid_script · invalid_injection_id · injection_exists · invalid_json · result_too_largeBad input or output
audio_already_started · audio_transport_unavailable · invalid_audio_requestAudio capture could not start
session_closed · browser_operation_failedThe Extension is stopping, or Cowork could not complete the call

For Micromeet Cowork 0.1.41 · build 261111110020