@lakitu/sdk 0.1.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 (111) hide show
  1. package/README.md +166 -0
  2. package/convex/_generated/api.d.ts +45 -0
  3. package/convex/_generated/api.js +23 -0
  4. package/convex/_generated/dataModel.d.ts +58 -0
  5. package/convex/_generated/server.d.ts +143 -0
  6. package/convex/_generated/server.js +93 -0
  7. package/convex/cloud/CLAUDE.md +238 -0
  8. package/convex/cloud/_generated/api.ts +84 -0
  9. package/convex/cloud/_generated/component.ts +861 -0
  10. package/convex/cloud/_generated/dataModel.ts +60 -0
  11. package/convex/cloud/_generated/server.ts +156 -0
  12. package/convex/cloud/convex.config.ts +16 -0
  13. package/convex/cloud/index.ts +29 -0
  14. package/convex/cloud/intentSchema/generate.ts +447 -0
  15. package/convex/cloud/intentSchema/index.ts +16 -0
  16. package/convex/cloud/intentSchema/types.ts +418 -0
  17. package/convex/cloud/ksaPolicy.ts +554 -0
  18. package/convex/cloud/mail.ts +92 -0
  19. package/convex/cloud/schema.ts +322 -0
  20. package/convex/cloud/utils/kanbanContext.ts +229 -0
  21. package/convex/cloud/workflows/agentBoard.ts +451 -0
  22. package/convex/cloud/workflows/agentPrompt.ts +272 -0
  23. package/convex/cloud/workflows/agentThread.ts +374 -0
  24. package/convex/cloud/workflows/compileSandbox.ts +146 -0
  25. package/convex/cloud/workflows/crudBoard.ts +217 -0
  26. package/convex/cloud/workflows/crudKSAs.ts +262 -0
  27. package/convex/cloud/workflows/crudLorobeads.ts +371 -0
  28. package/convex/cloud/workflows/crudSkills.ts +205 -0
  29. package/convex/cloud/workflows/crudThreads.ts +708 -0
  30. package/convex/cloud/workflows/lifecycleSandbox.ts +1396 -0
  31. package/convex/cloud/workflows/sandboxConvex.ts +1046 -0
  32. package/convex/sandbox/README.md +90 -0
  33. package/convex/sandbox/_generated/api.d.ts +2934 -0
  34. package/convex/sandbox/_generated/api.js +23 -0
  35. package/convex/sandbox/_generated/dataModel.d.ts +60 -0
  36. package/convex/sandbox/_generated/server.d.ts +143 -0
  37. package/convex/sandbox/_generated/server.js +93 -0
  38. package/convex/sandbox/actions/bash.ts +130 -0
  39. package/convex/sandbox/actions/browser.ts +282 -0
  40. package/convex/sandbox/actions/file.ts +336 -0
  41. package/convex/sandbox/actions/lsp.ts +325 -0
  42. package/convex/sandbox/actions/pdf.ts +119 -0
  43. package/convex/sandbox/agent/codeExecLoop.ts +535 -0
  44. package/convex/sandbox/agent/decisions.ts +284 -0
  45. package/convex/sandbox/agent/index.ts +515 -0
  46. package/convex/sandbox/agent/subagents.ts +651 -0
  47. package/convex/sandbox/brandResearch/index.ts +417 -0
  48. package/convex/sandbox/context/index.ts +7 -0
  49. package/convex/sandbox/context/session.ts +402 -0
  50. package/convex/sandbox/convex.config.ts +17 -0
  51. package/convex/sandbox/index.ts +51 -0
  52. package/convex/sandbox/nodeActions/codeExec.ts +130 -0
  53. package/convex/sandbox/planning/beads.ts +187 -0
  54. package/convex/sandbox/planning/index.ts +8 -0
  55. package/convex/sandbox/planning/sync.ts +194 -0
  56. package/convex/sandbox/prompts/codeExec.ts +852 -0
  57. package/convex/sandbox/prompts/modes.ts +231 -0
  58. package/convex/sandbox/prompts/system.ts +142 -0
  59. package/convex/sandbox/schema.ts +510 -0
  60. package/convex/sandbox/state/artifacts.ts +99 -0
  61. package/convex/sandbox/state/checkpoints.ts +341 -0
  62. package/convex/sandbox/state/files.ts +383 -0
  63. package/convex/sandbox/state/index.ts +10 -0
  64. package/convex/sandbox/state/verification.actions.ts +268 -0
  65. package/convex/sandbox/state/verification.ts +101 -0
  66. package/convex/sandbox/tsconfig.json +25 -0
  67. package/convex/sandbox/utils/codeExecHelpers.ts +52 -0
  68. package/dist/cli/commands/build.d.ts +19 -0
  69. package/dist/cli/commands/build.d.ts.map +1 -0
  70. package/dist/cli/commands/build.js +223 -0
  71. package/dist/cli/commands/init.d.ts +16 -0
  72. package/dist/cli/commands/init.d.ts.map +1 -0
  73. package/dist/cli/commands/init.js +148 -0
  74. package/dist/cli/commands/publish.d.ts +12 -0
  75. package/dist/cli/commands/publish.d.ts.map +1 -0
  76. package/dist/cli/commands/publish.js +33 -0
  77. package/dist/cli/index.d.ts +14 -0
  78. package/dist/cli/index.d.ts.map +1 -0
  79. package/dist/cli/index.js +40 -0
  80. package/dist/sdk/builders.d.ts +104 -0
  81. package/dist/sdk/builders.d.ts.map +1 -0
  82. package/dist/sdk/builders.js +214 -0
  83. package/dist/sdk/index.d.ts +29 -0
  84. package/dist/sdk/index.d.ts.map +1 -0
  85. package/dist/sdk/index.js +38 -0
  86. package/dist/sdk/types.d.ts +107 -0
  87. package/dist/sdk/types.d.ts.map +1 -0
  88. package/dist/sdk/types.js +6 -0
  89. package/ksa/README.md +263 -0
  90. package/ksa/_generated/REFERENCE.md +2954 -0
  91. package/ksa/_generated/registry.ts +257 -0
  92. package/ksa/_shared/configReader.ts +302 -0
  93. package/ksa/_shared/configSchemas.ts +649 -0
  94. package/ksa/_shared/gateway.ts +175 -0
  95. package/ksa/_shared/ksaBehaviors.ts +411 -0
  96. package/ksa/_shared/ksaProxy.ts +248 -0
  97. package/ksa/_shared/localDb.ts +302 -0
  98. package/ksa/index.ts +134 -0
  99. package/package.json +93 -0
  100. package/runtime/browser/agent-browser.ts +330 -0
  101. package/runtime/entrypoint.ts +194 -0
  102. package/runtime/lsp/manager.ts +366 -0
  103. package/runtime/pdf/pdf-generator.ts +50 -0
  104. package/runtime/pdf/renderer.ts +357 -0
  105. package/runtime/pdf/schema.ts +97 -0
  106. package/runtime/services/file-watcher.ts +191 -0
  107. package/template/build.ts +307 -0
  108. package/template/e2b/Dockerfile +69 -0
  109. package/template/e2b/e2b.toml +13 -0
  110. package/template/e2b/prebuild.sh +68 -0
  111. package/template/e2b/start.sh +14 -0
