createSeepient()
Create a persistent agent with session memory, provider switching, and abort support. Unlike askSeepient() which is stateless, an agent maintains conversation history across calls.
Signature
function createSeepient(options?: CreateSeepientOptions): Promise<Seepient>WARNING
createSeepient() is async -- always await it. The agent needs to resolve the provider configuration and optionally load persisted session state before it is ready.
Quick example
import { createSeepient } from "seepient";
const agent = await createSeepient({
model: "gpt-5.4",
systemPrompt: "You are a concise coding assistant.",
});
const reply = await agent.chat("What is a closure?");
console.log(reply.text);
// Context is preserved across calls
const followUp = await agent.chat("Show me an example in TypeScript");
console.log(followUp.text);
// Check cumulative usage
console.log(agent.getUsage());Parameters
Stateless Embedding
For multi-tenant workers and cloud functions requiring full state injection (audit, policy, capability ledger, sessions), use createSeepient or askSeepient. See Stateless Workers for full architecture details.
options (optional)
| Name | Type | Default | Description |
|---|---|---|---|
model | string | Provider default | Model identifier, e.g. "gpt-5.4", "claude-sonnet-4-6-20260320" |
provider | string | "openai" | Feeds the permission pipeline's modelProviderClass audit label |
purpose | Purpose | "text" | Purpose routing hint (see Purpose reference for all 15 supported values) |
tier | "efficient" | "standard" | "complex" | (none) | Model capability tier hint |
providerAccount | string | (none) | Active provider account name (persisted and restored with session state) |
providers | Record<string, any> | (none) | Programmatic provider account definitions for isolated or in-memory runtimes |
modelAssignments | PurposeModelMap | (none) | Custom purpose-and-tier model routing assignments |
credentials | CredentialStore | Local/env store | Injected credential store (e.g. MemoryCredentialStore for isolated runtimes) |
overlayFile | string | (none) | Config overlay file path, or ":memory:" for zero-disk ephemeral agents |
adapter | InferenceAdapter | AggregateInferenceAdapter | Custom inference adapter or test double |
override | { providerAccount?, model?, thinkingLevel? } | (none) | Per-instance model and account override |
runtime | ProviderRuntime | getDefaultProviderRuntime() | Provider runtime instance managing credentials, configurations, and inference adapters |
principalId | string | "sdk-user" | Identity of the calling principal/user, threaded into audit events and capability grants |
sessionId | string | Auto-generated UUID | Explicit session ID (^[a-zA-Z0-9_-]+$) for tracking and persistence |
auditStore | AuditStore | Local file audit store | Injected audit store for recording action lifecycle events |
policyStore | PolicyStore | Local file policy store | Injected policy store for grant snapshots and mutations |
capabilityLedger | CapabilityLedger | Local file capability ledger | Injected ledger for capability lease consumption and revocations |
systemPrompt | string | "You are a helpful assistant." | System prompt prepended to every conversation |
tools | (string | UserToolDefinition | AnyToolRegistration)[] | All built-in | Tool names, group constants, or custom tool registrations (trustedHostTool, preparedTool, brokerConnector) |
consentMode | ConsentMode | "edit-enabled" | Permission consent mode ("ask-everything", "edit-enabled", "autonomous") |
deploymentCeiling | CapabilitySet | Capability[] | (none) | Maximum capability lease permitted for any execution |
principalPolicy | CapabilitySet | Capability[] | (none) | Pre-granted capabilities for the calling principal |
approveTool | ApproveToolFn | (none) | Interactive tool approval callback |
approvalBroker | ApprovalBroker | (none) | Custom approval broker for permission escalation |
commitHelper | CommitHelper | Native helper | Custom or mock exact-commit verifier helper |
network | BrokerNetworkAdapter | Standard adapter | Custom broker network adapter with SSRF / IP pinning rules |
cwd | string | process.cwd() | Workspace directory for file operations and skill discovery |
skills | string[] | boolean | true | Specific skill names, true for all, or false to disable skill scanning and catalog injection |
maxSteps | number | 10 | Maximum agent loop iterations per call |
persist | string | PersistenceBackend | PersistenceConfig | (none) | Directory path, backend instance, or config object (e.g. { type: "memory" }). File persistence writes are atomic (tmp + rename). |
hooks | Hooks | (none) | Lifecycle callbacks |
middleware | Middleware[] | (none) | Request/response pipeline functions (auth, logging, rate limiting, etc.) |
metadata | Record<string, unknown> | {} | Adapter-specific metadata passed to middleware via PipelineContext |
config | Record<string, unknown> | {} | Extra config passed to tool handlers |
Partial State Store Injection Warning
For fully stateless zero-disk execution, all three permission contracts (auditStore, policyStore, and capabilityLedger) must be injected together along with persist. If 1 or 2 permission stores are injected, the SDK logs a warning ([seepient] WARNING: Partial state store injection detected...) and falls back missing stores to the local filesystem (~/.seepient or ./.seepient).
Permission Pipeline Always Active
The permission pipeline is always active across all SDK entry points (createSeepient, askSeepient). Every tool execution is evaluated by policy and recorded in the audit trail.
::: note Tool Registration and Declaration Validation Custom host callbacks bind into the execution boundary at agent creation time. setTools(tools: string[]) accepts tool names only by design and enables/disables already registered tools; custom registrations must be supplied at composition time in createSeepient({ tools: [...] }).
When declaring trustedHostTool, declarations fail closed with descriptive errors if unknown effects are supplied or required descriptor fields are omitted:
- Allowed
effects:"network-egress","secret-use","model-egress"(in that order). "network-egress"requiresdestinations: string[]."secret-use"requiressecretRefs: string[]."model-egress"requiresdataClasses: string[]. :::
Seepient interface
The object returned by createSeepient():
Conversation & Lifecycle Methods
| Method | Signature | Description |
|---|---|---|
chat | (message: string) => Promise<AgentResponse> | Send a message and get the full response. Context is preserved. |
chatStream | (message: string, options?: Omit<AskSeepientOptions, "stream" | "signal">) => Promise<AskSeepientStreamResult> | Send a message with streaming output. Returns async iterables and SSE helpers. |
switchProvider | (accountOrModel: string, model?: string) => Promise<void> | Switch the provider account (and optionally model) used for subsequent calls. One argument switches the model only. |
setSystemPrompt | (prompt: string) => void | Update the system prompt. Replaces the existing system message in history. |
setTools | (tools: string[]) => void | Update active tools by name. Custom tool registrations cannot be added dynamically via setTools. |
abort | () => void | Abort the currently running chat() or chatStream() call. |
clear | () => void | Clear conversation history. Keeps the system prompt. |
getHistory | () => Message[] | Return a copy of the full conversation history. |
getUsage | () => CumulativeUsage | Return cumulative token usage across all calls. |
flushAudit | () => Promise<number> | Flush pending terminal audit events (returns flushed count). |
close | () => Promise<void> | Abort any running calls and flush buffered audit events. |
dispose | () => Promise<void> | Closes agent, flushes audit logs, and removes all runtime listeners. |
Provider Management Methods (Spec 013 / Spec 021)
| Method | Signature | Description |
|---|---|---|
listProviders | () => Promise<string[]> | List distinct upstream provider names (e.g. ["anthropic", "openai"]). |
getCatalog | () => Promise<readonly AvailableModel[]> | Return all discovered and declared models across all configured accounts. |
getAssignments | () => PurposeModelMap | Return current purpose-and-tier routing assignments. |
addProvider | (input: AccountInput) => Promise<SaveResult> | Add or update a provider account with credentials. |
removeProvider | (id: string, opts?: { force?: boolean }) => Promise<DeleteResult> | Delete a configured provider account. |
setAssignment | (purpose, tier, target) => Promise<SaveResult> | Assign a model to a purpose and tier. |
clearAssignment | (purpose, tier) => Promise<SaveResult> | Remove an assignment for a purpose and tier. |
resolve | (opts: { purpose, tier?, override? }) => Promise<ResolutionResult> | Preview how a turn will route without invoking the model. |
reload | () => Promise<{ revision: number }> | Force-reload provider configuration from the backing store. |
AgentResponse
Returned by chat():
interface AgentResponse {
text: string;
toolCalls: ToolCall[];
usage: Usage;
}CumulativeUsage
Returned by getUsage():
interface CumulativeUsage {
totalPromptTokens: number;
totalCompletionTokens: number;
totalCost: number;
requestCount: number;
}Examples
Basic multi-turn conversation
import { createSeepient } from "seepient";
const agent = await createSeepient({
systemPrompt: "You are a helpful travel advisor.",
});
const r1 = await agent.chat("What are the top 3 things to do in Tokyo?");
console.log(r1.text);
const r2 = await agent.chat("Which of those is best for families?");
console.log(r2.text);
// The agent remembers the full conversation
console.log(`History: ${agent.getHistory().length} messages`);
console.log(`Total requests: ${agent.getUsage().requestCount}`);Streaming responses
Use chatStream() for real-time output:
import { createSeepient } from "seepient";
const agent = await createSeepient({
model: "claude-sonnet-4-6-20260320",
provider: "anthropic",
});
const stream = await agent.chatStream("Explain transformers architecture", {
onText: (delta) => process.stdout.write(delta),
});
const text = await stream.fullText;
console.log(`\nTokens: ${(await stream.usage).totalTokens}`);Provider switching
Switch the active provider account or model mid-conversation:
import { createSeepient } from "seepient";
const agent = await createSeepient({ provider: "openai", model: "gpt-5.4" });
// Start with the default resolution for the model
const r1 = await agent.chat("What is the capital of France?");
console.log(r1.text);
// Switch to another configured provider account for the next turn
await agent.switchProvider("main", "claude-sonnet-4-6-20260320");
const r2 = await agent.chat("Tell me more about its history");
console.log(r2.text);TIP
switchProvider(account, model) targets a provider account from your configuration; with a single argument it switches the model only. The conversation history is preserved, so context carries over seamlessly.
Session persistence
Persist conversation history so the agent can resume across process restarts:
import { createSeepient } from "seepient";
// Option 1: File-based persistence (just pass a path)
const agent = await createSeepient({
persist: "./sessions/my-agent",
});
// Option 2: In-memory persistence (great for testing)
const agent2 = await createSeepient({
persist: { type: "memory" },
});
// Option 3: Explicit file config
const agent3 = await createSeepient({
persist: { type: "file", path: "/var/data/sessions" },
});
await agent.chat("My name is Alice");
await agent.chat("I'm working on a React project");
// In a new process, recreate the agent with the same persist path:
// const agent2 = await createSeepient({ persist: "./sessions/my-agent" });
// The conversation history will be loaded automatically.Custom persistence backends
Register custom backends (Redis, SQLite, encrypted storage, etc.) with registerBackend:
import { registerBackend, createSeepient, type PersistenceBackend, type SessionData } from "seepient";
class RedisBackend implements PersistenceBackend {
readonly __persistenceBackend = true as const;
constructor(private url: string) { /* connect */ }
async save(sessionId: string, data: SessionData): Promise<void> {
await redis.set(`session:${sessionId}`, JSON.stringify(data));
}
async load(sessionId: string): Promise<SessionData | null> {
const raw = await redis.get(`session:${sessionId}`);
return raw ? JSON.parse(raw) : null;
}
async delete(sessionId: string): Promise<void> {
await redis.del(`session:${sessionId}`);
}
async list(): Promise<string[]> {
const keys = await redis.keys("session:*");
return keys.map((k) => k.replace("session:", ""));
}
}
// Register once at startup
registerBackend("redis", (config) => new RedisBackend(config.url as string));
// Then use by type name
const agent = await createSeepient({
persist: { type: "redis", url: "redis://localhost:6379" },
});Pass a backend instance directly
const myBackend: PersistenceBackend = {
readonly __persistenceBackend: true as const,
async save(id, data) { /* custom logic */ },
async load(id) { return null; },
async delete(id) {},
async list() { return []; },
};
const agent = await createSeepient({ persist: myBackend });Dynamic tools
Change the available tools at runtime:
import { createSeepient } from "seepient";
const agent = await createSeepient({
tools: ["core"], // Only shell, read_file, write_file, datetime
});
await agent.chat("Read ./package.json and tell me the version");
// Add web search for the next query
agent.setTools(["core", "web_search"]);
await agent.chat("Now search for the latest version of this package on npm");Abort a running call
import { createSeepient } from "seepient";
const agent = await createSeepient();
// Start a long-running request
const promise = agent.chat("Analyze all files in this repository");
// Abort after 5 seconds
setTimeout(() => agent.abort(), 5000);
try {
const result = await promise;
} catch (err) {
console.log("Agent was aborted");
}INFO
abort() cancels the in-flight HTTP request to the LLM provider, not just the agent loop between steps. The AbortSignal propagates through to the underlying provider SDK (OpenAI, Anthropic, etc.), so network resources are released immediately.
Concurrency: Mutex acquisition ensures caller-owned lock release: chat() and chatStream() are serialized — a second call blocks until the first completes. This prevents concurrent mutations of the shared message history.
Inspect history
import { createSeepient } from "seepient";
const agent = await createSeepient();
await agent.chat("Hello");
await agent.chat("What can you do?");
const history = agent.getHistory();
for (const msg of history) {
console.log(`[${msg.role}] ${msg.content.slice(0, 80)}`);
}
// Clear to start fresh
agent.clear();
console.log(agent.getHistory().length); // 1 (just the system prompt)Persistence types
PersistenceBackend interface
interface PersistenceBackend {
/** Brand discriminator */
readonly __persistenceBackend: true;
save(sessionId: string, data: SessionData): Promise<void>;
load(sessionId: string): Promise<SessionData | null>;
delete(sessionId: string): Promise<void>;
list(): Promise<string[]>;
}PersistenceConfig
interface PersistenceConfig {
type: string; // "file", "memory", or custom registered type
[key: string]: unknown; // Backend-specific options (path, url, etc.)
}Built-in backends and factory functions:
createPersistenceBackend(config)-- Creates a backend from a config objectregisterBackend(type, factory)-- Registers a custom backend type
Middleware
Add cross-cutting concerns (logging, auth, rate limiting) to agent execution:
import {
createSeepient,
loggingMiddleware,
rateLimitMiddleware,
authMiddleware,
} from "seepient";
const agent = await createSeepient({
middleware: [
authMiddleware({
validate: (ctx) => !!ctx.metadata.apiKey,
errorMessage: "Missing API key",
}),
rateLimitMiddleware({
maxRequests: 60,
windowMs: 60_000,
keyExtractor: (ctx) => String(ctx.metadata.userId ?? "anonymous"),
}),
loggingMiddleware({
logRequest: true,
logResponse: true,
}),
],
metadata: { apiKey: process.env.MY_API_KEY, userId: "user-123" },
});Custom middleware
import type { Middleware } from "seepient";
const auditLog: Middleware = async (ctx, next) => {
console.log(`[audit] request ${ctx.requestId} started`);
const start = Date.now();
await next(); // Continue to the agent loop
console.log(`[audit] request ${ctx.requestId} finished in ${Date.now() - start}ms`);
};
const agent = await createSeepient({ middleware: [auditLog] });Related APIs
- askSeepient() -- Stateless one-shot execution (streaming via
stream: true) - Tools -- Built-in and custom tool reference