Session Management
InteractiveSession is the event-driven session every Robota surface is built on. It keeps the
conversation, runs the built-in tools under a permission mode, tracks the context window, and can save
and resume itself through a session store.
Events and context
import { InteractiveSession } from '@robota-sdk/agent-framework';
import { AnthropicProvider } from '@robota-sdk/agent-provider-anthropic';
const provider = new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY });
const session = new InteractiveSession({
cwd: process.cwd(),
provider,
permissionMode: 'default',
});
session.on('text_delta', (delta) => process.stdout.write(delta));
session.on('context_update', (state) => {
console.log(`Context: ${state.usedPercentage.toFixed(1)}% used`);
});
session.on('complete', ({ response }) => {
console.log('\n--- Complete response ---');
console.log(response);
});
// submit() resolves after the turn when the session is idle; while a turn runs, a new
// submission is queued and submit() resolves at once. `completed` settles with this turn's result.
const { completed } = await session.submit('What is the architecture of this project?');
const result = await completed;
console.log(result.response.length, 'characters');
await session.submit('Show me the main entry point.');
// Compact the conversation, with instructions for what to keep
const state = session.getContextState();
if (state.usedPercentage > 70) {
await session.compactContext('Focus on the architecture discussion');
}
console.log(`History entries: ${session.getFullHistory().length}`);
console.log(`Mode: ${session.getSession().getPermissionMode()}`);
// Change the permission mode for the next tool calls
session.getSession().setPermissionMode('acceptEdits');
// Abort the running turn
setTimeout(() => session.abort(), 30000);In default mode a tool call that needs approval emits permission_request; answer it with
session.resolvePermission(id, result). If nothing is listening, the call is denied. The
permissions guide describes each mode.
Without a projectAccess decision the session runs Restricted: its tools still work inside cwd,
but it does not load the project's AGENTS.md/CLAUDE.md, settings or memory. The
agent-framework README describes how a host passes project
access.
Persist and resume
Give the session a store and it saves itself after each turn. createUserSessionStore() keeps the
records in a directory you choose.
import { InteractiveSession, createUserSessionStore } from '@robota-sdk/agent-framework';
import { AnthropicProvider } from '@robota-sdk/agent-provider-anthropic';
import { homedir } from 'node:os';
import { join } from 'node:path';
const sessionStore = createUserSessionStore(join(homedir(), '.my-agent', 'sessions'));
const provider = new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY });
const session = new InteractiveSession({
cwd: process.cwd(),
provider,
sessionStore,
sessionName: 'my-task',
});
await session.submit('What is the architecture?');
const sessionId = session.getSession().getSessionId();
session.setName('architecture-review');
console.log(session.getName()); // 'architecture-review'
// Resume: restores the history and the model's context
const resumed = new InteractiveSession({
cwd: process.cwd(),
provider,
sessionStore,
resumeSessionId: sessionId,
});
// Fork: a new session ID that starts from the same history
const forked = new InteractiveSession({
cwd: process.cwd(),
provider,
sessionStore,
resumeSessionId: sessionId,
forkSession: true,
});examples/telegram-bot resumes one saved session per chat this way.
Reading the store
load() and list() report why a record cannot be used instead of hiding it: each outcome is
valid, missing, corrupt (present but not a session record) or unsupported (written by a
version this one does not read). Only a valid outcome carries the record.
import { createUserSessionStore } from '@robota-sdk/agent-framework';
import { homedir } from 'node:os';
import { join } from 'node:path';
const sessionStore = createUserSessionStore(join(homedir(), '.my-agent', 'sessions'));
for (const entry of sessionStore.list()) {
const { outcome } = entry;
if (outcome.status === 'valid') {
console.log(entry.id, outcome.record.name ?? '(unnamed)', outcome.record.updatedAt);
} else {
console.log(entry.id, `cannot be resumed: ${outcome.status}`);
}
}
const outcome = sessionStore.load('session-id');
if (outcome.status === 'valid') {
console.log(`${outcome.record.messages.length} messages`);
}A project session store (createProjectSessionStore(), built on a trusted workspace's state
directories) also keeps an append-only log and can rebuild a session from it when the saved record is
missing. A damaged log is reported as corrupt rather than partly replayed.
From the CLI
# Continue the most recent session
robota -c
# Resume a session by name or ID
robota -r my-feature
# Fork into a new session with the same history
robota -c --fork-session
robota -r my-feature --fork-session
# Name a new session
robota --name "auth-refactor"Inside the terminal UI, /resume opens a session picker and /rename <name> renames the current
session. The session name appears on the input box border, in the terminal title and in the status
line.