Events
Pages and background scripts can subscribe to workspace events. List every
workspace event you use in muxy.events; some also need a permission.
"events": ["tab.focused", "agent.status"],
"permissions": ["agents:read"]
const unsubscribe = muxy.events.subscribe("agent.status", (event) => {
console.log(event.providerID, event.status);
});
Every payload value is a string.
Workspace events
| Event | Payload | Needs |
|---|---|---|
tab.created, tab.closed, tab.updated | Tab context, below | |
tab.focused | areaID, tabID | |
pane.created, pane.closed | Tab context plus paneID (terminal panes) | |
pane.focused | projectID, worktreeID, areaID, tabID | |
project.switched | projectID | |
worktree.switched | projectID, worktreeID | |
projects.changed | none | projects:read |
worktree.headChanged | projectID, worktreeID, branch, path | worktrees:read |
agent.status | projectID, worktreeID, paneID, providerID, status (working, waiting, idle) | agents:read |
file.changed | path, projectPath | files:read |
notification.posted | paneID, projectID, worktreeID, worktreePath, tabID, source, title, body | |
panel.opened, panel.closed | extensionID, panelID | |
popover.opened, popover.closed | extensionID, popoverID | |
modal.opened, modal.closed | extensionID, modalID |
Tab context: tabID, projectID, worktreeID, areaID, title,
projectPath, and kind (terminal, extensionWebView, or browser).
Terminal tabs add paneID and cwd. Extension tabs add extensionID,
tabTypeID, tabInstanceID, and data as a JSON string. Browser tabs add
paneID and url.
projectIDis the top-level project.worktreeIDis the project or worktree the tab belongs to.file.changedcovers the current project and projects with the extension's tabs open.agent.statusreports the busiest agent per worktree;providerIDisclaude,codex,opencode,cursor,copilot,droid,pi,grok,kiro,xal,antigravity,omp, orother.
Command events
A command with the event action sends command.<id> to the extension. No
declaration needed.
Extension messages
An extension's pages and background script can message each other with
extension.* events. No declaration needed; they never leave the extension.
// page
await muxy.events.emit("extension.refresh", { reason: "button" });
// background
muxy.events.subscribe("extension.refresh", (payload) => { /* ... */ });
A page's message needs the background script running. Payloads are JSON, up to 64 KiB.
Background scripts
Set muxy.background to a script path to run code while the extension is
enabled. It starts with the extension, restarts when the extension updates,
and stops when it is disabled or reloaded. Use it to react to events, keep
state, or update bar items. Most extensions don't need one.
// background.js
muxy.events.subscribe("tab.focused", () => {
muxy.statusbar.set({ id: "count", text: String(muxy.agents.list().length) });
});
Background scripts run in-process on their own thread. Errors and console
output go to the extension's log.