These packages wire the agent's runtime state into your UI. The plan state machine tracks guided steps, glanceable events surface short observations for overlays and HUDs, knowledge sources expose what the agent cited, and credits keep usage visible to the worker.

Plan State, Glanceable, Knowledge, Credits

Plan State Machine — @picoagent/embed-plan

The plan is the list of steps the agent will walk the worker through. createPlanStore turns the raw session events into a reactive state object you can render directly.

import { createPlanStore } from "@picoagent/embed-plan";

const planStore = createPlanStore(session);

planStore.onStateChange((state) => {
  renderStepPanel(state.currentStep, state.currentStepIndex, state.totalSteps);
});

// When the agent sends a plan, call confirmPlan() to tell it the worker
// has reviewed and accepted the steps before proceeding.
session.on("plan", () => planStore.confirmPlan());

State Shape

FieldTypeDescription
currentStepobject | nullThe active step object, including description and any metadata. null before the first plan arrives.
currentStepIndexnumberZero-based index of the active step.
totalStepsnumberTotal number of steps in the current plan.

confirmPlan()

Calling confirmPlan() signals to the agent that the worker has reviewed the plan and is ready to begin. The agent will not advance to the first step until this signal is received. Call it in response to a user action (e.g. a "Start" button) or automatically inside session.on("plan", ...) if your UI does not show a pre-start review screen.

Glanceable Overlay — @picoagent/embed-glance

Glanceable text is a short phrase the agent emits as its current surface observation — designed for AR overlays, HUD banners, or any persistent status area that needs to update in near-real time without interrupting the main UI flow.

import { onGlanceable } from "@picoagent/embed-glance";

const offGlance = onGlanceable(session, (text, source) => {
  showOverlay(text);
});

// When you no longer need glanceable updates (e.g. on unmount), call the off function.
offGlance();

The callback receives two arguments:

  • text — the glanceable string to display (e.g. "Good knee bend", "Watch your elbow angle")
  • source — an identifier for which part of the agent pipeline produced the observation

onGlanceable returns an unsubscribe function. Store it and call it when your component unmounts to avoid memory leaks.

Knowledge Sources — @picoagent/embed-knowledge

When the agent answers a question by retrieving content from the knowledge base, onKnowledge delivers the list of documents or pages it cited.

import { onKnowledge } from "@picoagent/embed-knowledge";

onKnowledge(session, (sources) => {
  renderSourcesList(sources);
});

sources is a KnowledgeSource[]. Each entry describes the document or page the agent cited when forming its answer — use this to show a "Sources" panel or footnote links in your UI.

A minimal rendering example:

onKnowledge(session, (sources) => {
  const list = document.getElementById("sources-list");
  list.innerHTML = sources
    .map((s) => `<li>${s.title}</li>`)
    .join("");
});

Credits HUD — @picoagent/embed-credits

onCredits delivers a live usage snapshot whenever the session's credit balance updates. Use it to keep a usage meter visible so workers are aware of session costs.

import { onCredits } from "@picoagent/embed-credits";

onCredits(session, ({ creditsUsed, sessionMinutes, ratePerMin }) => {
  updateCreditsHUD(creditsUsed, sessionMinutes, ratePerMin);
});
FieldTypeDescription
creditsUsednumberTotal credits consumed so far in this session.
sessionMinutesnumberElapsed session time in minutes (floating point).
ratePerMinnumberCredit burn rate per minute for this session configuration.

Session Events

Subscribe to session events with session.on(eventName, handler). All four events are fired on the Session object returned by createSession().

plan

session.on("plan", (e) => {
  // e.plan contains the full plan object
  planStore.confirmPlan(); // acknowledge the plan when ready
});

Fires when the agent sends a plan. This is the initial trigger for the plan state machine. It may also fire mid-session if the agent revises the plan.

step_update

session.on("step_update", (e) => {
  // e.stepIndex — zero-based index of the new active step
  // e.totalSteps — total step count
  updateStepIndicator(e.stepIndex, e.totalSteps);
});

Fires whenever the active step index changes — typically when the worker advances to the next step or the agent moves the session forward automatically.

session_ended

session.on("session_ended", (e) => {
  // e.reason is one of: "completed" | "terminated" | "credits_exhausted"
  showEndScreen(e.reason);
});

Fires when the session is over. The reason values:

ReasonMeaning
completedThe agent finished all steps and closed the session normally.
terminatedThe session was ended by the server or an admin action.
credits_exhaustedThe session hit the credit cap set in the credential.

error

session.on("error", (e) => {
  if (!e.recoverable) {
    showFatalErrorScreen(e.message);
    return;
  }
  showToast(e.message);
});

Fires when a session-level error occurs. Always check e.recoverable:

  • true — the session is still running. Show a toast or inline warning and let the worker continue.
  • false — the session cannot continue. Show a full error screen and offer a retry or exit option.

e.code and e.message provide machine-readable and human-readable details respectively.

Session Resume — getResumeRunId()

The SDK automatically writes the current runId to browser session storage. Use getResumeRunId() to retrieve it — do not read session storage directly, since the key name is an SDK implementation detail and may change. If the worker refreshes the page mid-session, you can detect this and resume from where they left off.

import { createSession, getResumeRunId } from "@picoagent/embed-core";

// On page load — check for an in-progress session
const existingRunId = getResumeRunId();

// Pass it to your backend, which forwards it to POST /guide/session
const credential = await fetch("/api/your-token-endpoint", {
  method: "POST",
  body: JSON.stringify({
    agentId: "your-agent-id",
    continueRunId: existingRunId ?? undefined,
  }),
}).then(r => r.json());

const session = await createSession({ credential });

When continueRunId is provided, Bourne restores the plan state, step index, and conversation history from the previous session. The SDK clears its session-storage record as soon as the session ends with a terminal reason (completed, terminated, credits_exhausted), so getResumeRunId() will return null on a clean next visit.

Next Steps