Skip to content

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

typescript
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

typescript
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)

NameTypeDefaultDescription
modelstringProvider defaultModel identifier, e.g. "gpt-5.4", "claude-sonnet-4-6-20260320"
providerstring"openai"Feeds the permission pipeline's modelProviderClass audit label
purposePurpose"text"Purpose routing hint (see Purpose reference for all 15 supported values)
tier"efficient" | "standard" | "complex"(none)Model capability tier hint
providerAccountstring(none)Active provider account name (persisted and restored with session state)
providersRecord<string, any>(none)Programmatic provider account definitions for isolated or in-memory runtimes
modelAssignmentsPurposeModelMap(none)Custom purpose-and-tier model routing assignments
credentialsCredentialStoreLocal/env storeInjected credential store (e.g. MemoryCredentialStore for isolated runtimes)
overlayFilestring(none)Config overlay file path, or ":memory:" for zero-disk ephemeral agents
adapterInferenceAdapterAggregateInferenceAdapterCustom inference adapter or test double
override{ providerAccount?, model?, thinkingLevel? }(none)Per-instance model and account override
runtimeProviderRuntimegetDefaultProviderRuntime()Provider runtime instance managing credentials, configurations, and inference adapters
principalIdstring"sdk-user"Identity of the calling principal/user, threaded into audit events and capability grants
sessionIdstringAuto-generated UUIDExplicit session ID (^[a-zA-Z0-9_-]+$) for tracking and persistence
auditStoreAuditStoreLocal file audit storeInjected audit store for recording action lifecycle events
policyStorePolicyStoreLocal file policy storeInjected policy store for grant snapshots and mutations
capabilityLedgerCapabilityLedgerLocal file capability ledgerInjected ledger for capability lease consumption and revocations
systemPromptstring"You are a helpful assistant."System prompt prepended to every conversation
tools(string | UserToolDefinition | AnyToolRegistration)[]All built-inTool names, group constants, or custom tool registrations (trustedHostTool, preparedTool, brokerConnector)
consentModeConsentMode"edit-enabled"Permission consent mode ("ask-everything", "edit-enabled", "autonomous")
deploymentCeilingCapabilitySet | Capability[](none)Maximum capability lease permitted for any execution
principalPolicyCapabilitySet | Capability[](none)Pre-granted capabilities for the calling principal
approveToolApproveToolFn(none)Interactive tool approval callback
approvalBrokerApprovalBroker(none)Custom approval broker for permission escalation
commitHelperCommitHelperNative helperCustom or mock exact-commit verifier helper
networkBrokerNetworkAdapterStandard adapterCustom broker network adapter with SSRF / IP pinning rules
cwdstringprocess.cwd()Workspace directory for file operations and skill discovery
skillsstring[] | booleantrueSpecific skill names, true for all, or false to disable skill scanning and catalog injection
maxStepsnumber10Maximum agent loop iterations per call
persiststring | PersistenceBackend | PersistenceConfig(none)Directory path, backend instance, or config object (e.g. { type: "memory" }). File persistence writes are atomic (tmp + rename).
hooksHooks(none)Lifecycle callbacks
middlewareMiddleware[](none)Request/response pipeline functions (auth, logging, rate limiting, etc.)
metadataRecord<string, unknown>{}Adapter-specific metadata passed to middleware via PipelineContext
configRecord<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" requires destinations: string[].
  • "secret-use" requires secretRefs: string[].
  • "model-egress" requires dataClasses: string[]. :::

Seepient interface

The object returned by createSeepient():

Conversation & Lifecycle Methods

MethodSignatureDescription
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) => voidUpdate the system prompt. Replaces the existing system message in history.
setTools(tools: string[]) => voidUpdate active tools by name. Custom tool registrations cannot be added dynamically via setTools.
abort() => voidAbort the currently running chat() or chatStream() call.
clear() => voidClear conversation history. Keeps the system prompt.
getHistory() => Message[]Return a copy of the full conversation history.
getUsage() => CumulativeUsageReturn 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)

MethodSignatureDescription
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() => PurposeModelMapReturn 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():

typescript
interface AgentResponse {
  text: string;
  toolCalls: ToolCall[];
  usage: Usage;
}

CumulativeUsage

Returned by getUsage():

typescript
interface CumulativeUsage {
  totalPromptTokens: number;
  totalCompletionTokens: number;
  totalCost: number;
  requestCount: number;
}

Examples

Basic multi-turn conversation

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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

typescript
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:

typescript
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

typescript
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

typescript
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

typescript
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

typescript
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 object
  • registerBackend(type, factory) -- Registers a custom backend type

Middleware

Add cross-cutting concerns (logging, auth, rate limiting) to agent execution:

typescript
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

typescript
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] });
  • askSeepient() -- Stateless one-shot execution (streaming via stream: true)
  • Tools -- Built-in and custom tool reference

Released under the Business Source License 1.1.