@agent-native/core 0.51.5 → 0.51.7

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 (38) hide show
  1. package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
  2. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  3. package/dist/cli/pr-visual-recap-workflow.js +1 -1
  4. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  5. package/dist/cli/templates-meta.js +1 -1
  6. package/dist/cli/templates-meta.js.map +1 -1
  7. package/dist/coding-tools/run-code.d.ts.map +1 -1
  8. package/dist/coding-tools/run-code.js +435 -3
  9. package/dist/coding-tools/run-code.js.map +1 -1
  10. package/dist/provider-api/corpus-jobs-store.d.ts +95 -0
  11. package/dist/provider-api/corpus-jobs-store.d.ts.map +1 -0
  12. package/dist/provider-api/corpus-jobs-store.js +394 -0
  13. package/dist/provider-api/corpus-jobs-store.js.map +1 -0
  14. package/dist/provider-api/corpus-jobs.d.ts +146 -0
  15. package/dist/provider-api/corpus-jobs.d.ts.map +1 -0
  16. package/dist/provider-api/corpus-jobs.js +1192 -0
  17. package/dist/provider-api/corpus-jobs.js.map +1 -0
  18. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  19. package/dist/server/agent-chat-plugin.js +9 -2
  20. package/dist/server/agent-chat-plugin.js.map +1 -1
  21. package/dist/server/auth-marketing.js +4 -4
  22. package/dist/server/auth-marketing.js.map +1 -1
  23. package/docs/content/cloneable-saas.md +1 -1
  24. package/docs/content/getting-started.md +1 -1
  25. package/docs/content/local-file-mode.md +6 -1
  26. package/docs/content/template-analytics.md +0 -8
  27. package/docs/content/template-assets.md +0 -6
  28. package/docs/content/template-brain.md +0 -8
  29. package/docs/content/template-calendar.md +0 -8
  30. package/docs/content/template-clips.md +0 -8
  31. package/docs/content/template-content.md +27 -23
  32. package/docs/content/template-design.md +0 -6
  33. package/docs/content/template-forms.md +0 -10
  34. package/docs/content/template-mail.md +0 -8
  35. package/docs/content/template-plan.md +180 -0
  36. package/docs/content/template-slides.md +0 -8
  37. package/docs/content/template-videos.md +0 -8
  38. package/package.json +3 -1
