@deepagents/text2sql 0.39.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +130 -73
  2. package/dist/bin/sql.js +699 -0
  3. package/dist/index.d.ts +2 -3
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +3376 -3309
  6. package/dist/index.js.map +4 -4
  7. package/dist/lib/adapter-index.d.ts +35 -0
  8. package/dist/lib/adapter-index.d.ts.map +1 -0
  9. package/dist/lib/agents/question.agent.d.ts.map +1 -1
  10. package/dist/lib/agents/sql-transform-plugins.d.ts +3 -3
  11. package/dist/lib/agents/sql-transform-plugins.d.ts.map +1 -1
  12. package/dist/lib/agents/sql.agent.d.ts.map +1 -1
  13. package/dist/lib/instructions.d.ts +1 -1
  14. package/dist/lib/instructions.d.ts.map +1 -1
  15. package/dist/lib/sql-hooks.d.ts +15 -0
  16. package/dist/lib/sql-hooks.d.ts.map +1 -0
  17. package/dist/lib/sql-meta-hook.d.ts +20 -0
  18. package/dist/lib/sql-meta-hook.d.ts.map +1 -0
  19. package/dist/lib/sql-repair.d.ts +2 -0
  20. package/dist/lib/sql-repair.d.ts.map +1 -0
  21. package/dist/lib/sql-transform.d.ts +2 -0
  22. package/dist/lib/sql-transform.d.ts.map +1 -0
  23. package/dist/lib/sql.d.ts +5 -38
  24. package/dist/lib/sql.d.ts.map +1 -1
  25. package/dist/lib/synthesis/extractors/base-contextual-extractor.d.ts.map +1 -1
  26. package/dist/lib/synthesis/extractors/segmented-context-extractor.d.ts.map +1 -1
  27. package/dist/lib/synthesis/extractors/sql-extractor.d.ts.map +1 -1
  28. package/dist/lib/synthesis/index.js +59 -2
  29. package/dist/lib/synthesis/index.js.map +4 -4
  30. package/dist/lib/synthesis/synthesizers/breadth-evolver.d.ts.map +1 -1
  31. package/dist/lib/synthesis/synthesizers/depth-evolver.d.ts.map +1 -1
  32. package/dist/lib/synthesis/synthesizers/persona-generator.d.ts.map +1 -1
  33. package/package.json +8 -4
  34. package/dist/lib/agents/sql-command.d.ts +0 -11
  35. package/dist/lib/agents/sql-command.d.ts.map +0 -1
  36. package/dist/lib/demo.d.ts +0 -2
  37. package/dist/lib/demo.d.ts.map +0 -1
  38. package/dist/lib/sandbox.d.ts +0 -21
  39. package/dist/lib/sandbox.d.ts.map +0 -1
package/README.md CHANGED
@@ -32,18 +32,9 @@ Requires Node.js LTS (20+).
32
32
 
