History
Persist Text2SQL conversations through ContextEngine stores
Text2SQL no longer exposes a separate History class. Conversation persistence
comes from the ContextStore backing the ContextEngine you wire into the
chat() helper from @deepagents/context. That helper writes turns to the
store; toSql() does not persist conversation history.
Choose a Store
Use in-memory storage for tests and short-lived processes:
import { groq } from '@ai-sdk/groq';
import {
ContextEngine,
InMemoryContextStore,
agent,
createBashTool,
createDockerSandbox,
errorRecoveryGuardrail,
npm,
user,
} from '@deepagents/context';
import { createSqlCommandHooks, instructions } from '@deepagents/text2sql';
const model = groq('openai/gpt-oss-20b');
const store = new InMemoryContextStore();
const backend = await createDockerSandbox({
installers: [npm('@deepagents/text2sql', { ensureRuntime: true })],
env: {
TEXT2SQL_ADAPTERS: '/workspace/text2sql-adapters.ts',
},
});
const sandbox = await createBashTool({
sandbox: backend,
...createSqlCommandHooks({ adapters }),
});
const context = new ContextEngine({
store,
chatId: 'chat-123',
userId: 'user-456',
});
const indexResult = await sandbox.sandbox.executeCommand('sql index');
if (indexResult.exitCode !== 0) throw new Error(indexResult.stderr);
const manifest = JSON.parse(indexResult.stdout) as { fragmentsPath: string };
const fragments = JSON.parse(
await sandbox.sandbox.readFile(manifest.fragmentsPath),
);
context.set(...instructions(), ...fragments);
const ai = agent({
name: 'sql-assistant',
sandbox,
model,
context,
guardrails: [errorRecoveryGuardrail],
maxGuardrailRetries: 3,
});Use SQLite when you want persistence across server restarts:
import { groq } from '@ai-sdk/groq';
import {
ContextEngine,
SqliteContextStore,
agent,
createBashTool,
createDockerSandbox,
errorRecoveryGuardrail,
npm,
user,
} from '@deepagents/context';
import { createSqlCommandHooks, instructions } from '@deepagents/text2sql';
const model = groq('openai/gpt-oss-20b');
const store = new SqliteContextStore('./text2sql-history.sqlite');
const backend = await createDockerSandbox({
installers: [npm('@deepagents/text2sql', { ensureRuntime: true })],
env: {
TEXT2SQL_ADAPTERS: '/workspace/text2sql-adapters.ts',
},
});
const sandbox = await createBashTool({
sandbox: backend,
...createSqlCommandHooks({ adapters }),
});
const context = new ContextEngine({
store,
chatId: 'chat-123',
userId: 'user-456',
});
const indexResult = await sandbox.sandbox.executeCommand('sql index');
if (indexResult.exitCode !== 0) throw new Error(indexResult.stderr);
const manifest = JSON.parse(indexResult.stdout) as { fragmentsPath: string };
const fragments = JSON.parse(
await sandbox.sandbox.readFile(manifest.fragmentsPath),
);
context.set(...instructions(), ...fragments);
const ai = agent({
name: 'sql-assistant',
sandbox,
model,
context,
guardrails: [errorRecoveryGuardrail],
maxGuardrailRetries: 3,
});Both history examples assume adapters is the host-side adapter map and that
/workspace/text2sql-adapters.ts exists inside the sandbox with the same map
as its default export. Mount, upload, or write that module before calling
chat().
Continuing a Conversation
Reuse the same store, chatId, and userId for follow-up turns. In practice,
that usually means reusing the same agent instance or creating a new one with
a new ContextEngine built from the same thread identity. Pass only the new
incoming message for that turn; earlier turns are loaded from the store
automatically:
import { chat } from '@deepagents/context';
await context.continue(user('Show me sales data'));
for await (const _ of await chat(ai, { generateTitle: true })) {
// drain or stream chunks to your UI
}
await context.continue(user('Filter that to last quarter'));
for await (const _ of await chat(ai)) {
// drain or stream chunks to your UI
}Create a new chatId when you want to start a separate thread that should not
reuse prior context.
What Gets Persisted
Persistence is owned by the chat() helper from @deepagents/context, not by
Text2Sql. When you call chat(ai), that helper writes through the underlying
ContextEngine:
- the incoming user or assistant message for that turn (queued by
context.continue(...)) - the finalized assistant reply for the turn
- chat-level data such as the generated title and tracked usage
That gives later chat() calls access to prior turns without a separate history
API on Text2Sql itself. Direct toSql() calls skip the store entirely.
Direct Store Access
If you need to inspect or manage persisted chats directly, work with the
@deepagents/context storage layer rather than expecting Text2Sql helper
classes.
See:
Best Practices
- Share one store across requests when you want continuity across server restarts.
- Keep
chatIdstable inside a thread so follow-up questions resolve prior turns. - Use a fresh
chatIdfor a new task instead of mixing unrelated analyses together. - Prefer
SqliteContextStorein production when process memory alone is not durable enough.