Local and Global State (upcoming)

Available in version 1.1.72 (not yet live).

Store values for the current run (local state) or across runs on the same Desktop Agent (global state).

API zw.state.*, zw.globalState.*

// @zw-run-locally

// Local per-run state — cleared automatically when the run ends
const runState = zw.state.access();
runState.runStartedAt = Date.now();

// Shared Desktop Agent global state — persists across runs on this device
const globalState = zw.globalState.access();
globalState.totalRuns = (globalState.totalRuns ?? 0) + 1;
// In the browser — work with a JSON-safe snapshot of local or global state
const runStateCopy = await zw.state.browser.getCopy();
runStateCopy.lastSeenAt = Date.now();
await zw.state.browser.commit({ state: runStateCopy });

1. Local Per-Run State — zw.state.*

A per-run scratchpad automatically initiated on run start. It is deleted when the run ends.

At a Glance

  • zw.state.access() → a live mutable object you can read and update anywhere in the run. It can hold anything (e.g., helpers, counters, functions, Maps, Playwright objects like pages or contexts, etc.). It's deleted automatically when the run ends. It is not available in the browser (see Use state in the browser below).

  • zw.state.clear() → clears the whole run state.

Examples

// @zw-run-locally

// Add a type-safe validator and stringifier around zw.setRef()
zw.state.access().setRefSafely = ({ ref_id, name, value }, expectedType) => {
  if (typeof value !== expectedType) {
    throw new Error(`Value for ${name} is not ${expectedType}`);
  }
  const valueStr = typeof value === "string"
    ? value
    : value
      ? JSON.stringify(value)
      : "";
  zw.setRef({ ref_id, name, value: valueStr });
};

// Use the helper in later blocks within the same run
zw.state.access().setRefSafely({ ref_id, name: "age", value: 42 }, "number");
// @zw-run-locally

// Counters or flags across multiple Write JS blocks in the same run
const runState = zw.state.access();
runState.processed = (runState.processed ?? 0) + 1;
await zw.log("processed", runState.processed);

// Clear mid-run if needed
zw.state.clear();

2. Global State — zw.globalState.*

A device-level scratchpad shared across runs on the same Desktop Agent. Persists between runs until the desktop agent quits or restarts. Use it when you need to reuse something across runs on one machine (e.g., cached tokens, a counter of all runs, etc.).

At a Glance

  • zw.globalState.access() → a live mutable object you can read and update across runs. It is not available in the browser (see Use state in the browser below).

  • zw.globalState.clear() → clears the whole global state.

Example

// @zw-run-locally

// Track which TaskBots ran today (per device)
const gs = zw.globalState.access();
const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD

if (!gs.botsThatRanToday || gs.botsThatRanToday.date !== today) {
  gs.botsThatRanToday = { date: today, bots: [] };
}

const taskbotInfo = await zw.getTaskbotInfo();
gs.botsThatRanToday.bots.push(taskbotInfo.id);

Notes

  • It is not durable storage. It is lost when the Desktop Agent quits or restarts. For durable data use:

    • Variables/tables via zw.getRef() / zw.setRef()

    • Device storage via zw.deviceStorage.*

  • As long as the Desktop Agent isn't quit or restarted, global state is not auto-cleaned. Clear what you no longer need.


3. Use State in the Browser — zw.state.browser.* and zw.globalState.browser.*

In the browser you cannot use *.access(). Use JSON-safe snapshots instead.

At a Glance

  • await zw.state.browser.getCopy({ key? }) await zw.globalState.browser.getCopy({ key? }) → returns the current state (or the section under key).

  • await zw.state.browser.commit({ state, key? }) await zw.globalState.browser.commit({ state, key? }) → accepts any serializable value. If key is provided, only that section is updated.

key is optional and lets you read/update just a section (safer, smaller payloads; no need to snapshot the whole state if it’s large).

Examples

// Whole snapshot

const stateCopy = await zw.state.browser.getCopy();
stateCopy.step = (stateCopy.step ?? 0) + 1;
await zw.state.browser.commit({ state: stateCopy });
// Section-only snapshot with 'key'

const prefs = (await zw.state.browser.getCopy({ key: "prefs" })) ?? {};
prefs.theme = "dark";
await zw.state.browser.commit({ key: "prefs", state: prefs });

Notes

  • JSON-safe only when using *.browser.getCopy() or *.browser.commit() Snapshots must be JSON-safe. Functions, Maps, circular references, and similar types cannot be serialized for browser use. If your state contains such values, use key to fetch only a JSON-safe section that you need in the browser.

  • In-browser snapshot size limits Passing snapshots larger than ~3,000,000 characters fails. If your state is large, use key to work with just a section. Serialization of smaller objects is also faster.

  • In-browser calls require await In the browser, *.browser.getCopy(), *.browser.commit(), and *.clear() are async.

    // 🚫 Won't work
    const stateCopy = zw.state.browser.getCopy(); // missing await
    stateCopy.runData = {}; // throws because stateCopy is a Promise
    await zw.state.browser.commit({ state: stateCopy }); // this line never runs
    
    // ✅ Works
    const stateCopy = await zw.state.browser.getCopy();
    stateCopy.runData = {};
    await zw.state.browser.commit({ state: stateCopy });
  • *.browser.* scope is reserved for the browser only zw.state.browser.* is meant for the browser context. In local runs, just use zw.state.access() and make a copy yourself if needed:

    // @zw-run-locally
    const s = zw.state.access();
    const shallowCopy = { ...s };

Last updated

Was this helpful?