33
33
  ```typescript
34
34
  import { groq } from '@ai-sdk/groq';
35
- import { InMemoryFs } from 'just-bash';
36
35
  import pg from 'pg';
37
36
 
38
- import {
39
- ContextEngine,
40
- InMemoryContextStore,
41
- createBashTool,
42
- createRoutingSandbox,
43
- createVirtualSandbox,
44
- user,
45
- } from '@deepagents/context';
46
- import { Text2Sql, sqlSandboxExtension } from '@deepagents/text2sql';
37
+ import { Text2Sql } from '@deepagents/text2sql';
47
38
  import {
48
39
  Postgres,
49
40
  columnValues,
@@ -59,12 +50,6 @@ const pool = new pg.Pool({
59
50
  connectionString: process.env.DATABASE_URL,
60
51
  });
61
52
 
62
- const store = new InMemoryContextStore();
63
- const engine = new ContextEngine({
64
- store,
65
- chatId: 'chat-123',
66
- userId: 'user-456',
67
- });
68
53
  const adapter = new Postgres({
69
54
  execute: async (sql) => {
70
55
  const result = await pool.query(sql);
@@ -80,19 +65,11 @@ const adapter = new Postgres({
80
65
  columnValues(),
81
66
  ],
82
67
  });
83
- const sandbox = await createBashTool({
84
- sandbox: await createRoutingSandbox({
85
- backend: await createVirtualSandbox({ fs: new InMemoryFs() }),
86
- hostExtensions: [sqlSandboxExtension({ main: adapter })],
87
- }),
88
- });
89
68
 
90
69
  const text2sql = new Text2Sql({
91
70
  version: 'v1',
92
71
  model: groq('openai/gpt-oss-20b'),
93
- sandbox,
94
72
  adapters: { main: adapter },
95
- context: engine,
96
73
  });
97
74
 
98
75
  // Generate SQL
@@ -105,32 +82,106 @@ console.log(sql);
105
82
 
106
83
  The adapter-map key (`main` here) is the adapter name. Reuse that same key in
107
84
  `text2sql.toSql(..., 'main')` and in any `sql validate <db> "..."` /
108
- `sql run <db> "..."` sandbox calls.
85
+ `sql run <db> "..."` calls from a sandbox where the package CLI is installed.
109
86
 
110
87
  Adapter names must match `/^[A-Za-z_][A-Za-z0-9_]*$/`. If you build adapter
111
88
  maps dynamically, use `isValidAdapterName(name)` to check one key or
112
89
  `validateAdapterNames(names)` to fail fast before constructing `Text2Sql` or
113
- `sqlSandboxExtension`.
90
+ the sandbox-side adapter module used by the `sql` CLI.
114
91
 
115
92
  ## AI Model Providers
116
93
 
117
94
  Text2SQL works with any model provider supported by the [Vercel AI SDK](https://sdk.vercel.ai/docs), including OpenAI, Anthropic, Google, Groq, and more.
118
95
 
119
- ## Advanced: Composing Sandbox Extensions
96
+ ## Building a Conversational Agent
120
97
 
121
- `Text2Sql` takes a caller-owned `sandbox`. `sqlSandboxExtension(adaptersMap)`
122
- returns a `SandboxExtension` that bundles the `sql` subcommand, transform
123
- plugins, and arg-repair hook. In the simplest case, that map is
124
- `{ main: adapter }`. Compose it (alongside any of your own extensions) via
125
- `createBashTool` + `createRoutingSandbox` + `createVirtualSandbox`.
126
- See [Caller-Owned Sandbox](https://januarylabs.github.io/deepagents/docs/text2sql/sqlv3)
127
- for composition patterns.
98
+ `Text2Sql` ships only the SQL-aware primitives: `toSql`, `toPairs`,
99
+ `AdapterIndexer`, and `instructions()`. You build the chat agent yourself by
100
+ composing a `ContextEngine`, a sandbox, and `agent` + `chat` from
101
+ `@deepagents/context`:
102
+
103
+ ```typescript
104
+ import {
105
+ ContextEngine,
106
+ type ContextFragment,
107
+ InMemoryContextStore,
108
+ agent,
109
+ chat,
110
+ createContainerTool,
111
+ errorRecoveryGuardrail,
112
+ npm,
113
+ user,
114
+ } from '@deepagents/context';
115
+ import { createSqlCommandHooks, instructions } from '@deepagents/text2sql';
116
+
117
+ const store = new InMemoryContextStore();
118
+ const context = new ContextEngine({
119
+ store,
120
+ chatId: 'chat-123',
121
+ userId: 'user-456',
122
+ });
123
+
124
+ const sandbox = await createContainerTool({
125
+ installers: [npm('@deepagents/text2sql', { ensureRuntime: true })],
126
+ env: {
127
+ TEXT2SQL_ADAPTERS: '/workspace/text2sql-adapters.ts',
128
+ },
129
+ ...createSqlCommandHooks({ adapters: { main: adapter } }),
130
+ });
131
+
132
+ const indexResult = await sandbox.sandbox.executeCommand('sql index');
133
+ if (indexResult.exitCode !== 0) throw new Error(indexResult.stderr);
134
+ const manifest = JSON.parse(indexResult.stdout) as { fragmentsPath: string };
135
+ const fragments = JSON.parse(
136
+ await sandbox.sandbox.readFile(manifest.fragmentsPath),
137
+ ) as ContextFragment[];
138
+ context.set(...instructions(), ...fragments);
139
+
140
+ const ai = agent({
141
+ name: 'sql-assistant',
142
+ sandbox,
143
+ model: groq('openai/gpt-oss-20b'),
144
+ context,
145
+ guardrails: [errorRecoveryGuardrail],
146
+ maxGuardrailRetries: 3,
147
+ });
148
+
149
+ await context.continue(user('Show me the top 10 customers by revenue'));
150
+ const stream = await chat(ai);
151
+
152
+ for await (const chunk of stream) {
153
+ // handle streaming response
154
+ }
155
+ ```
156
+
157
+ `instructions()` returns the SQL-flavored system fragments (policies, workflows,
158
+ clarifications, style guides) — spread them into `context.set()` alongside the
159
+ schema fragments returned by `sql index`. Add or replace them with your
160
+ own domain fragments as needed.
161
+
162
+ ## Advanced: SQL CLI in Sandboxes
163
+
164
+ `sql validate <db> "..."` and `sql run <db> "..."` are real commands from the
165
+ `@deepagents/text2sql` package. `sql index [adapter ...]` writes schema
166
+ fragments plus progress events for chat setup. Install the package inside the
167
+ sandbox and set `TEXT2SQL_ADAPTERS` to a module whose default export is
168
+ `Record<string, Adapter>`. Missing `sql` means the sandbox was not prepared
169
+ correctly.
170
+
171
+ Spread `createSqlCommandHooks({ adapters })` into `createBashTool()` or
172
+ `createContainerTool()` for model-driven bash calls. The before hook preserves
173
+ the old virtual-command tolerance for common LLM quote mistakes, rewrites SQL
174
+ identifier backticks so bash does not run them as command substitutions, and
175
+ blocks raw database access so read-only and scope checks stay behind
176
+ `sql validate` / `sql run`. The after hook restores hidden `formattedSql`
177
+ metadata from the host adapter map without putting that concern into the real
178
+ CLI.
128
179
 
129
180
  ## Fragments
130
181
 
131
- Inject domain knowledge by setting fragments on the `ContextEngine` you pass to
132
- `Text2Sql`. Those fragments affect `chat()` sessions. If you need direct SQL
133
- generation with extra fragments, use the lower-level `toSql({ fragments })`
182
+ Inject domain knowledge by setting fragments on the `ContextEngine` you build
183
+ for the agent. Those fragments affect every `chat()` turn. If you need direct
184
+ SQL generation with extra fragments, use the lower-level `toSql({ fragments })`
134
185
  helper exported from `@deepagents/text2sql`.
135
186
 
136
187
  ```typescript
