N AgentNavaKit
agentnava.com →

How a session works

Your UI · start an.sessions.start({ agentId, env, user, vars }) Mints a session for one user's conversation. vars become per-session state readable from every tool handler.
session id minted
AgentNava platform · state Session holds its own history, attachments, vars Independent from every other session on the same deployed agent. Auto-hibernates after 24h idle, resumes instantly.
each turn
Your UI · chat for await (const event of an.sessions.chat(id, msg)) Opens an SSE iterator for one turn. The iterator closes when the agent's reply is complete.
agent runs · streams events
AgentNava platform · runtime Typed AgentEvents stream back over SSE message-delta → chat, tool-start/tool-end → spinners, widget-update → renderers, phase → progress, done → end of turn.

How sessions get created

SourceBehavior
Your code an.sessions.start({ agentId, env, user, vars }) mints a session ID. Use it on every subsequent chat call.
Embed widget The embed creates one session per browser tab on first message. The session ID is persisted in localStorage, so users can refresh and resume.
Implicit (single-turn) Call an.sessions.chat without an ID and the runtime mints a one-shot session, returns the response, and tears it down. Good for fire-and-forget invocations.

Start a session

const session = await an.sessions.start({
  agentId: 'agt_2b8e',
  env:     'prod',                            // or 'test'
  user:    { id: 'u_42', name: 'Jamie' },     // your auth's user identity, propagated to tool handlers
  vars:    { plan: 'gold', region: 'eu' },    // per-session vars, readable in tool handlers as ctx.vars
});
// → { id: 'sess_5d3a', agentId: 'agt_2b8e', env: 'prod', startedAt: '…' }

Resume an existing session by ID:

const existing = await an.sessions.get(sessionId);

The chat call

Each turn returns an async iterator of AgentEvents. The iterator completes when the agent's turn ends.

for await (const event of an.sessions.chat(session.id, 'Can you walk me through this floor plan?')) {
  switch (event.type) {
    case 'message-start':   /* assistant turn begins */                       break;
    case 'message-delta':   appendTokens(event.delta);                        break;
    case 'message-end':     /* full text in event.final */                    break;
    case 'tool-start':      showSpinner(event.name);                          break;
    case 'tool-end':        hideSpinner(event.toolId);                        break;
    case 'phase':           updatePhase(event.label, event.state);            break;
    case 'widget-update':   renderWidget(event);                              break;
    case 'widget-remove':   unmountWidget(event.widgetId);                    break;
    case 'error':           reportError(event.message);                       break;
    case 'done':            /* iterator will close after this */              break;
  }
}

The shape is the same whether the agent is on a test or a prod deploy. See Spec → Event stream for the full type union.

Sending structured messages

Pass an object instead of a string for messages with role, attachments, or metadata:

for await (const event of an.sessions.chat(session.id, {
  role:    'user',
  content: 'Can you walk me through this floor plan?',
  attachments: [
    { id: 'file_4f1c9a', type: 'image', name: 'floor-plan.png' },
  ],
  meta: { source: 'embed-widget', clientVersion: '1.4.2' },
})) {
  // …
}

Attachments

Attachments are files the user uploads as part of a conversation. They live for the lifetime of the session and are not added to the agent's persistent Knowledge base — they're transient context for that one chat.

// Upload a file to a session; returns a file ID you reference in a message
const file = await an.sessions.attachFile(session.id, blob, { name: 'floor-plan.png' });
// → { id: 'file_4f1c9a', type: 'image', status: 'ready' }

await an.sessions.chat(session.id, {
  role: 'user',
  content: 'What rooms are visible?',
  attachments: [{ id: file.id }],
});

The embed widget does this automatically when the user drops a file into the chat composer (if data-uploads="true" is on the script tag — see Operate → Embed).

Per-session variables

Pass vars at session start. Read them from any tool handler via ctx.vars; update them mid-session via ctx.setVar.

// At session start
await an.sessions.start({
  agentId: 'agt_2b8e',
  vars:    { plan: 'gold', region: 'eu', tenantId: 't_91' },
});

// In a tool handler
defineTool({
  name: 'fetch_quote',
  handler: async (input, ctx) => {
    const tenant = ctx.vars.tenantId;
    const plan   = ctx.vars.plan;
    const q = await pricingApi.quote(input, { tenant, plan });
    ctx.setVar('lastQuoteId', q.id);   // visible to subsequent tools and widgets
    return q;
  },
});

Vars are per-session, not per-workspace. Use them for user identity propagation, feature flags, tenant scoping, and any state that should survive across turns within one conversation but not leak across users.

Session lifecycle

StateWhat it means
activeTouched within the last hour. Messages stream as SSE.
idleNo activity for an hour, but history is still in memory. Resuming is instant.
hibernatedNo activity for 24h. History persisted to durable storage; first resume after this has a small wake-up cost (~200ms).
closedExplicitly ended via an.sessions.close(id) or by the agent emitting a handoff event. Attachments are deleted; messages remain queryable from the audit log.

Listing & querying

// list recent sessions for one agent
const recent = await an.sessions.list({ agentId: 'agt_2b8e', since: '2026-05-10T00:00:00Z' });

// get all messages from a single session
const history = await an.sessions.messages(sessionId);

// close a session explicitly
await an.sessions.close(sessionId);

The workspace dashboard surfaces session counts, average turn count, common handoff reasons, and per-version metrics. For programmatic analytics, query an.sessions.list with date ranges + an.sessions.messages per session.