Skip to content

Sessions Specification

Scope

Owns the CLI session lifecycle for the Robota SDK. This package provides the Session class that wraps a Robota agent instance with permission-gated tool execution, hook-based lifecycle events, context window tracking, conversation compaction, and optional JSON file persistence via SessionStore. It is the primary runtime used by the CLI application (agent-cli) via the assembly layer (agent-sdk).

Boundaries

  • Does not own AI provider creation. Accepts a pre-constructed IAIProvider via injection.
  • Does not own tool implementations. Accepts pre-constructed IToolWithEventService[] via injection.
  • Does not own system prompt building. Accepts a pre-built systemMessage string.
  • Does not own configuration resolution or context loading. Those belong to agent-sdk.
  • Does not own the permission evaluation algorithm or hook execution engine. Those belong to @robota-sdk/agent-core (evaluatePermission, runHooks).

Architecture Overview

The package follows a modular structure with Session delegating to focused sub-components:

session.ts                -- Session class: orchestrates run loop, delegates to sub-components
permission-enforcer.ts    -- PermissionEnforcer: tool wrapping, permission checks, hooks, truncation
context-window-tracker.ts -- ContextWindowTracker: token usage tracking, auto-compact threshold
compaction-orchestrator.ts -- CompactionOrchestrator: conversation summarization via LLM
session-logger.ts         -- ISessionLogger interface + FileSessionLogger / SilentSessionLogger
session-store.ts          -- SessionStore: JSON file persistence for conversation sessions

Design patterns used:

  • Facade -- Session hides Robota agent creation, tool registration, permission wiring, and hook execution behind a single run() method.
  • Decorator -- Each tool is wrapped with a permission-checking proxy via PermissionEnforcer.wrapTools() before being registered with the Robota agent.
  • Strategy (injected) -- Permission approval can be handled by a TPermissionHandler callback, an injected promptForApproval function, or denied by default.
  • Composition -- Session delegates to PermissionEnforcer, ContextWindowTracker, and CompactionOrchestrator rather than implementing everything inline.
  • Null Object -- When no SessionStore is provided, persistence is silently skipped.

Dependency direction:

  • @robota-sdk/agent-sessions depends on @robota-sdk/agent-core only.
  • No dependency on @robota-sdk/agent-tools or @robota-sdk/agent-provider-anthropic.
  • Tool and provider assembly is the responsibility of the consuming layer (agent-sdk).

Type Ownership

Types owned by this package (SSOT):

TypeKindFileDescription
ISessionOptionsInterfacesession.tsConstructor options for Session (tools, provider, systemMessage)
TPermissionHandlerTypepermission-enforcer.tsAsync callback (toolName, toolArgs) => Promise<TPermissionResult>
TPermissionResultTypepermission-enforcer.tsboolean | 'allow-session'
ITerminalOutputInterfacepermission-enforcer.tsTerminal I/O abstraction (write, prompt, select, spinner)
ISpinnerInterfacepermission-enforcer.tsSpinner handle returned by ITerminalOutput.spinner()
IPermissionEnforcerOptionsInterfacepermission-enforcer.tsOptions for constructing PermissionEnforcer
ICompactionOptionsInterfacecompaction-orchestrator.tsOptions for constructing CompactionOrchestrator
ISessionLoggerInterfacesession-logger.tsPluggable session event logger interface
TSessionLogDataTypesession-logger.tsStructured log event data (Record<string, string | number | boolean | object>)
ISessionRecordInterfacesession-store.tsPersisted session record (id, cwd, timestamps, messages)

Types consumed from other packages (not owned here):

TypeSource
Robota@robota-sdk/agent-core
IAgentConfig@robota-sdk/agent-core
IAIProvider@robota-sdk/agent-core
IToolWithEventService@robota-sdk/agent-core
TPermissionMode@robota-sdk/agent-core
TToolArgs@robota-sdk/agent-core
THooksConfig@robota-sdk/agent-core
IHookInput@robota-sdk/agent-core
evaluatePermission@robota-sdk/agent-core
runHooks@robota-sdk/agent-core
TRUST_TO_MODE@robota-sdk/agent-core
TUniversalMessage@robota-sdk/agent-core

Public API Surface

ExportKindDescription
SessionClassWraps Robota agent with permissions, hooks, streaming, and persistence
PermissionEnforcerClassTool permission checking, hook execution, output truncation
ContextWindowTrackerClassToken usage tracking and auto-compact threshold
CompactionOrchestratorClassConversation compaction via LLM summary
SessionStoreClassJSON file persistence for session records (~/.robota/sessions/)
FileSessionLoggerClassJSONL file-based session event logger
SilentSessionLoggerClassNo-op session logger
ISessionOptionsInterfaceConstructor options for Session
TPermissionHandlerTypeCustom permission approval callback
TPermissionResultTypePermission decision result
ITerminalOutputInterfaceTerminal I/O abstraction
ISpinnerInterfaceSpinner handle
ISessionLoggerInterfacePluggable session event logger interface
TSessionLogDataTypeStructured log event data
ISessionRecordInterfacePersisted session record shape
IContextWindowStateTypeContext window usage state (re-exported from agent-core)

Key Session Methods

MethodSignatureDescription
run(message: string) => Promise<string>Send a message; returns AI response. Persists session if store exists.
getPermissionMode() => TPermissionModeReturns the active permission mode.
setPermissionMode(mode: TPermissionMode) => voidChanges the permission mode for future tool calls.
getSessionId() => stringReturns the stable session identifier.
getMessageCount() => numberReturns the number of completed run() calls.
clearHistory() => voidClears the underlying Robota conversation history and resets token usage.
getHistory() => TUniversalMessage[]Returns the current conversation history.
getContextState() => IContextWindowStateReturns real-time context window usage (tokens, percentage).
compact(instructions?: string) => Promise<void>Compresses conversation via LLM summary. Fires PreCompact/PostCompact hooks.
abort() => voidCancels the currently running run() call. No-op if not running.
isRunning() => booleanReturns true if a run() call is in progress.
getSessionAllowedTools() => string[]Returns tools that were session-approved ("Allow always").
clearSessionAllowedTools() => voidClears all session-scoped allow rules.

