NevoFlux
Canvas

NevoFlux SDK

The JavaScript API available inside Canvas apps — agent, storage, events, tools, sharing.

Every Canvas artifact runs with a global window.NevofluxSDK injected. Through it your app can talk to the agent, persist data, exchange events, run whitelisted tools, and share itself — all over a safe message bridge to the host.

API surface

NamespaceMethods
(root)callTool(action, params)
agentchat(message, options), cancel(sessionId), sendCommand(command, params)
sidebaropen(), send(message), notify(type, data)
storageget(key), set(key, value), delete(key), query(prefix)
systemgetInfo()
eventssubscribe(patterns, handler, options), publish(topic, data, options), history(topic, options), waitFor(pattern, options), recover()
toollist(options), invoke(toolName, params, options)
shareshare(artifactId, options), import(shareId, password), extend(shareId, secs), delete(shareId), list()

agent.chat(message, options)

Streams a reply from the agent. options may include sessionId, attachments, and callbacks onStream, onToolResult, onState. Resolves to { text, toolResults, sessionId }.

Example

// 1) Ask the agent and stream the answer into the UI
const result = await NevofluxSDK.agent.chat("summarize the current tab", {
  onStream: (c) => { output.textContent += c.delta; },
  onState:  (s) => { status.textContent = s.status; },
});
console.log(result.text);

// 2) Persist a little state for this artifact
await NevofluxSDK.storage.set("count", "1");
const count = await NevofluxSDK.storage.get("count");

// 3) Discover and run a whitelisted tool, streaming its events
const { tools } = await NevofluxSDK.tool.list();
const { callId } = await NevofluxSDK.tool.invoke(
  "ffmpeg.probe",
  { input: "/clip.mp4" },
  { timeoutMs: 10000, onEvent: (e) => console.log(e.event_type, e) }
);

// 4) React to events from elsewhere in NevoFlux
const sub = NevofluxSDK.events.subscribe(["ui:*"], (evt) => {
  console.log("event", evt);
});
// sub.unsubscribe() when done

Which tools tool.invoke can run is controlled by the tool whitelist. Calls to disabled or unlisted tools are rejected.

What a canvas may do

A canvas runs in an isolated iframe, so the Canvas bridge — the host side of NevofluxSDK — is its only way to reach anything privileged. Since NevoFlux 0.3.16 the bridge keeps each canvas to what is its own. Code that relied on the looser behaviour of earlier versions may need changes:

AreaRule
storagePrivate to the canvas: keys live in that canvas's own namespace, so two canvases never see each other's data and query('') lists only this canvas's keys. A key written before 0.3.16 is moved into the namespace the first time it is read, unless it starts with config:, canvas:, session: or app:.
agent.chat / agent.cancelA canvas can only continue or cancel sessions it started. Only image attachments ({ name, mime_type, data }) are attached; a { path } attachment is named in the message instead, and the agent decides whether to read it with its file tools — under your Agent execution tier.
agent.sendCommandA canvas you or the agent made may send only daemon.info, skill.list, and artifact.get for its own artifact. A pack dashboard may send exactly the commands its manifest declares. Settings, pairing, account, raw-storage, MCP-server and pack-install commands are never available to a canvas.
callToolActions that change a page (click, type, …) work only in tabs the canvas opened itself — open one with navigate and new_tab: true. Read actions work on any tab.
sidebar.sendText only, up to 4,000 characters, shown in the chat labelled with the canvas id.
sidebar.notifyA system notification titled with the canvas's name, at most three per 30 seconds per canvas.
events.publishTopics starting with ui:notification, ui:soul, ui:render or ui:tab belong to the browser and are refused.
shareshare() always shares the calling canvas; list() shows only its shares, and extend / delete work only on them.

A refused call rejects its promise with an error whose message starts with a code such as FORBIDDEN_COMMAND:, COMMAND_NOT_AVAILABLE: or NOT_OWN_SESSION:, so your code can tell a policy refusal from a failure.

Policy checks on callTool

Before a canvas's callTool runs, the browser asks the agent's policy (canvas.policy_check) whether it may, using the same pack site rules the agent's own calls go through:

  • A rule that would ask refuses instead — a canvas cannot host a confirmation dialog mid-call.
  • A pack dashboard built for pack-protocol/0.2 may only use the actions it declared in its manifest.
  • If the agent cannot be reached, reads still work and anything else is refused, so stopping the agent is not a way around a pack's rules.

The check and the tab-ownership rule are enforced by the browser before the action runs, not by the canvas, so canvas code cannot skip them.

On this page