Events

Events

Pages and background scripts can subscribe to workspace events. List every workspace event you use in nana.events; some also need a permission.

"events": ["tab.focused", "agent.status"],
"permissions": ["agents:read"]
const unsubscribe = nana.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. Kibble 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 kibble’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.

Kibble messages

A kibble’s pages and background script can message each other with extension.* events. No declaration needed; they never leave the extension.

// page
await nana.events.emit("extension.refresh", { reason: "button" });

// background
nana.events.subscribe("extension.refresh", (payload) => { /* ... */ });

A page’s message needs the background script running. Payloads are JSON, up to 64 KiB.

Background scripts

Set nana.background to a script path to run code while the kibble is enabled. It starts with the kibble, restarts when the kibble updates, and stops when it is disabled or reloaded. Use it to react to events, keep state, or update bar items. Most kibbles don’t need one.

// background.js
nana.events.subscribe("tab.focused", () => {
  nana.statusbar.set({ id: "count", text: String(nana.agents.list().length) });
});

Background scripts run in-process on their own thread. Errors and console output go to the kibble’s log.