Muxy

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

EventPayloadNeeds
tab.created, tab.closed, tab.updatedTab context, below
tab.focusedareaID, tabID
pane.created, pane.closedTab context plus paneID (terminal panes)
pane.focusedprojectID, worktreeID, areaID, tabID
project.switchedprojectID
worktree.switchedprojectID, worktreeID
projects.changednoneprojects:read
worktree.headChangedprojectID, worktreeID, branch, pathworktrees:read
agent.statusprojectID, worktreeID, paneID, providerID, status (working, waiting, idle)agents:read
file.changedpath, projectPathfiles:read
notification.postedpaneID, projectID, worktreeID, worktreePath, tabID, source, title, body
panel.opened, panel.closedextensionID, panelID
popover.opened, popover.closedextensionID, popoverID
modal.opened, modal.closedextensionID, 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.

  • projectID is the top-level project. worktreeID is the project or worktree the tab belongs to.
  • file.changed covers the current project and projects with the extension's tabs open.
  • agent.status reports the busiest agent per worktree; providerID is claude, codex, opencode, cursor, copilot, droid, pi, grok, kiro, xal, antigravity, omp, or other.

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.