createLilyAgent reference
createLilyAgent is the entry point of the SDK. It creates and initializes an agent and resolves to a LilyAgentHandle you drive from your code.
function createLilyAgent(opts?: CreateAgentOptions): Promise<LilyAgentHandle>
It is asynchronous — it initializes the engine (loading the WebAssembly module in the browser, or selecting a backend on Node) before resolving. apiKey is required at runtime; creating an agent without one throws createLilyAgent: apiKey required.
import { createLilyAgent } from '@poolot/lily-web'
const agent = await createLilyAgent({
apiKey: process.env.POOLOT_API_KEY,
model: 'poolot-standard',
})
Options
Common (browser and Node)
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | — (required) | Your Pathvela auth token — a signed-in user token or a programmatic sk-lily-… key. Sent as Authorization: Bearer <apiKey>. |
baseUrl | string | /api/agent-server | Where agent requests go. The default is a same-origin path, so route it through your own proxy — or point it straight at the Pathvela backend (https://agent.pathvela.com). |
model | string | "poolot-standard" | The model tier: poolot-standard, poolot-pro, poolot-vision, or poolot-mini. |
systemPrompt | string | — | A leading instruction that stays stable for the agent's life (cache-friendly). |
initialMessages | LilyMessage[] | — | A conversation to start from. |
autoEnvironment | boolean | true | Inject a system note describing the runtime's capabilities. |
telemetry | TelemetryOptions | false | on, silent | Error reporting. Never sends prompt or response content; stays silent until a dsn is set. |
Engine loading
| Option | Type | Default | Description |
|---|---|---|---|
assetBase | string | — | Folder you serve the engine's files from (see Installation). |
cdnFallback | boolean | see note | Fall back to a pinned CDN copy of the engine if local assets aren't found. |
version | string | — | Pin the engine version. |
Note on
cdnFallback: the shipped type comment documents the default astrue, while the package README lists it asfalse. Set it explicitly if it matters to you, and serve your own assets withassetBasein production.
Filesystem, storage & exec (both environments)
These work in the browser and on Node — see Browser vs Node for how each is backed in-page.
| Option | Type | Default | Description |
|---|---|---|---|
root | string | process.cwd() (Node) | Folder the file tools are confined to. In the browser, a named OPFS subfolder that paths cannot escape. |
project | string | "default" | Scopes stored records (research, memory, audit) — disk records on Node, IndexedDB in the browser. |
exec | { javascript?; python?; pythonBackend? } | both off | Enable code-execution tools. In the browser these run in a sandbox. pythonBackend is "auto" | "pyodide" | "system" (default "auto"). |
execTimeoutMs | number | 30000 | Timeout for a single tool execution. |
Node / harness-only
These are ignored in the browser.
| Option | Type | Default | Description |
|---|---|---|---|
dataDir | string | ~/.lily/sdk | Filesystem path for research, memory, and audit records. Honors $LILY_SDK_HOME. In the browser these live in IndexedDB scoped by project instead. |
backend | "harness" | "wasm" | auto | Force the engine. Omitted = harness if usable, else WebAssembly. |
autoDownload | boolean | false | Download the harness (~90 MB) if none is installed. |
harnessBinary | string | — | Explicit harness path (overrides discovery and LILY_BINARY). |
harnessVersion | string | — | Pin the harness release. |
onProgress | (p) => void | — | Harness download/install progress callback. |
sessionId | string | — | Resume an existing harness session. |
cwd | string | — | Working directory for the harness backend. |
provider | string | — | Bring-your-own-key provider (harness only; requires the byok entitlement). |
env | Record<string, string> | — | Extra environment for the harness subprocess. |
The agent handle
createLilyAgent resolves to a LilyAgentHandle:
interface LilyAgentHandle {
environment: LilyEnvironment
runDetailed(messages: LilyMessage[], opts?: LilyRunOptions): Promise<LilyRunResult>
continue(input?: string | LilyMessage, opts?: LilyRunOptions): Promise<LilyRunResult>
run(messages: LilyMessage[], maxSteps?: number): Promise<string>
runStreaming(
messages: LilyMessage[],
onToken: (text: string) => void,
onEvent?: (event: LilyEvent) => void,
maxSteps?: number,
): Promise<string>
complete(messages: LilyMessage[]): Promise<string>
snapshot(): { status: string; iterations: number; messages: LilyMessage[]; usage: LilyUsage }
restore(messages: LilyMessage[]): number
getMe(): Promise<{ userId: string; email?: string; name?: string; tier: string; balance: number; capabilities?: { byok: boolean; providers: 'any' | 'poolot' } }>
abort(): void
}
Methods
runDetailed(messages, opts?)— run the agent over a conversation and get the fullLilyRunResult: text, status, usage, tool calls, and the resulting messages. This is the method to reach for when you want everything.run(messages, maxSteps?)— a convenience wrapper that resolves to just the output text.runStreaming(messages, onToken, onEvent?, maxSteps?)— likerun, but streams generated text throughonTokenand lifecycle events throughonEvent. Resolves to the final text.complete(messages)— a single completion with no tool loop.continue(input?, opts?)— run another turn, keeping the agent's accumulated history. Pass a string or aLilyMessage.snapshot()— capture the current status, iteration count, messages, and usage.restore(messages)— reset the conversation to a given message list; returns the count restored.getMe()— fetch the account identity, tier, credit balance, and capabilities.abort()— cancel the run in flight, including any tool work.
There is no
sendMessage/getMessagesmethod. You send by passing amessagesarray (orcontinue(input)), and you read the conversation fromsnapshot().messagesorLilyRunResult.messages.
Run options
runDetailed, runStreaming, and continue accept a LilyRunOptions:
| Field | Type | Default | Description |
|---|---|---|---|
maxSteps | number | — | Max iterations of the agent loop. |
maxTokens | number | — | Token budget for the run. |
maxCalls | number | — | Max tool/model calls. |
maxConsecutiveFailures | number | 5 | Stop after this many failures in a row. |
maxRepeatedCalls | number | 3 | Stop after the same call repeats this many times. |
onToken | (text) => void | — | Streamed output tokens. |
onEvent | (event: LilyEvent) => void | — | Lifecycle events. |
onReasoning | (text) => void | — | Reasoning tokens. Works on both backends for reasoning-capable model routes; the default chat route emits none. |
LilyRunResult
interface LilyRunResult {
status: 'completed' | 'aborted' | 'max_steps' | 'failed'
| 'budget_tokens' | 'budget_calls' | 'too_many_failures' | 'repeated_tool_call'
outputText: string
messages: LilyMessage[]
toolCalls: LilyToolCall[]
usage: LilyUsage | null
iterations: number
durationMs: number
result?: unknown // from a run-completing tool
completedBy?: string | null
reasoning?: string // both backends, reasoning-capable routes only
sessionId?: string | null // harness only
}
usage is LilyUsage — { inputTokens, outputTokens, cachedInputTokens, calls } — or null when the backend reported none (deliberately distinct from all-zeroes).
See also
- Events and messages —
LilyMessageand theLilyEventunion. - TypeScript types — the full exported surface.
- Examples — using these methods in real code.