Sessions & Agent
client.sessions, SessionHandle, runTurn, interrupts, and SDK 3.0 agent realtime patterns.
On this page10
The agent API is client.sessions plus SessionHandle (SDK 3.0). HTTP
projections are the source of truth; WebSocket carries invalidation signals and optional
streaming deltas.
Session CRUD
| Method | Description |
|---|---|
list(options?) | AsyncGenerator<Session> — filter with PageOptions |
get(id, options?) | Session metadata |
create(vaultId, title?, options?) | New session |
delete(id, options?) | Delete |
archive(id, options?) | Archive |
dispatch(params) | One-shot dispatch (202) without a long-lived handle |
handle(sessionId, vaultId, options?) | Open a SessionHandle |
listTasks / createTask / updateTask / deleteTask | Task management |
stop(…) | Stop agent run |
const session = await client.sessions.create(vaultId, 'Recon session')
const handle = await client.sessions.handle(session.id, vaultId, {
realtime: true, // open WebSocket (default true)
messageLimit: 50,
})wsUrl in local dev
When the HTTP API is on http://localhost:3000 but the WS worker listens on
ws://localhost:3002, set it on the client:
const client = new TrickestClient({
token: process.env.TRICKEST_TOKEN,
baseUrl: 'http://localhost:3000',
wsUrl: 'ws://localhost:3002',
})SessionHandle
Returned by client.sessions.handle(sessionId, vaultId, options).
| Member | Description |
|---|---|
vaultId, sessionId | Identifiers |
state | Latest SessionStateProjection or null before first refresh |
refresh(opts?) | Manual GET …/state refetch |
subscribe(fn) | Called on each projection update; returns unsubscribe |
subscribeStreaming(fn) | Per-character / tool-input deltas (UI paths) |
send(params) | Dispatch turn → { runId, sessionId, jobId } (202) |
abort(opts?) | Stop current run |
resolveInterrupt(decision, opts?) | Answer permission / approval / ask_user |
getActiveInterrupt() | Pending interrupt or null |
close() | Tear down WS + subscribers |
SessionSendParams
await handle.send({
prompt: 'List my recent workflows',
agentMode: 'agent', // 'agent' | 'plan' | 'ask'
modelId: 'google/gemini-3-flash',
workflowId,
spaceId,
editorMode: 'workflow', // 'workflow' | 'app' | 'agent' | 'database'
autoApprove: true,
signal: abortController.signal,
timeout: 120_000,
})send resolves on 202 Accepted — the answer arrives via subscribe, not the
send promise.
Interactive loop
const handle = await client.sessions.handle(sessionId, vaultId, { realtime: true })
const off = handle.subscribe((projection, change) => {
console.log('phase', projection.run.phase, change.sources)
})
await handle.refresh()
const { runId } = await handle.send({
prompt: 'Summarize open findings',
autoApprove: true,
})
// … wait for terminal phase in subscribe callback …
off()
handle.close()Streaming frames (UI)
handle.subscribeStreaming((frame) => {
if (frame.type === 'text_stream_delta') process.stdout.write(frame.delta)
if (frame.type === 'text_stream_end') console.log('\n', frame.finalStatus)
})Frame types include text_stream_delta, thinking_stream_delta,
tool_input_stream_delta, text_stream_end.
runTurn — headless one-shot
For CLI, bench, and scripts that want "send prompt, wait, return answer":
import { runTurn } from '@trickest/sdk'
const handle = await client.sessions.handle(sessionId, vaultId)
const summary = await runTurn(handle, {
prompt: 'Build a recon workflow for example.com',
agentMode: 'agent',
}, { timeoutMs: 120_000 })
if (summary.success) {
console.log(summary.answer)
for (const tc of summary.toolCalls) {
console.log(tc.name, tc.status, tc.result)
}
} else {
console.error(summary.phase, summary.errorMessage)
}
handle.close()runTurn does not close the handle — call handle.close() yourself.
Interrupts
const interrupt = handle.getActiveInterrupt()
if (interrupt?.type === 'permission') {
await handle.resolveInterrupt({
type: 'permission',
id: interrupt.id,
behavior: 'allow',
rememberFor: 'session',
})
}InterruptResolution types: permission, approval, plan_approval, mode_switch,
ask_user.
Exported projection types
Re-exported from @trickest/sdk for typing UI and automation:
SessionStateProjection, MessageProjection, ToolCallProjection, RunProjection,
StreamingFrame, InterruptProjection, and related enums — use your IDE on
import type { … } from '@trickest/sdk'.