@@ -144,13 +195,13 @@ import {
144
195
  } from '@deepagents/context';
145
196
 
146
197
  const store = new InMemoryContextStore();
147
- const engine = new ContextEngine({
198
+ const context = new ContextEngine({
148
199
  store,
149
200
  chatId: 'chat-123',
150
201
  userId: 'user-456',
151
202
  });
152
203
 
153
- engine.set(
204
+ context.set(
154
205
  term('MRR', 'monthly recurring revenue'),
155
206
  hint('Always exclude test accounts with email ending in @test.com'),
156
207
  guardrail({
@@ -188,62 +239,69 @@ Control what schema metadata the AI receives:
188
239
 
189
240
  ## Conversations
190
241
 
191
- `chat()` persists history through the `ContextEngine` you pass to `Text2Sql`.
192
- Reuse the same store, `chatId`, and `userId` to continue the same thread.
193
- Before each turn, append only the new incoming message to the engine; do not
194
- replay earlier turns that are already stored in the context store:
242
+ `chat()` (from `@deepagents/context`) persists history through the
243
+ `ContextEngine` you build for the agent. Reuse the same store, `chatId`, and
244
+ `userId` to continue the same thread. Before each turn, append only the new
245
+ incoming message to the engine; do not replay earlier turns that are already
246
+ stored in the context store:
195
247
 
196
248
  ```typescript
197
- await engine.continue(user('Show me orders from last month'));
198
- const stream = await text2sql.chat();
249
+ await context.continue(user('Show me orders from last month'));
250
+ const stream = await chat(ai);
199
251
 
200
252
  for await (const chunk of stream) {
201
253
  // handle streaming response
202
254
  }
203
255
 
204
256
  // Continue the same conversation
205
- await engine.continue(user('Now filter to only completed ones'));
206
- const followUp = await text2sql.chat();
257
+ await context.continue(user('Now filter to only completed ones'));
258
+ const followUp = await chat(ai);
207
259
  for await (const chunk of followUp) {
208
260
  // handle streaming response
209
261
  }
210
262
  ```
211
263
 
212
- While `chat()` builds or reads cached schema context, it emits progress chunks
213
- before assistant text starts:
264
+ ## Streaming Index Progress
265
+
266
+ `AdapterIndexer#index({ onProgress })` emits progress events while it warms or reads
267
+ the schema cache for host-side indexing. The sandbox CLI writes the same event
268
+ shape to the `eventsPath` file returned by `sql index`:
214
269
 
215
270
  ```typescript
271
+ import { createUIMessageStream } from 'ai';
272
+
216
273
  import {
274
+ AdapterIndexer,
217
275
  TEXT2SQL_INDEX_PROGRESS_CHUNK,
218
276
  type Text2SqlIndexProgressEvent,
219
277
  } from '@deepagents/text2sql';
220
278
 
221
- await engine.continue(user('Show me top 10 customers'));
222
- const stream = await text2sql.chat();
223
- const startedAt = new Map<string, number>();
224
-
225
- for await (const chunk of stream) {
226
- if (chunk.type === TEXT2SQL_INDEX_PROGRESS_CHUNK) {
227
- const event = chunk.data as Text2SqlIndexProgressEvent;
228
- const scope =
229
- [event.adapter, event.phase].filter(Boolean).join(':') || 'index';
279
+ const indexer = new AdapterIndexer({ adapters, version: 'v1' });
230
280
 
231
- if (event.type.endsWith(':start') || event.type === 'index:start') {
232
- startedAt.set(scope, event.timestampMs ?? Date.now());
233
- }
281
+ await context.continue(user('Show me top 10 customers'));
234
282
 
235
- if (event.type.endsWith(':end') || event.type.endsWith(':error')) {
236
- const durationMs =
237
- (event.timestampMs ?? Date.now()) -
238
- (startedAt.get(scope) ?? event.timestampMs ?? Date.now());
239
- console.log(event.message, durationMs);
283
+ const stream = createUIMessageStream({
284
+ execute: async ({ writer }) => {
285
+ const head = await context.headMessage();
286
+ if (!head || head.name !== 'assistant') {
287
+ throw new Error(
288
+ 'expected head to be an assistant message — call context.continue() before chat()',
289
+ );
240
290
  }
241
-
242
- continue;
243
- }
244
-
245
- // handle assistant stream chunks
246
- }
291
+ writer.write({ type: 'start', messageId: head.id });
292
+
293
+ const fragments = await indexer.index({
294
+ onProgress: (event) =>
295
+ writer.write({
296
+ type: TEXT2SQL_INDEX_PROGRESS_CHUNK,
297
+ data: event,
298
+ }),
299
+ });
300
+
301
+ context.set(...instructions(), ...fragments);
302
+ writer.merge(await chat(ai));
303
+ },
304
+ });
247
305
  ```
248
306
 
249
307
  Each progress event includes a generic Unix epoch `timestampMs` so clients can
@@ -281,7 +339,6 @@ Full documentation available at [januarylabs.github.io/deepagents](https://janua
281
339
 
282
340
  - [Getting Started](https://januarylabs.github.io/deepagents/docs/text2sql/getting-started)
283
341
  - [Generate SQL](https://januarylabs.github.io/deepagents/docs/text2sql/to-sql)
284
- - [Caller-Owned Sandbox](https://januarylabs.github.io/deepagents/docs/text2sql/sqlv3)
285
342
  - [Teach the System](https://januarylabs.github.io/deepagents/docs/text2sql/teach-the-system)
286
343
  - [Build Conversations](https://januarylabs.github.io/deepagents/docs/text2sql/build-conversations)
287
344
  - [Grounding](https://januarylabs.github.io/deepagents/docs/text2sql/grounding)