@@ -0,0 +1,394 @@
1
+ /**
2
+ * Durable state for provider corpus jobs.
3
+ *
4
+ * Jobs are scoped by (app_id, owner_email), matching staged datasets. The
5
+ * runner stores only request configuration, checkpoints, and compact search
6
+ * hits so large provider corpora never enter chat context or filesystem
7
+ * scratch files.
8
+ */
9
+ import { getDbExec, intType, isPostgres } from "../db/client.js";
10
+ let initPromise;
11
+ async function ensureTables() {
12
+ if (!initPromise) {
13
+ initPromise = (async () => {
14
+ const db = getDbExec();
15
+ const integerType = intType();
16
+ await db.execute(`
17
+ CREATE TABLE IF NOT EXISTS provider_corpus_jobs (
18
+ id TEXT NOT NULL,
19
+ app_id TEXT NOT NULL,
20
+ owner_email TEXT NOT NULL,
21
+ name TEXT NOT NULL,
22
+ mode TEXT NOT NULL,
23
+ status TEXT NOT NULL,
24
+ provider TEXT NOT NULL,
25
+ request_json TEXT NOT NULL,
26
+ pagination_json TEXT,
27
+ batch_json TEXT,
28
+ search_json TEXT NOT NULL,
29
+ limits_json TEXT NOT NULL,
30
+ checkpoint_json TEXT NOT NULL,
31
+ pages_processed ${integerType} NOT NULL DEFAULT 0,
32
+ batches_processed ${integerType} NOT NULL DEFAULT 0,
33
+ items_processed ${integerType} NOT NULL DEFAULT 0,
34
+ matched_items ${integerType} NOT NULL DEFAULT 0,
35
+ total_hits ${integerType} NOT NULL DEFAULT 0,
36
+ stored_hits ${integerType} NOT NULL DEFAULT 0,
37
+ error TEXT,
38
+ next_resume_at ${integerType},
39
+ created_at ${integerType} NOT NULL,
40
+ updated_at ${integerType} NOT NULL,
41
+ PRIMARY KEY (id)
42
+ )
43
+ `);
44
+ await db.execute(`
45
+ CREATE TABLE IF NOT EXISTS provider_corpus_job_hits (
46
+ job_id TEXT NOT NULL,
47
+ hit_index ${integerType} NOT NULL,
48
+ hit_data TEXT NOT NULL,
49
+ PRIMARY KEY (job_id, hit_index)
50
+ )
51
+ `);
52
+ if (isPostgres()) {
53
+ await widenPostgresIntegerColumns(db);
54
+ }
55
+ for (const ddl of [
56
+ `CREATE INDEX IF NOT EXISTS provider_corpus_jobs_scope_idx ON provider_corpus_jobs (app_id, owner_email, updated_at)`,
57
+ `CREATE INDEX IF NOT EXISTS provider_corpus_jobs_status_idx ON provider_corpus_jobs (app_id, owner_email, status)`,
58
+ `CREATE INDEX IF NOT EXISTS provider_corpus_job_hits_job_idx ON provider_corpus_job_hits (job_id)`,
59
+ ]) {
60
+ try {
61
+ await db.execute(ddl);
62
+ }
63
+ catch {
64
+ // Index already exists or the backend rejected best-effort indexing.
65
+ }
66
+ }
67
+ })().catch((err) => {
68
+ initPromise = undefined;
69
+ throw err;
70
+ });
71
+ }
72
+ return initPromise;
73
+ }
74
+ async function widenPostgresIntegerColumns(db) {
75
+ const statements = [
76
+ `ALTER TABLE provider_corpus_jobs ALTER COLUMN pages_processed TYPE BIGINT USING pages_processed::bigint`,
77
+ `ALTER TABLE provider_corpus_jobs ALTER COLUMN batches_processed TYPE BIGINT USING batches_processed::bigint`,
78
+ `ALTER TABLE provider_corpus_jobs ALTER COLUMN items_processed TYPE BIGINT USING items_processed::bigint`,
79
+ `ALTER TABLE provider_corpus_jobs ALTER COLUMN matched_items TYPE BIGINT USING matched_items::bigint`,
80
+ `ALTER TABLE provider_corpus_jobs ALTER COLUMN total_hits TYPE BIGINT USING total_hits::bigint`,
81
+ `ALTER TABLE provider_corpus_jobs ALTER COLUMN stored_hits TYPE BIGINT USING stored_hits::bigint`,
82
+ `ALTER TABLE provider_corpus_jobs ALTER COLUMN next_resume_at TYPE BIGINT USING next_resume_at::bigint`,
83
+ `ALTER TABLE provider_corpus_jobs ALTER COLUMN created_at TYPE BIGINT USING created_at::bigint`,
84
+ `ALTER TABLE provider_corpus_jobs ALTER COLUMN updated_at TYPE BIGINT USING updated_at::bigint`,
85
+ `ALTER TABLE provider_corpus_job_hits ALTER COLUMN hit_index TYPE BIGINT USING hit_index::bigint`,
86
+ ];
87
+ for (const sql of statements) {
88
+ try {
89
+ await db.execute(sql);
90
+ }
91
+ catch {
92
+ // Best-effort compatibility for older deployments.
93
+ }
94
+ }
95
+ }
96
+ export async function createProviderCorpusJob(options) {
97
+ await ensureTables();
98
+ const db = getDbExec();
99
+ const now = Date.now();
100
+ await db.execute({
101
+ sql: isPostgres()
102
+ ? `
103
+ INSERT INTO provider_corpus_jobs
104
+ (id, app_id, owner_email, name, mode, status, provider, request_json, pagination_json, batch_json, search_json, limits_json, checkpoint_json, created_at, updated_at)
105
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
106
+ ON CONFLICT (id) DO UPDATE SET
107
+ app_id = EXCLUDED.app_id,
108
+ owner_email = EXCLUDED.owner_email,
109
+ name = EXCLUDED.name,
110
+ mode = EXCLUDED.mode,
111
+ status = EXCLUDED.status,
112
+ provider = EXCLUDED.provider,
113
+ request_json = EXCLUDED.request_json,
114
+ pagination_json = EXCLUDED.pagination_json,
115
+ batch_json = EXCLUDED.batch_json,
116
+ search_json = EXCLUDED.search_json,
117
+ limits_json = EXCLUDED.limits_json,
118
+ checkpoint_json = EXCLUDED.checkpoint_json,
119
+ pages_processed = 0,
120
+ batches_processed = 0,
121
+ items_processed = 0,
122
+ matched_items = 0,
123
+ total_hits = 0,
124
+ stored_hits = 0,
125
+ error = NULL,
126
+ next_resume_at = NULL,
127
+ updated_at = EXCLUDED.updated_at
128
+ WHERE provider_corpus_jobs.app_id = EXCLUDED.app_id
129
+ AND provider_corpus_jobs.owner_email = EXCLUDED.owner_email
130
+ `
131
+ : `
132
+ INSERT INTO provider_corpus_jobs
133
+ (id, app_id, owner_email, name, mode, status, provider, request_json, pagination_json, batch_json, search_json, limits_json, checkpoint_json, created_at, updated_at)
134
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
135
+ ON CONFLICT (id) DO UPDATE SET
136
+ app_id = excluded.app_id,
137
+ owner_email = excluded.owner_email,
138
+ name = excluded.name,
139
+ mode = excluded.mode,
140
+ status = excluded.status,
141
+ provider = excluded.provider,
142
+ request_json = excluded.request_json,
143
+ pagination_json = excluded.pagination_json,
144
+ batch_json = excluded.batch_json,
145
+ search_json = excluded.search_json,
146
+ limits_json = excluded.limits_json,
147
+ checkpoint_json = excluded.checkpoint_json,
148
+ pages_processed = 0,
149
+ batches_processed = 0,
150
+ items_processed = 0,
151
+ matched_items = 0,
152
+ total_hits = 0,
153
+ stored_hits = 0,
154
+ error = NULL,
155
+ next_resume_at = NULL,
156
+ updated_at = excluded.updated_at
157
+ WHERE provider_corpus_jobs.app_id = excluded.app_id
158
+ AND provider_corpus_jobs.owner_email = excluded.owner_email
159
+ `,
160
+ args: [
161
+ options.id,
162
+ options.appId,
163
+ options.ownerEmail,
164
+ options.name,
165
+ options.mode,
166
+ options.status,
167
+ options.provider,
168
+ JSON.stringify(options.request),
169
+ jsonOrNull(options.pagination),
170
+ jsonOrNull(options.batch),
171
+ JSON.stringify(options.search),
172
+ JSON.stringify(options.limits),
173
+ JSON.stringify(options.checkpoint),
174
+ now,
175
+ now,
176
+ ],
177
+ });
178
+ await db.execute({
179
+ sql: `DELETE FROM provider_corpus_job_hits WHERE job_id = ?`,
180
+ args: [options.id],
181
+ });
182
+ const job = await getProviderCorpusJob({
183
+ id: options.id,
184
+ appId: options.appId,
185
+ ownerEmail: options.ownerEmail,
186
+ });
187
+ if (!job) {
188
+ // The scoped upsert above only updates a row that already belongs to this
189
+ // (app_id, owner_email). A null read here means a job with this id exists
190
+ // under a different owner, so the conflicting insert was skipped rather
191
+ // than clobbering the other tenant's job.
192
+ throw new Error(`Failed to create provider corpus job ${options.id}: a job with this id ` +
193
+ `already exists for a different owner. Use a different jobId.`);
194
+ }
195
+ return job;
196
+ }
197
+ export async function getProviderCorpusJob(options) {
198
+ await ensureTables();
199
+ const db = getDbExec();
200
+ const { rows } = await db.execute({
201
+ sql: `SELECT * FROM provider_corpus_jobs WHERE id = ? AND app_id = ? AND owner_email = ?`,
202
+ args: [options.id, options.appId, options.ownerEmail],
203
+ });
204
+ return rows[0] ? rowToJob(rows[0]) : null;
205
+ }
206
+ export async function listProviderCorpusJobs(options) {
207
+ await ensureTables();
208
+ const db = getDbExec();
209
+ const limit = Math.max(1, Math.min(options.limit ?? 20, 100));
210
+ const { rows } = await db.execute({
211
+ sql: `SELECT * FROM provider_corpus_jobs WHERE app_id = ? AND owner_email = ? ORDER BY updated_at DESC LIMIT ?`,
212
+ args: [options.appId, options.ownerEmail, limit],
213
+ });
214
+ return rows.map(rowToJob);
215
+ }
216
+ export async function updateProviderCorpusJob(options) {
217
+ await ensureTables();
218
+ const existing = await getProviderCorpusJob({
219
+ id: options.id,
220
+ appId: options.appId,
221
+ ownerEmail: options.ownerEmail,
222
+ });
223
+ if (!existing)
224
+ return null;
225
+ const next = {
226
+ status: options.status ?? existing.status,
227
+ checkpoint: options.checkpoint ?? existing.checkpoint,
228
+ pagesProcessed: options.pagesProcessed ?? existing.pagesProcessed,
229
+ batchesProcessed: options.batchesProcessed ?? existing.batchesProcessed,
230
+ itemsProcessed: options.itemsProcessed ?? existing.itemsProcessed,
231
+ matchedItems: options.matchedItems ?? existing.matchedItems,
232
+ totalHits: options.totalHits ?? existing.totalHits,
233
+ storedHits: options.storedHits ?? existing.storedHits,
234
+ error: options.error === undefined ? existing.error : options.error,
235
+ nextResumeAt: options.nextResumeAt === undefined
236
+ ? existing.nextResumeAt
237
+ : options.nextResumeAt,
238
+ };
239
+ const db = getDbExec();
240
+ await db.execute({
241
+ sql: `
242
+ UPDATE provider_corpus_jobs SET
243
+ status = ?,
244
+ checkpoint_json = ?,
245
+ pages_processed = ?,
246
+ batches_processed = ?,
247
+ items_processed = ?,
248
+ matched_items = ?,
249
+ total_hits = ?,
250
+ stored_hits = ?,
251
+ error = ?,
252
+ next_resume_at = ?,
253
+ updated_at = ?
254
+ WHERE id = ? AND app_id = ? AND owner_email = ?
255
+ `,
256
+ args: [
257
+ next.status,
258
+ JSON.stringify(next.checkpoint),
259
+ next.pagesProcessed,
260
+ next.batchesProcessed,
261
+ next.itemsProcessed,
262
+ next.matchedItems,
263
+ next.totalHits,
264
+ next.storedHits,
265
+ next.error,
266
+ next.nextResumeAt,
267
+ Date.now(),
268
+ options.id,
269
+ options.appId,
270
+ options.ownerEmail,
271
+ ],
272
+ });
273
+ return getProviderCorpusJob({
274
+ id: options.id,
275
+ appId: options.appId,
276
+ ownerEmail: options.ownerEmail,
277
+ });
278
+ }
279
+ export async function appendProviderCorpusJobHits(options) {
280
+ if (options.hits.length === 0)
281
+ return;
282
+ await ensureTables();
283
+ const db = getDbExec();
284
+ // Insert in chunked multi-row statements rather than one round-trip per hit,
285
+ // and ignore conflicts on (job_id, hit_index). The runner writes hits before
286
+ // it advances the stored-hits counter and checkpoint, so a crash between the
287
+ // two means resume re-fetches the same page and re-appends the same indices;
288
+ // DO NOTHING makes that retry idempotent instead of a primary-key violation.
289
+ const CHUNK = 100;
290
+ for (let start = 0; start < options.hits.length; start += CHUNK) {
291
+ const chunk = options.hits.slice(start, start + CHUNK);
292
+ const placeholders = chunk.map(() => "(?, ?, ?)").join(", ");
293
+ const args = [];
294
+ for (let j = 0; j < chunk.length; j++) {
295
+ args.push(options.jobId, options.startIndex + start + j, JSON.stringify(chunk[j]));
296
+ }
297
+ await db.execute({
298
+ sql: `INSERT INTO provider_corpus_job_hits (job_id, hit_index, hit_data) VALUES ${placeholders} ON CONFLICT (job_id, hit_index) DO NOTHING`,
299
+ args,
300
+ });
301
+ }
302
+ }
303
+ export async function getProviderCorpusJobHits(options) {
304
+ await ensureTables();
305
+ const job = await getProviderCorpusJob({
306
+ id: options.jobId,
307
+ appId: options.appId,
308
+ ownerEmail: options.ownerEmail,
309
+ });
310
+ if (!job)
311
+ return [];
312
+ const offset = Math.max(0, options.offset ?? 0);
313
+ const limit = Math.max(1, Math.min(options.limit ?? 100, 1000));
314
+ const db = getDbExec();
315
+ const { rows } = await db.execute({
316
+ sql: `SELECT hit_data FROM provider_corpus_job_hits WHERE job_id = ? ORDER BY hit_index ASC LIMIT ? OFFSET ?`,
317
+ args: [options.jobId, limit, offset],
318
+ });
319
+ return rows.map((row) => parseJsonRecord(row.hit_data));
320
+ }
321
+ export async function deleteProviderCorpusJob(options) {
322
+ await ensureTables();
323
+ const existing = await getProviderCorpusJob(options);
324
+ if (!existing)
325
+ return false;
326
+ const db = getDbExec();
327
+ await db.execute({
328
+ sql: `DELETE FROM provider_corpus_job_hits WHERE job_id = ?`,
329
+ args: [options.id],
330
+ });
331
+ const result = await db.execute({
332
+ sql: `DELETE FROM provider_corpus_jobs WHERE id = ? AND app_id = ? AND owner_email = ?`,
333
+ args: [options.id, options.appId, options.ownerEmail],
334
+ });
335
+ return result.rowsAffected > 0;
336
+ }
337
+ function jsonOrNull(value) {
338
+ return value ? JSON.stringify(value) : null;
339
+ }
340
+ function parseJsonRecord(value) {
341
+ if (typeof value !== "string")
342
+ return {};
343
+ try {
344
+ const parsed = JSON.parse(value);
345
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
346
+ ? parsed
347
+ : {};
348
+ }
349
+ catch {
350
+ return {};
351
+ }
352
+ }
353
+ function parseNullableJsonRecord(value) {
354
+ if (typeof value !== "string" || !value)
355
+ return null;
356
+ return parseJsonRecord(value);
357
+ }
358
+ function nullableNumber(value) {
359
+ if (value === null || value === undefined)
360
+ return null;
361
+ const number = Number(value);
362
+ return Number.isFinite(number) ? number : null;
363
+ }
364
+ function rowToJob(row) {
365
+ return {
366
+ id: String(row.id),
367
+ appId: String(row.app_id),
368
+ ownerEmail: String(row.owner_email),
369
+ name: String(row.name),
370
+ mode: String(row.mode),
371
+ status: String(row.status),
372
+ provider: String(row.provider),
373
+ request: parseJsonRecord(row.request_json),
374
+ pagination: parseNullableJsonRecord(row.pagination_json),
375
+ batch: parseNullableJsonRecord(row.batch_json),
376
+ search: parseJsonRecord(row.search_json),
377
+ limits: parseJsonRecord(row.limits_json),
378
+ checkpoint: parseJsonRecord(row.checkpoint_json),
379
+ pagesProcessed: Number(row.pages_processed ?? 0),
380
+ batchesProcessed: Number(row.batches_processed ?? 0),
381
+ itemsProcessed: Number(row.items_processed ?? 0),
382
+ matchedItems: Number(row.matched_items ?? 0),
383
+ totalHits: Number(row.total_hits ?? 0),
384
+ storedHits: Number(row.stored_hits ?? 0),
385
+ error: row.error == null ? null : String(row.error),
386
+ nextResumeAt: nullableNumber(row.next_resume_at),
387
+ createdAt: Number(row.created_at ?? 0),
388
+ updatedAt: Number(row.updated_at ?? 0),
389
+ };
390
+ }
391
+ export function _resetProviderCorpusJobsStoreForTests() {
392
+ initPromise = undefined;
393
+ }
394
+ //# sourceMappingURL=corpus-jobs-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"corpus-jobs-store.js","sourceRoot":"","sources":["../../src/provider-api/corpus-jobs-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Durable state for provider corpus jobs.\n *\n * Jobs are scoped by (app_id, owner_email), matching staged datasets. The\n * runner stores only request configuration, checkpoints, and compact search\n * hits so large provider corpora never enter chat context or filesystem\n * scratch files.\n */\n\nimport { getDbExec, intType, isPostgres, type DbExec } from \"../db/client.js\";\n\nexport type ProviderCorpusJobStatus =\n | \"running\"\n | \"paused\"\n | \"quota_wait\"\n | \"completed\"\n | \"failed\";\n\nexport interface ProviderCorpusJobRecord {\n id: string;\n appId: string;\n ownerEmail: string;\n name: string;\n mode: string;\n status: ProviderCorpusJobStatus;\n provider: string;\n request: Record<string, unknown>;\n pagination: Record<string, unknown> | null;\n batch: Record<string, unknown> | null;\n search: Record<string, unknown>;\n limits: Record<string, unknown>;\n checkpoint: Record<string, unknown>;\n pagesProcessed: number;\n batchesProcessed: number;\n itemsProcessed: number;\n matchedItems: number;\n totalHits: number;\n storedHits: number;\n error: string | null;\n nextResumeAt: number | null;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface CreateProviderCorpusJobOptions {\n id: string;\n appId: string;\n ownerEmail: string;\n name: string;\n mode: string;\n status: ProviderCorpusJobStatus;\n provider: string;\n request: Record<string, unknown>;\n pagination?: Record<string, unknown> | null;\n batch?: Record<string, unknown> | null;\n search: Record<string, unknown>;\n limits: Record<string, unknown>;\n checkpoint: Record<string, unknown>;\n}\n\nexport interface UpdateProviderCorpusJobOptions {\n id: string;\n appId: string;\n ownerEmail: string;\n status?: ProviderCorpusJobStatus;\n checkpoint?: Record<string, unknown>;\n pagesProcessed?: number;\n batchesProcessed?: number;\n itemsProcessed?: number;\n matchedItems?: number;\n totalHits?: number;\n storedHits?: number;\n error?: string | null;\n nextResumeAt?: number | null;\n}\n\nlet initPromise: Promise<void> | undefined;\n\nasync function ensureTables(): Promise<void> {\n if (!initPromise) {\n initPromise = (async () => {\n const db = getDbExec();\n const integerType = intType();\n await db.execute(`\n CREATE TABLE IF NOT EXISTS provider_corpus_jobs (\n id TEXT NOT NULL,\n app_id TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n name TEXT NOT NULL,\n mode TEXT NOT NULL,\n status TEXT NOT NULL,\n provider TEXT NOT NULL,\n request_json TEXT NOT NULL,\n pagination_json TEXT,\n batch_json TEXT,\n search_json TEXT NOT NULL,\n limits_json TEXT NOT NULL,\n checkpoint_json TEXT NOT NULL,\n pages_processed ${integerType} NOT NULL DEFAULT 0,\n batches_processed ${integerType} NOT NULL DEFAULT 0,\n items_processed ${integerType} NOT NULL DEFAULT 0,\n matched_items ${integerType} NOT NULL DEFAULT 0,\n total_hits ${integerType} NOT NULL DEFAULT 0,\n stored_hits ${integerType} NOT NULL DEFAULT 0,\n error TEXT,\n next_resume_at ${integerType},\n created_at ${integerType} NOT NULL,\n updated_at ${integerType} NOT NULL,\n PRIMARY KEY (id)\n )\n `);\n await db.execute(`\n CREATE TABLE IF NOT EXISTS provider_corpus_job_hits (\n job_id TEXT NOT NULL,\n hit_index ${integerType} NOT NULL,\n hit_data TEXT NOT NULL,\n PRIMARY KEY (job_id, hit_index)\n )\n `);\n if (isPostgres()) {\n await widenPostgresIntegerColumns(db);\n }\n for (const ddl of [\n `CREATE INDEX IF NOT EXISTS provider_corpus_jobs_scope_idx ON provider_corpus_jobs (app_id, owner_email, updated_at)`,\n `CREATE INDEX IF NOT EXISTS provider_corpus_jobs_status_idx ON provider_corpus_jobs (app_id, owner_email, status)`,\n `CREATE INDEX IF NOT EXISTS provider_corpus_job_hits_job_idx ON provider_corpus_job_hits (job_id)`,\n ]) {\n try {\n await db.execute(ddl);\n } catch {\n // Index already exists or the backend rejected best-effort indexing.\n }\n }\n })().catch((err) => {\n initPromise = undefined;\n throw err;\n });\n }\n return initPromise;\n}\n\nasync function widenPostgresIntegerColumns(db: DbExec): Promise<void> {\n const statements = [\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN pages_processed TYPE BIGINT USING pages_processed::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN batches_processed TYPE BIGINT USING batches_processed::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN items_processed TYPE BIGINT USING items_processed::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN matched_items TYPE BIGINT USING matched_items::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN total_hits TYPE BIGINT USING total_hits::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN stored_hits TYPE BIGINT USING stored_hits::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN next_resume_at TYPE BIGINT USING next_resume_at::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN created_at TYPE BIGINT USING created_at::bigint`,\n `ALTER TABLE provider_corpus_jobs ALTER COLUMN updated_at TYPE BIGINT USING updated_at::bigint`,\n `ALTER TABLE provider_corpus_job_hits ALTER COLUMN hit_index TYPE BIGINT USING hit_index::bigint`,\n ];\n for (const sql of statements) {\n try {\n await db.execute(sql);\n } catch {\n // Best-effort compatibility for older deployments.\n }\n }\n}\n\nexport async function createProviderCorpusJob(\n options: CreateProviderCorpusJobOptions,\n): Promise<ProviderCorpusJobRecord> {\n await ensureTables();\n const db = getDbExec();\n const now = Date.now();\n await db.execute({\n sql: isPostgres()\n ? `\n INSERT INTO provider_corpus_jobs\n (id, app_id, owner_email, name, mode, status, provider, request_json, pagination_json, batch_json, search_json, limits_json, checkpoint_json, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n app_id = EXCLUDED.app_id,\n owner_email = EXCLUDED.owner_email,\n name = EXCLUDED.name,\n mode = EXCLUDED.mode,\n status = EXCLUDED.status,\n provider = EXCLUDED.provider,\n request_json = EXCLUDED.request_json,\n pagination_json = EXCLUDED.pagination_json,\n batch_json = EXCLUDED.batch_json,\n search_json = EXCLUDED.search_json,\n limits_json = EXCLUDED.limits_json,\n checkpoint_json = EXCLUDED.checkpoint_json,\n pages_processed = 0,\n batches_processed = 0,\n items_processed = 0,\n matched_items = 0,\n total_hits = 0,\n stored_hits = 0,\n error = NULL,\n next_resume_at = NULL,\n updated_at = EXCLUDED.updated_at\n WHERE provider_corpus_jobs.app_id = EXCLUDED.app_id\n AND provider_corpus_jobs.owner_email = EXCLUDED.owner_email\n `\n : `\n INSERT INTO provider_corpus_jobs\n (id, app_id, owner_email, name, mode, status, provider, request_json, pagination_json, batch_json, search_json, limits_json, checkpoint_json, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n app_id = excluded.app_id,\n owner_email = excluded.owner_email,\n name = excluded.name,\n mode = excluded.mode,\n status = excluded.status,\n provider = excluded.provider,\n request_json = excluded.request_json,\n pagination_json = excluded.pagination_json,\n batch_json = excluded.batch_json,\n search_json = excluded.search_json,\n limits_json = excluded.limits_json,\n checkpoint_json = excluded.checkpoint_json,\n pages_processed = 0,\n batches_processed = 0,\n items_processed = 0,\n matched_items = 0,\n total_hits = 0,\n stored_hits = 0,\n error = NULL,\n next_resume_at = NULL,\n updated_at = excluded.updated_at\n WHERE provider_corpus_jobs.app_id = excluded.app_id\n AND provider_corpus_jobs.owner_email = excluded.owner_email\n `,\n args: [\n options.id,\n options.appId,\n options.ownerEmail,\n options.name,\n options.mode,\n options.status,\n options.provider,\n JSON.stringify(options.request),\n jsonOrNull(options.pagination),\n jsonOrNull(options.batch),\n JSON.stringify(options.search),\n JSON.stringify(options.limits),\n JSON.stringify(options.checkpoint),\n now,\n now,\n ],\n });\n await db.execute({\n sql: `DELETE FROM provider_corpus_job_hits WHERE job_id = ?`,\n args: [options.id],\n });\n const job = await getProviderCorpusJob({\n id: options.id,\n appId: options.appId,\n ownerEmail: options.ownerEmail,\n });\n if (!job) {\n // The scoped upsert above only updates a row that already belongs to this\n // (app_id, owner_email). A null read here means a job with this id exists\n // under a different owner, so the conflicting insert was skipped rather\n // than clobbering the other tenant's job.\n throw new Error(\n `Failed to create provider corpus job ${options.id}: a job with this id ` +\n `already exists for a different owner. Use a different jobId.`,\n );\n }\n return job;\n}\n\nexport async function getProviderCorpusJob(options: {\n id: string;\n appId: string;\n ownerEmail: string;\n}): Promise<ProviderCorpusJobRecord | null> {\n await ensureTables();\n const db = getDbExec();\n const { rows } = await db.execute({\n sql: `SELECT * FROM provider_corpus_jobs WHERE id = ? AND app_id = ? AND owner_email = ?`,\n args: [options.id, options.appId, options.ownerEmail],\n });\n return rows[0] ? rowToJob(rows[0]) : null;\n}\n\nexport async function listProviderCorpusJobs(options: {\n appId: string;\n ownerEmail: string;\n limit?: number;\n}): Promise<ProviderCorpusJobRecord[]> {\n await ensureTables();\n const db = getDbExec();\n const limit = Math.max(1, Math.min(options.limit ?? 20, 100));\n const { rows } = await db.execute({\n sql: `SELECT * FROM provider_corpus_jobs WHERE app_id = ? AND owner_email = ? ORDER BY updated_at DESC LIMIT ?`,\n args: [options.appId, options.ownerEmail, limit],\n });\n return rows.map(rowToJob);\n}\n\nexport async function updateProviderCorpusJob(\n options: UpdateProviderCorpusJobOptions,\n): Promise<ProviderCorpusJobRecord | null> {\n await ensureTables();\n const existing = await getProviderCorpusJob({\n id: options.id,\n appId: options.appId,\n ownerEmail: options.ownerEmail,\n });\n if (!existing) return null;\n const next = {\n status: options.status ?? existing.status,\n checkpoint: options.checkpoint ?? existing.checkpoint,\n pagesProcessed: options.pagesProcessed ?? existing.pagesProcessed,\n batchesProcessed: options.batchesProcessed ?? existing.batchesProcessed,\n itemsProcessed: options.itemsProcessed ?? existing.itemsProcessed,\n matchedItems: options.matchedItems ?? existing.matchedItems,\n totalHits: options.totalHits ?? existing.totalHits,\n storedHits: options.storedHits ?? existing.storedHits,\n error: options.error === undefined ? existing.error : options.error,\n nextResumeAt:\n options.nextResumeAt === undefined\n ? existing.nextResumeAt\n : options.nextResumeAt,\n };\n const db = getDbExec();\n await db.execute({\n sql: `\n UPDATE provider_corpus_jobs SET\n status = ?,\n checkpoint_json = ?,\n pages_processed = ?,\n batches_processed = ?,\n items_processed = ?,\n matched_items = ?,\n total_hits = ?,\n stored_hits = ?,\n error = ?,\n next_resume_at = ?,\n updated_at = ?\n WHERE id = ? AND app_id = ? AND owner_email = ?\n `,\n args: [\n next.status,\n JSON.stringify(next.checkpoint),\n next.pagesProcessed,\n next.batchesProcessed,\n next.itemsProcessed,\n next.matchedItems,\n next.totalHits,\n next.storedHits,\n next.error,\n next.nextResumeAt,\n Date.now(),\n options.id,\n options.appId,\n options.ownerEmail,\n ],\n });\n return getProviderCorpusJob({\n id: options.id,\n appId: options.appId,\n ownerEmail: options.ownerEmail,\n });\n}\n\nexport async function appendProviderCorpusJobHits(options: {\n jobId: string;\n startIndex: number;\n hits: Record<string, unknown>[];\n}): Promise<void> {\n if (options.hits.length === 0) return;\n await ensureTables();\n const db = getDbExec();\n // Insert in chunked multi-row statements rather than one round-trip per hit,\n // and ignore conflicts on (job_id, hit_index). The runner writes hits before\n // it advances the stored-hits counter and checkpoint, so a crash between the\n // two means resume re-fetches the same page and re-appends the same indices;\n // DO NOTHING makes that retry idempotent instead of a primary-key violation.\n const CHUNK = 100;\n for (let start = 0; start < options.hits.length; start += CHUNK) {\n const chunk = options.hits.slice(start, start + CHUNK);\n const placeholders = chunk.map(() => \"(?, ?, ?)\").join(\", \");\n const args: unknown[] = [];\n for (let j = 0; j < chunk.length; j++) {\n args.push(\n options.jobId,\n options.startIndex + start + j,\n JSON.stringify(chunk[j]),\n );\n }\n await db.execute({\n sql: `INSERT INTO provider_corpus_job_hits (job_id, hit_index, hit_data) VALUES ${placeholders} ON CONFLICT (job_id, hit_index) DO NOTHING`,\n args,\n });\n }\n}\n\nexport async function getProviderCorpusJobHits(options: {\n jobId: string;\n appId: string;\n ownerEmail: string;\n offset?: number;\n limit?: number;\n}): Promise<Record<string, unknown>[]> {\n await ensureTables();\n const job = await getProviderCorpusJob({\n id: options.jobId,\n appId: options.appId,\n ownerEmail: options.ownerEmail,\n });\n if (!job) return [];\n const offset = Math.max(0, options.offset ?? 0);\n const limit = Math.max(1, Math.min(options.limit ?? 100, 1000));\n const db = getDbExec();\n const { rows } = await db.execute({\n sql: `SELECT hit_data FROM provider_corpus_job_hits WHERE job_id = ? ORDER BY hit_index ASC LIMIT ? OFFSET ?`,\n args: [options.jobId, limit, offset],\n });\n return rows.map((row) => parseJsonRecord(row.hit_data));\n}\n\nexport async function deleteProviderCorpusJob(options: {\n id: string;\n appId: string;\n ownerEmail: string;\n}): Promise<boolean> {\n await ensureTables();\n const existing = await getProviderCorpusJob(options);\n if (!existing) return false;\n const db = getDbExec();\n await db.execute({\n sql: `DELETE FROM provider_corpus_job_hits WHERE job_id = ?`,\n args: [options.id],\n });\n const result = await db.execute({\n sql: `DELETE FROM provider_corpus_jobs WHERE id = ? AND app_id = ? AND owner_email = ?`,\n args: [options.id, options.appId, options.ownerEmail],\n });\n return result.rowsAffected > 0;\n}\n\nfunction jsonOrNull(\n value: Record<string, unknown> | null | undefined,\n): string | null {\n return value ? JSON.stringify(value) : null;\n}\n\nfunction parseJsonRecord(value: unknown): Record<string, unknown> {\n if (typeof value !== \"string\") return {};\n try {\n const parsed = JSON.parse(value) as unknown;\n return parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction parseNullableJsonRecord(\n value: unknown,\n): Record<string, unknown> | null {\n if (typeof value !== \"string\" || !value) return null;\n return parseJsonRecord(value);\n}\n\nfunction nullableNumber(value: unknown): number | null {\n if (value === null || value === undefined) return null;\n const number = Number(value);\n return Number.isFinite(number) ? number : null;\n}\n\nfunction rowToJob(row: Record<string, unknown>): ProviderCorpusJobRecord {\n return {\n id: String(row.id),\n appId: String(row.app_id),\n ownerEmail: String(row.owner_email),\n name: String(row.name),\n mode: String(row.mode),\n status: String(row.status) as ProviderCorpusJobStatus,\n provider: String(row.provider),\n request: parseJsonRecord(row.request_json),\n pagination: parseNullableJsonRecord(row.pagination_json),\n batch: parseNullableJsonRecord(row.batch_json),\n search: parseJsonRecord(row.search_json),\n limits: parseJsonRecord(row.limits_json),\n checkpoint: parseJsonRecord(row.checkpoint_json),\n pagesProcessed: Number(row.pages_processed ?? 0),\n batchesProcessed: Number(row.batches_processed ?? 0),\n itemsProcessed: Number(row.items_processed ?? 0),\n matchedItems: Number(row.matched_items ?? 0),\n totalHits: Number(row.total_hits ?? 0),\n storedHits: Number(row.stored_hits ?? 0),\n error: row.error == null ? null : String(row.error),\n nextResumeAt: nullableNumber(row.next_resume_at),\n createdAt: Number(row.created_at ?? 0),\n updatedAt: Number(row.updated_at ?? 0),\n };\n}\n\nexport function _resetProviderCorpusJobsStoreForTests(): void {\n initPromise = undefined;\n}\n"]}
@@ -0,0 +1,146 @@
1
+ import type { ProviderApiRequestArgs } from "./index.js";
2
+ import { type ProviderCorpusJobStatus } from "./corpus-jobs-store.js";
3
+ type ProviderCorpusRuntime = {
4
+ executeRequest(args: ProviderApiRequestArgs): Promise<unknown>;
5
+ };
6
+ export interface CreateProviderCorpusJobActionOptions {
7
+ appId: string;
8
+ getRuntime: () => ProviderCorpusRuntime;
9
+ }
10
+ export declare function createProviderCorpusJobAction(options: CreateProviderCorpusJobActionOptions): import("../action.js").ActionDefinition<{
11
+ operation?: "start" | "status" | "continue" | "list" | "results" | "delete" | undefined;
12
+ jobId?: string | undefined;
13
+ name?: string | undefined;
14
+ mode?: "paginated-search" | "batch-search" | undefined;
15
+ request?: {
16
+ provider: string;
17
+ path: string;
18
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "PATCH" | undefined;
19
+ query?: unknown;
20
+ headers?: Record<string, unknown> | undefined;
21
+ body?: unknown;
22
+ auth?: "none" | "default" | undefined;
23
+ connectionId?: string | undefined;
24
+ accountId?: string | undefined;
25
+ timeoutMs?: unknown;
26
+ maxBytes?: unknown;
27
+ } | undefined;
28
+ pagination?: {
29
+ itemsPath?: string | undefined;
30
+ nextCursorPath?: string | undefined;
31
+ cursorPath?: string | undefined;
32
+ cursorParam?: string | undefined;
33
+ cursorBodyPath?: string | undefined;
34
+ pageParam?: string | undefined;
35
+ startPage?: unknown;
36
+ offsetParam?: string | undefined;
37
+ startOffset?: unknown;
38
+ pageSize?: unknown;
39
+ maxPages?: unknown;
40
+ } | undefined;
41
+ batch?: {
42
+ items?: unknown[] | undefined;
43
+ inputDatasetId?: string | undefined;
44
+ inputValuePath?: string | undefined;
45
+ batchSize?: unknown;
46
+ itemBodyPath?: string | undefined;
47
+ itemQueryParam?: string | undefined;
48
+ responseItemsPath?: string | undefined;
49
+ } | undefined;
50
+ search?: {
51
+ query?: string | undefined;
52
+ queries?: string[] | undefined;
53
+ terms?: string[] | undefined;
54
+ regex?: string | undefined;
55
+ regexFlags?: string | undefined;
56
+ matchMode?: "query" | "allTerms" | "anyTerm" | undefined;
57
+ caseSensitive?: boolean | undefined;
58
+ textPaths?: string[] | undefined;
59
+ idPaths?: string[] | undefined;
60
+ metadataPaths?: string[] | undefined;
61
+ contextChars?: unknown;
62
+ maxHits?: unknown;
63
+ maxHitsPerItem?: unknown;
64
+ maxFieldsPerItem?: unknown;
65
+ } | undefined;
66
+ limits?: {
67
+ pageBudget?: unknown;
68
+ batchBudget?: unknown;
69
+ runtimeMs?: unknown;
70
+ itemBudget?: unknown;
71
+ maxHits?: unknown;
72
+ } | undefined;
73
+ offset?: unknown;
74
+ limit?: unknown;
75
+ }, {
76
+ job: {
77
+ id: string;
78
+ name: string;
79
+ mode: string;
80
+ status: ProviderCorpusJobStatus;
81
+ provider: string;
82
+ createdAt: string;
83
+ updatedAt: string;
84
+ };
85
+ coverage: {
86
+ pagesProcessed: number;
87
+ batchesProcessed: number;
88
+ itemsProcessed: number;
89
+ matchedItems: number;
90
+ totalHits: number;
91
+ storedHits: number;
92
+ truncatedHits: boolean;
93
+ };
94
+ checkpoint: Record<string, unknown>;
95
+ error: string | null;
96
+ nextResumeAt: string | null;
97
+ } | {
98
+ jobs: {
99
+ id: string;
100
+ name: string;
101
+ mode: string;
102
+ status: ProviderCorpusJobStatus;
103
+ provider: string;
104
+ createdAt: string;
105
+ updatedAt: string;
106
+ }[];
107
+ total: number;
108
+ deleted?: undefined;
109
+ jobId?: undefined;
110
+ } | {
111
+ deleted: boolean;
112
+ jobId: string;
113
+ jobs?: undefined;
114
+ total?: undefined;
115
+ } | {
116
+ hits: Record<string, unknown>[];
117
+ offset: number;
118
+ limit: number;
119
+ job: {
120
+ id: string;
121
+ name: string;
122
+ mode: string;
123
+ status: ProviderCorpusJobStatus;
124
+ provider: string;
125
+ createdAt: string;
126
+ updatedAt: string;
127
+ };
128
+ coverage: {
129
+ pagesProcessed: number;
130
+ batchesProcessed: number;
131
+ itemsProcessed: number;
132
+ matchedItems: number;
133
+ totalHits: number;
134
+ storedHits: number;
135
+ truncatedHits: boolean;
136
+ };
137
+ checkpoint: Record<string, unknown>;
138
+ error: string | null;
139
+ nextResumeAt: string | null;
140
+ jobs?: undefined;
141
+ total?: undefined;
142
+ deleted?: undefined;
143
+ jobId?: undefined;
144
+ }>;
145
+ export {};
146
+ //# sourceMappingURL=corpus-jobs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"corpus-jobs.d.ts","sourceRoot":"","sources":["../../src/provider-api/corpus-jobs.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAqB,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAE5E,OAAO,EASL,KAAK,uBAAuB,EAC7B,MAAM,wBAAwB,CAAC;AAEhC,KAAK,qBAAqB,GAAG;IAC3B,cAAc,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAChE,CAAC;AAEF,MAAM,WAAW,oCAAoC;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,qBAAqB,CAAC;CACzC;AAiOD,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAa9C"}