Files
paseo/public-docs/sdk/events.md
Mohamed Boudra 18aaec2772 fix(client): keep daemon connections lean by default (#4470)
Move supported protocol capabilities into client defaults and make session traffic subscription-driven. Preserve timeline readiness and reconnect recovery across SDK, CLI, and plugin consumers.
2026-09-08 12:59:25 +02:00

3.5 KiB

title, description, nav, order, category
title description nav order category
SDK events Subscribe to agent status, timeline, workspace, and provider updates without maintaining a second state model. Events 56 TypeScript SDK

SDK events

Subscriptions report changes after they happen. Fetch an initial snapshot first, then apply updates to it.

Every subscribe() method returns an unsubscribe function. Timeline, project, and provider subscriptions establish network demand and restore it after reconnect. Unsubscribing releases that demand when its last listener leaves. Agent and workspace directories use the explicit list({ subscribe }) bootstrap shown below. Unsubscribing never stops or archives the underlying resource.

Follow one agent's status

The daemon sends agent-directory updates only after the connection opens an agent-list subscription:

await client.agents.list({
  filter: { includeArchived: false },
  subscribe: { subscriptionId: "issue-board-agents" },
});

const agent = client.agents.ref(agentId);
await agent.refresh();

const unsubscribe = agent.subscribe((update) => {
  if (update.kind === "upsert") {
    console.log(update.agent.status);
  } else {
    console.log("Agent removed from the active directory");
  }
});

The handle updates its properties and current() value before it calls your handler.

Follow timeline events

const unsubscribe = agent.timeline.subscribe((update) => {
  const { event } = update;
  if (event.type === "replacement") {
    // Previously fetched history belongs to an old epoch. Fetch the page your UI needs.
    void agent.timeline.refetch().then((page) => console.log(page.entries));
    return;
  }
  if (event.type === "timeline" && event.item.type === "assistant_message") {
    process.stdout.write(event.item.text);
  }

  if (event.type === "turn_completed") {
    console.log("\nTurn completed");
  }
});

Await unsubscribe.ready before starting work whose events you need to observe. It resolves when the daemon acknowledges the subscription and rejects if establishment fails. Call unsubscribe() to release it.

Assistant messages can arrive in pieces. Concatenate their text when you need a complete message, or use run() and read lastMessage when you only need the final reply.

Turn completion comes from turn_completed, turn_failed, or turn_canceled. Do not infer turn completion from an agent_update transition to idle.

Fetch timeline history

const page = await agent.timeline.refetch({
  direction: "before",
  limit: 100,
  projection: "projected",
});

for (const entry of page.entries) {
  console.log(entry.seq, entry.event.type);
}

Use startCursor, endCursor, hasOlder, and hasNewer from the result to page without inventing offsets.

Follow workspace updates

Workspace updates also require a directory subscription:

await client.workspaces.list({
  subscribe: { subscriptionId: "issue-board-workspaces" },
});

const workspace = client.workspaces.ref(workspaceId);
const unsubscribe = workspace.subscribe((update) => {
  if (update.kind === "upsert") {
    console.log(update.workspace.status);
  }
});

Follow provider catalog changes

const unsubscribe = client.providers.subscribe((update) => {
  const ready = update.entries.filter((entry) => entry.status === "ready");
  console.log(
    "Ready providers:",
    ready.map((entry) => entry.provider),
  );
});

Always call the returned unsubscribe functions before discarding the owning object. Call client.close() when the application no longer needs the daemon connection.