Key SessionStore Methods

MethodSignatureDescription
save(session: ISessionRecord) => voidPersist a session record to disk. Creates directory if needed.
load(id: string) => ISessionRecord | undefinedLoad a session by ID. Returns undefined if not found.
list() => ISessionRecord[]List all sessions, sorted by updatedAt descending.
delete(id: string) => voidDelete a session file. No-ops if not found.

Session Logging

The session log records structured events to a JSONL file for diagnostics and replay:

  • server_tool event -- Recorded when a server-managed tool (e.g., web search) executes during streaming. Includes the tool name and query.
  • pre_run event -- Recorded at the start of each run() call. Includes the provider name and webToolsEnabled flag.
  • assistant event -- Recorded after each assistant response. Includes historyStructure: an array with per-message metadata (role, contentLength, hasToolCalls, toolCallNames, metadata).
  • onServerToolUse callback wiring -- When session logging is enabled, the onServerToolUse callback from the provider is automatically wired to emit server_tool log events.

Extension Points

  1. ISessionOptions.terminal (required) -- Inject an ITerminalOutput implementation for permission prompts and UI output. The consuming layer provides either a real terminal (CLI print mode) or an Ink-based no-op (TUI mode).

  2. ISessionOptions.tools -- Inject any set of IToolWithEventService[]. The consuming layer (agent-sdk) provides the default 8 tools + agent-tool.

  3. ISessionOptions.provider -- Inject any IAIProvider. The consuming layer (agent-sdk) creates the appropriate provider from config.

  4. ISessionOptions.systemMessage -- Inject the pre-built system prompt string. The consuming layer (agent-sdk) builds this from AGENTS.md, CLAUDE.md, tool descriptions, and trust level.

  5. ISessionOptions.permissionHandler -- Inject a custom permission approval callback (used by Ink-based UI to show approval prompts in React components).

  6. ISessionOptions.promptForApproval -- Alternative approval function that receives the terminal handle.

  7. ISessionOptions.onTextDelta -- Streaming callback for real-time text output to the UI.

  8. ISessionOptions.onCompact -- Callback invoked when compaction occurs (auto or manual), receives the generated summary string.

  9. ISessionOptions.compactInstructions -- Custom instructions for the compaction summary prompt (e.g., extracted from CLAUDE.md "Compact Instructions" section).

  10. SessionStore constructor -- Accept a custom baseDir to redirect storage location (useful in tests).

Error Taxonomy

This package does not define a custom error hierarchy. All errors are thrown as standard Error instances. Error scenarios include:

Error ConditionThrown ByMessage Pattern
Tool permission deniedPermissionEnforcerReturns IToolResult with "Permission denied" (no throw)
Hook blocked toolPermissionEnforcerReturns IToolResult with "Blocked by hook: {reason}"
Tool execution errorPermissionEnforcerReturns IToolResult with error message (never throws)

The permission wrapper deliberately catches all errors and returns them as IToolResult objects to avoid corrupting the conversation history with unmatched tool_use/tool_result pairs.

Session.run() Error Recovery

When run() encounters an error (e.g., from the execution loop or provider), the Session must:

  1. Log the error — write an error event to the session logger with the error details
  2. Preserve history — conversation history up to the point of failure remains intact
  3. Remain usable — the session is not corrupted; the user can continue or retry
  4. Propagate the error — re-throw after logging so the caller can handle it (e.g., display an error message)

Class Contract Registry

Interface Implementations

No formal interface implementations. Session, PermissionEnforcer, ContextWindowTracker, CompactionOrchestrator, and SessionStore are standalone classes.

Inheritance Chains

None. Classes are standalone.

Cross-Package Port Consumers

Port (Owner)Consumer ClassLocation
Robota (agent-core)Sessionsrc/session.ts
IAIProvider (agent-core)Sessionsrc/session.ts
evaluatePermission (agent-core)PermissionEnforcersrc/permission-enforcer.ts
runHooks (agent-core)PermissionEnforcersrc/permission-enforcer.ts
runHooks (agent-core)Sessionsrc/session.ts (PostCompact)
runHooks (agent-core)CompactionOrchestratorsrc/compaction-orchestrator.ts

Test Strategy

Current Test Coverage

  • Session system prompt delivery -- 6 tests verifying system prompt is passed to Robota at both top-level and defaultModel.

Gaps

  • Session -- run(), permission mode switching, hook integration, streaming callback wiring, and session persistence are untested.
  • PermissionEnforcer -- wrapTools(), checkPermission(), session-scoped allow, tool truncation are untested.
  • ContextWindowTracker -- updateFromHistory(), shouldAutoCompact(), metadata vs fallback estimation are untested.
  • CompactionOrchestrator -- compact(), hook firing, prompt building are untested.
  • SessionStore -- Covered by agent-sdk/src/__tests__/session-store.test.ts (12 tests: save/load/list/delete/directory creation).
  • FileSessionLogger -- log(), file creation, JSONL formatting, error handling on read-only paths are untested.
  • SilentSessionLogger -- No-op behavior untested (trivial, low priority).
  • All classes should be testable with mock IAIProvider and mock ITerminalOutput injections.

Dependencies

Production (1)

  • @robota-sdk/agent-core -- Robota agent, permission system, hook system, core types

Released under the MIT License.