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.
projectIDis the top-level project.worktreeIDis the project or worktree the tab belongs to.file.changedcovers the current project and projects with the kibble’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.
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.