@@ -0,0 +1,417 @@
1
+ /**
2
+ * Brand Research - Local Convex Functions
3
+ *
4
+ * These functions run in the sandbox's local Convex backend.
5
+ * The agent uses them to store and query discovered data,
6
+ * then syncs verified data to the cloud.
7
+ */
8
+
9
+ import { v } from "convex/values";
10
+ import { mutation, query, internalMutation } from "../_generated/server";
11
+
12
+ // ============================================================================
13
+ // Site Analysis
14
+ // ============================================================================
15
+
16
+ /**
17
+ * Store a site analysis result
18
+ */
19
+ export const storeSiteAnalysis = mutation({
20
+ args: {
21
+ domain: v.string(),
22
+ siteType: v.union(
23
+ v.literal("ecommerce"),
24
+ v.literal("saas"),
25
+ v.literal("service"),
26
+ v.literal("restaurant"),
27
+ v.literal("media"),
28
+ v.literal("other")
29
+ ),
30
+ platform: v.optional(v.string()),
31
+ confidence: v.number(),
32
+ navigation: v.array(v.object({
33
+ label: v.string(),
34
+ selector: v.optional(v.string()),
35
+ url: v.optional(v.string()),
36
+ purpose: v.string(),
37
+ })),
38
+ observations: v.array(v.string()),
39
+ productLocations: v.array(v.string()),
40
+ screenshotPath: v.optional(v.string()),
41
+ threadId: v.optional(v.string()),
42
+ },
43
+ handler: async (ctx, args) => {
44
+ // Check if we already have an analysis for this domain
45
+ const existing = await ctx.db
46
+ .query("discoveredSites")
47
+ .withIndex("by_domain", q => q.eq("domain", args.domain))
48
+ .first();
49
+
50
+ if (existing) {
51
+ // Update existing
52
+ await ctx.db.patch(existing._id, {
53
+ ...args,
54
+ analyzedAt: Date.now(),
55
+ });
56
+ return existing._id;
57
+ }
58
+
59
+ // Create new
60
+ return await ctx.db.insert("discoveredSites", {
61
+ ...args,
62
+ analyzedAt: Date.now(),
63
+ });
64
+ },
65
+ });
66
+
67
+ /**
68
+ * Get site analysis for a domain
69
+ */
70
+ export const getSiteAnalysis = query({
71
+ args: { domain: v.string() },
72
+ handler: async (ctx, args) => {
73
+ return await ctx.db
74
+ .query("discoveredSites")
75
+ .withIndex("by_domain", q => q.eq("domain", args.domain))
76
+ .first();
77
+ },
78
+ });
79
+
80
+ // ============================================================================
81
+ // URL Discovery
82
+ // ============================================================================
83
+
84
+ /**
85
+ * Store discovered URLs
86
+ */
87
+ export const storeDiscoveredUrls = mutation({
88
+ args: {
89
+ domain: v.string(),
90
+ urls: v.array(v.object({
91
+ url: v.string(),
92
+ urlType: v.union(
93
+ v.literal("product"),
94
+ v.literal("listing"),
95
+ v.literal("pricing"),
96
+ v.literal("other"),
97
+ v.literal("skip")
98
+ ),
99
+ confidence: v.number(),
100
+ })),
101
+ threadId: v.optional(v.string()),
102
+ },
103
+ handler: async (ctx, args) => {
104
+ const inserted: string[] = [];
105
+
106
+ for (const urlData of args.urls) {
107
+ // Skip if already exists
108
+ const existing = await ctx.db
109
+ .query("discoveredUrls")
110
+ .withIndex("by_domain", q => q.eq("domain", args.domain))
111
+ .filter(q => q.eq(q.field("url"), urlData.url))
112
+ .first();
113
+
114
+ if (existing) continue;
115
+
116
+ const id = await ctx.db.insert("discoveredUrls", {
117
+ domain: args.domain,
118
+ url: urlData.url,
119
+ urlType: urlData.urlType,
120
+ confidence: urlData.confidence,
121
+ scraped: false,
122
+ discoveredAt: Date.now(),
123
+ threadId: args.threadId,
124
+ });
125
+ inserted.push(id);
126
+ }
127
+
128
+ return inserted;
129
+ },
130
+ });
131
+
132
+ /**
133
+ * Get URLs to scrape (unscraped product/listing pages)
134
+ */
135
+ export const getUrlsToScrape = query({
136
+ args: {
137
+ domain: v.string(),
138
+ limit: v.optional(v.number()),
139
+ },
140
+ handler: async (ctx, args) => {
141
+ const limit = args.limit || 20;
142
+
143
+ return await ctx.db
144
+ .query("discoveredUrls")
145
+ .withIndex("by_domain", q => q.eq("domain", args.domain))
146
+ .filter(q =>
147
+ q.and(
148
+ q.eq(q.field("scraped"), false),
149
+ q.or(
150
+ q.eq(q.field("urlType"), "product"),
151
+ q.eq(q.field("urlType"), "listing")
152
+ )
153
+ )
154
+ )
155
+ .take(limit);
156
+ },
157
+ });
158
+
159
+ /**
160
+ * Mark URL as scraped
161
+ */
162
+ export const markUrlScraped = mutation({
163
+ args: {
164
+ urlId: v.id("discoveredUrls"),
165
+ productCount: v.optional(v.number()),
166
+ error: v.optional(v.string()),
167
+ },
168
+ handler: async (ctx, args) => {
169
+ await ctx.db.patch(args.urlId, {
170
+ scraped: true,
171
+ scrapedAt: Date.now(),
172
+ productCount: args.productCount,
173
+ error: args.error,
174
+ });
175
+ },
176
+ });
177
+
178
+ /**
179
+ * Get URL scraping stats
180
+ */
181
+ export const getUrlStats = query({
182
+ args: { domain: v.string() },
183
+ handler: async (ctx, args) => {
184
+ const urls = await ctx.db
185
+ .query("discoveredUrls")
186
+ .withIndex("by_domain", q => q.eq("domain", args.domain))
187
+ .collect();
188
+
189
+ return {
190
+ total: urls.length,
191
+ product: urls.filter(u => u.urlType === "product").length,
192
+ listing: urls.filter(u => u.urlType === "listing").length,
193
+ scraped: urls.filter(u => u.scraped).length,
194
+ pending: urls.filter(u => !u.scraped && (u.urlType === "product" || u.urlType === "listing")).length,
195
+ failed: urls.filter(u => u.error).length,
196
+ };
197
+ },
198
+ });
199
+
200
+ // ============================================================================
201
+ // Product Discovery
202
+ // ============================================================================
203
+
204
+ /**
205
+ * Store discovered products
206
+ */
207
+ export const storeProducts = mutation({
208
+ args: {
209
+ domain: v.string(),
210
+ sourceUrl: v.string(),
211
+ products: v.array(v.object({
212
+ name: v.string(),
213
+ type: v.union(
214
+ v.literal("physical"),
215
+ v.literal("saas"),
216
+ v.literal("service")
217
+ ),
218
+ price: v.optional(v.number()),
219
+ currency: v.optional(v.string()),
220
+ description: v.optional(v.string()),
221
+ images: v.array(v.string()),
222
+ category: v.optional(v.string()),
223
+ variants: v.optional(v.array(v.object({
224
+ name: v.string(),
225
+ price: v.optional(v.number()),
226
+ sku: v.optional(v.string()),
227
+ available: v.optional(v.boolean()),
228
+ }))),
229
+ })),
230
+ threadId: v.optional(v.string()),
231
+ },
232
+ handler: async (ctx, args) => {
233
+ const inserted: string[] = [];
234
+
235
+ for (const product of args.products) {
236
+ // Check for duplicates by name and domain
237
+ const existing = await ctx.db
238
+ .query("discoveredProducts")
239
+ .withIndex("by_domain", q => q.eq("domain", args.domain))
240
+ .filter(q => q.eq(q.field("name"), product.name))
241
+ .first();
242
+
243
+ if (existing) continue;
244
+
245
+ const id = await ctx.db.insert("discoveredProducts", {
246
+ domain: args.domain,
247
+ sourceUrl: args.sourceUrl,
248
+ ...product,
249
+ verified: false,
250
+ extractedAt: Date.now(),
251
+ threadId: args.threadId,
252
+ syncedToCloud: false,
253
+ });
254
+ inserted.push(id);
255
+ }
256
+
257
+ return inserted;
258
+ },
259
+ });
260
+
261
+ /**
262
+ * Get all products for a domain
263
+ */
264
+ export const getProducts = query({
265
+ args: {
266
+ domain: v.string(),
267
+ limit: v.optional(v.number()),
268
+ verifiedOnly: v.optional(v.boolean()),
269
+ },
270
+ handler: async (ctx, args) => {
271
+ let query = ctx.db
272
+ .query("discoveredProducts")
273
+ .withIndex("by_domain", q => q.eq("domain", args.domain));
274
+
275
+ if (args.verifiedOnly) {
276
+ query = query.filter(q => q.eq(q.field("verified"), true));
277
+ }
278
+
279
+ return await query.take(args.limit || 100);
280
+ },
281
+ });
282
+
283
+ /**
284
+ * Get product stats for a domain
285
+ */
286
+ export const getProductStats = query({
287
+ args: { domain: v.string() },
288
+ handler: async (ctx, args) => {
289
+ const products = await ctx.db
290
+ .query("discoveredProducts")
291
+ .withIndex("by_domain", q => q.eq("domain", args.domain))
292
+ .collect();
293
+
294
+ return {
295
+ total: products.length,
296
+ verified: products.filter(p => p.verified).length,
297
+ unverified: products.filter(p => !p.verified).length,
298
+ synced: products.filter(p => p.syncedToCloud).length,
299
+ withImages: products.filter(p => p.images.length > 0).length,
300
+ withPrice: products.filter(p => p.price !== undefined).length,
301
+ byType: {
302
+ physical: products.filter(p => p.type === "physical").length,
303
+ saas: products.filter(p => p.type === "saas").length,
304
+ service: products.filter(p => p.type === "service").length,
305
+ },
306
+ };
307
+ },
308
+ });
309
+
310
+ /**
311
+ * Verify a product
312
+ */
313
+ export const verifyProduct = mutation({
314
+ args: {
315
+ productId: v.id("discoveredProducts"),
316
+ verified: v.boolean(),
317
+ notes: v.optional(v.string()),
318
+ },
319
+ handler: async (ctx, args) => {
320
+ await ctx.db.patch(args.productId, {
321
+ verified: args.verified,
322
+ verificationNotes: args.notes,
323
+ });
324
+ },
325
+ });
326
+
327
+ /**
328
+ * Mark product as synced to cloud
329
+ */
330
+ export const markProductSynced = mutation({
331
+ args: {
332
+ productId: v.id("discoveredProducts"),
333
+ cloudProductId: v.string(),
334
+ },
335
+ handler: async (ctx, args) => {
336
+ await ctx.db.patch(args.productId, {
337
+ syncedToCloud: true,
338
+ cloudProductId: args.cloudProductId,
339
+ });
340
+ },
341
+ });
342
+
343
+ /**
344
+ * Get unsynced products
345
+ */
346
+ export const getUnsyncedProducts = query({
347
+ args: {
348
+ domain: v.optional(v.string()),
349
+ verifiedOnly: v.optional(v.boolean()),
350
+ limit: v.optional(v.number()),
351
+ },
352
+ handler: async (ctx, args) => {
353
+ let query = ctx.db
354
+ .query("discoveredProducts")
355
+ .withIndex("by_synced", q => q.eq("syncedToCloud", false));
356
+
357
+ if (args.domain) {
358
+ query = query.filter(q => q.eq(q.field("domain"), args.domain));
359
+ }
360
+
361
+ if (args.verifiedOnly) {
362
+ query = query.filter(q => q.eq(q.field("verified"), true));
363
+ }
364
+
365
+ return await query.take(args.limit || 50);
366
+ },
367
+ });
368
+
369
+ // ============================================================================
370
+ // Research Summary
371
+ // ============================================================================
372
+
373
+ /**
374
+ * Get full research summary for a domain
375
+ */
376
+ export const getResearchSummary = query({
377
+ args: { domain: v.string() },
378
+ handler: async (ctx, args) => {
379
+ const site = await ctx.db
380
+ .query("discoveredSites")
381
+ .withIndex("by_domain", q => q.eq("domain", args.domain))
382
+ .first();
383
+
384
+ const urls = await ctx.db
385
+ .query("discoveredUrls")
386
+ .withIndex("by_domain", q => q.eq("domain", args.domain))
387
+ .collect();
388
+
389
+ const products = await ctx.db
390
+ .query("discoveredProducts")
391
+ .withIndex("by_domain", q => q.eq("domain", args.domain))
392
+ .collect();
393
+
394
+ return {
395
+ domain: args.domain,
396
+ site: site ? {
397
+ siteType: site.siteType,
398
+ platform: site.platform,
399
+ confidence: site.confidence,
400
+ analyzedAt: site.analyzedAt,
401
+ navigationHints: site.navigation.length,
402
+ } : null,
403
+ urls: {
404
+ total: urls.length,
405
+ product: urls.filter(u => u.urlType === "product").length,
406
+ listing: urls.filter(u => u.urlType === "listing").length,
407
+ scraped: urls.filter(u => u.scraped).length,
408
+ },
409
+ products: {
410
+ total: products.length,
411
+ verified: products.filter(p => p.verified).length,
412
+ synced: products.filter(p => p.syncedToCloud).length,
413
+ withImages: products.filter(p => p.images.length > 0).length,
414
+ },
415
+ };
416
+ },
417
+ });
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Context - Context Window Orchestration
3
+ *
4
+ * Session persistence, caching, and dependency tracking.
5
+ */
6
+
7
+ export * as session from "./session";