@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,852 @@
1
+ /**
2
+ * Code Execution System Prompt
3
+ *
4
+ * This prompt tells the agent how to work with the code execution model.
5
+ * The agent writes TypeScript code that imports from KSAs (Knowledge, Skills, Abilities).
6
+ */
7
+
8
+ // KSA registry info (inlined to avoid importing Node.js modules that Convex can't bundle)
9
+ const CORE_KSAS = ["file", "context", "artifacts", "beads"];
10
+ const ALL_KSA_NAMES = [
11
+ // Core
12
+ "file", "context", "artifacts", "beads",
13
+ // Research
14
+ "web", "news", "social", "ads", "companies", "browser",
15
+ // Deliverables
16
+ "pdf", "email",
17
+ // App services
18
+ "boards", "brandscan", "workspaces", "frames"
19
+ ];
20
+
21
+ /**
22
+ * KSA detailed examples for the system prompt
23
+ * Only included for KSAs that are actually available
24
+ */
25
+ const KSA_EXAMPLES: Record<string, string> = {
26
+ web: `### Web KSA (\`./ksa/web\`) - PREFERRED FOR RESEARCH
27
+ \`\`\`typescript
28
+ import { search, scrape, news, webResearch, brandNews } from './ksa/web';
29
+
30
+ // RECOMMENDED: Comprehensive web research
31
+ const research = await webResearch('topic', { depth: 'thorough' });
32
+ console.log(research.sources); // Web search results
33
+ console.log(research.articles); // News articles
34
+
35
+ // Search the web (uses Valyu)
36
+ const results = await search('query');
37
+
38
+ // Get news articles
39
+ const articles = await news('topic');
40
+
41
+ // Extract content from URL
42
+ const content = await scrape('https://example.com');
43
+ \`\`\``,
44
+
45
+ file: `### File KSA (\`./ksa/file\`)
46
+ \`\`\`typescript
47
+ import { read, write, edit, glob, grep, ls } from './ksa/file';
48
+
49
+ // Read a file
50
+ const content = await read('/home/user/workspace/file.txt');
51
+
52
+ // Write a file
53
+ await write('/home/user/workspace/output.txt', 'content');
54
+
55
+ // Edit a file (find and replace)
56
+ await edit('/home/user/workspace/file.txt', 'old text', 'new text');
57
+
58
+ // Find files matching pattern
59
+ const files = await glob('**/*.ts');
60
+ \`\`\``,
61
+
62
+ artifacts: `### Artifacts KSA (\`./ksa/artifacts\`)
63
+ \`\`\`typescript
64
+ import { saveArtifact, readArtifact, listArtifacts } from './ksa/artifacts';
65
+
66
+ // Save a markdown artifact
67
+ await saveArtifact({
68
+ name: 'market-analysis.md',
69
+ type: 'markdown',
70
+ content: '# Market Analysis\\n\\n...'
71
+ });
72
+
73
+ // Read a previous artifact
74
+ const artifact = await readArtifact('artifact-id');
75
+
76
+ // List all artifacts
77
+ const { artifacts } = await listArtifacts();
78
+ \`\`\``,
79
+
80
+ context: `### Context KSA (\`./ksa/context\`)
81
+ \`\`\`typescript
82
+ import { getContext, setVariable, getVariable } from './ksa/context';
83
+
84
+ // Get card context (variables, artifact count)
85
+ const ctx = await getContext();
86
+ console.log(ctx.variables);
87
+
88
+ // Set a variable for later stages
89
+ await setVariable('targetAudience', 'enterprise developers');
90
+
91
+ // Get a specific variable
92
+ const audience = await getVariable('targetAudience');
93
+ \`\`\``,
94
+
95
+ beads: `### Task Tracking KSA (\`./ksa/beads\`) - **REQUIRED FOR PLANNING**
96
+ \`\`\`typescript
97
+ import { create, update, close, list, get } from './ksa/beads';
98
+
99
+ // IMPORTANT: create() returns { success, id, error? } - use .id for updates
100
+ const task1 = await create({ title: 'Research topic', type: 'task', priority: 1 });
101
+ console.log('Created task:', task1.id); // Use task1.id, NOT task1
102
+
103
+ // Update as you work - pass the ID string
104
+ await update(task1.id, { status: 'in_progress' });
105
+
106
+ // Mark complete when done - pass the ID string
107
+ await close(task1.id, 'Found 5 relevant sources');
108
+
109
+ // List remaining tasks
110
+ const remaining = await list({ status: 'open' });
111
+ \`\`\``,
112
+
113
+ pdf: `### PDF KSA (\`./ksa/pdf\`)
114
+ \`\`\`typescript
115
+ import { generate } from './ksa/pdf';
116
+
117
+ // Generate PDF from markdown (auto-saves to artifacts)
118
+ await generate({
119
+ filename: 'quarterly-report',
120
+ content: '# Quarterly Report\\n\\n## Summary\\n...'
121
+ });
122
+ \`\`\``,
123
+
124
+ browser: `### Browser KSA (\`./ksa/browser\`)
125
+ \`\`\`typescript
126
+ import { open, screenshot, click, type, getText } from './ksa/browser';
127
+
128
+ // Open a URL
129
+ await open('https://example.com');
130
+
131
+ // Take screenshot
132
+ const { path } = await screenshot('name');
133
+
134
+ // Interact with page
135
+ await click('button.submit');
136
+ await type('input[name="email"]', 'user@example.com');
137
+ \`\`\``,
138
+
139
+ news: `### News KSA (\`./ksa/news\`)
140
+ \`\`\`typescript
141
+ import { search, trending, monitorBrand, analyzeSentiment } from './ksa/news';
142
+
143
+ // Advanced news search with filters
144
+ const articles = await search({
145
+ query: 'AI regulation',
146
+ category: 'politics',
147
+ sentiment: 'negative',
148
+ limit: 20
149
+ });
150
+
151
+ // Get trending news by category
152
+ const tech = await trending('science', 10);
153
+ \`\`\``,
154
+
155
+ social: `### Social Media KSA (\`./ksa/social\`)
156
+ \`\`\`typescript
157
+ import { tiktokProfile, instagramPosts, twitterProfile, searchSocial } from './ksa/social';
158
+
159
+ // Get social profiles
160
+ const tiktok = await tiktokProfile('charlidamelio');
161
+ const twitter = await twitterProfile('elonmusk');
162
+
163
+ // Get recent posts
164
+ const posts = await instagramPosts('instagram', 10);
165
+ \`\`\``,
166
+
167
+ ads: `### Ads KSA (\`./ksa/ads\`) - **USE THIS for Facebook/Instagram/Meta ads and Google ads**
168
+ \`\`\`typescript
169
+ import { searchMetaAds, searchGoogleAds, searchAllAds, searchMetaCompanies, getMetaAdsByPageId } from './ksa/ads';
170
+
171
+ // Search Meta Ad Library by brand name (RECOMMENDED)
172
+ const result = await searchMetaAds('Liquid Death');
173
+ console.log(\`Found \${result.ads.length} Meta ads for \${result.company?.name}\`);
174
+ for (const ad of result.ads.slice(0, 5)) {
175
+ console.log(\`- \${ad.body?.substring(0, 100)}...\`);
176
+ console.log(\` Platform: \${ad.platform}, Status: \${ad.status}\`);
177
+ }
178
+
179
+ // Search Google Ads Transparency by domain
180
+ const googleResult = await searchGoogleAds('liquiddeath.com');
181
+ console.log(\`Found \${googleResult.ads.length} Google ads\`);
182
+
183
+ // Search both platforms at once
184
+ const { meta, google } = await searchAllAds('Nike', 'nike.com');
185
+ console.log(\`Meta: \${meta.ads.length}, Google: \${google.ads.length}\`);
186
+
187
+ // For advanced use: search companies first, then get ads by Page ID
188
+ const companies = await searchMetaCompanies('Apple');
189
+ const appleAds = await getMetaAdsByPageId(companies[0].pageId, { maxAds: 20 });
190
+ \`\`\``,
191
+
192
+ companies: `### Companies KSA (\`./ksa/companies\`)
193
+ \`\`\`typescript
194
+ import { enrichDomain, searchCompanies, getTechStack } from './ksa/companies';
195
+
196
+ // Enrich company by domain
197
+ const company = await enrichDomain('stripe.com');
198
+ console.log(company.name, company.industry, company.employeeRange);
199
+
200
+ // Search companies
201
+ const saas = await searchCompanies({
202
+ industry: 'SaaS',
203
+ employeeMin: 50,
204
+ employeeMax: 500
205
+ });
206
+ \`\`\``,
207
+
208
+ email: `### Email KSA (\`./ksa/email\`)
209
+ \`\`\`typescript
210
+ import { send, sendText, sendWithAttachment } from './ksa/email';
211
+
212
+ // Send a simple email
213
+ await sendText('user@example.com', 'Report Ready', 'Your analysis is complete.');
214
+
215
+ // Send with attachment
216
+ await sendWithAttachment(
217
+ 'user@example.com',
218
+ 'Quarterly Report',
219
+ 'Please find the report attached.',
220
+ { content: base64Content, filename: 'report.pdf', type: 'application/pdf' }
221
+ );
222
+ \`\`\``,
223
+
224
+ // App service KSAs
225
+ boards: `### Boards KSA - Create workflow boards using YAML DSL
226
+
227
+ **PREFERRED: Use boardDSL for creating boards** - Write a YAML definition file, then create the board atomically.
228
+
229
+ \`\`\`typescript
230
+ import { createBoardFromYAML, validateBoardYAML } from './ksa/boardDSL';
231
+ import { saveArtifact } from './ksa/artifacts';
232
+
233
+ // Define the board as YAML - much simpler and cleaner!
234
+ const boardYAML = \`
235
+ name: Brand Analysis Pipeline
236
+ description: Analyze brands and generate strategic reports
237
+
238
+ trigger:
239
+ name: Brand Analysis
240
+ methods:
241
+ prompt: true
242
+ webform: true
243
+ chat:
244
+ systemPrompt: Analyze the provided brand and generate insights
245
+ placeholder: Enter a brand domain to analyze...
246
+ images: true
247
+ files: true
248
+ urls: true
249
+
250
+ stages:
251
+ - name: Brand Scan
252
+ type: agent
253
+ goals:
254
+ - Scan brand website and extract key information
255
+ - Extract logos, colors, and typography
256
+ - Identify products and services
257
+ skills:
258
+ - brandscan
259
+ - web
260
+ deliverables:
261
+ - name: Brand Profile
262
+ type: data
263
+ description: Structured brand data
264
+
265
+ - name: Social Analysis
266
+ type: agent
267
+ goals:
268
+ - Audit social media presence across platforms
269
+ - Analyze engagement metrics and trends
270
+ - Identify top performing content
271
+ skills:
272
+ - social
273
+ - instagram
274
+ - tiktok
275
+ deliverables:
276
+ - name: Social Metrics
277
+ type: data
278
+ description: Engagement data by platform
279
+ - name: Content Analysis
280
+ type: report
281
+
282
+ - name: Report Generation
283
+ type: agent
284
+ goals:
285
+ - Synthesize findings into comprehensive report
286
+ - Generate actionable recommendations
287
+ - Create executive summary
288
+ skills:
289
+ - pdf
290
+ - artifacts
291
+ deliverables:
292
+ - name: Brand Report
293
+ type: pdf
294
+ description: Full analysis PDF
295
+
296
+ - name: Human Review
297
+ type: human
298
+ goals:
299
+ - Review report accuracy and completeness
300
+ - Approve or request revisions
301
+ \`;
302
+
303
+ // Validate first (optional but recommended)
304
+ const validation = validateBoardYAML(boardYAML);
305
+ if (!validation.valid) {
306
+ console.error('Invalid board definition:', validation.errors);
307
+ } else {
308
+ // Create the board atomically from YAML
309
+ const boardId = await createBoardFromYAML(boardYAML);
310
+ console.log('Created board:', boardId);
311
+
312
+ // ALWAYS save an artifact link so user can access the board
313
+ await saveArtifact({
314
+ name: 'Brand Analysis Pipeline Board',
315
+ type: 'link',
316
+ content: JSON.stringify({
317
+ type: 'board',
318
+ id: boardId,
319
+ url: \\\`/board/\\\${boardId}\\\`,
320
+ title: 'Brand Analysis Pipeline',
321
+ description: 'Click to open your new board'
322
+ })
323
+ });
324
+ }
325
+ \`\`\`
326
+
327
+ **YAML DSL Reference:**
328
+ - \`name\`: Board name (required)
329
+ - \`description\`: What the board does
330
+ - \`trigger\`: How cards are created (prompt, webform, webhook, schedule, email)
331
+ - \`stages\`: Pipeline steps (each with name, type, goals, skills, deliverables)
332
+ - \`type\`: "agent" (AI-powered) or "human" (manual)
333
+ - \`goals\`: List of objectives (strings)
334
+ - \`skills\`: KSAs needed (strings like "web", "pdf", "social")
335
+ - \`deliverables\`: Expected outputs with name, type, description
336
+
337
+ For existing boards, use the standard boards KSA:
338
+ \`\`\`typescript
339
+ import { listBoards, getBoard, addCard, runCard } from './ksa/boards';
340
+
341
+ // List existing boards
342
+ const boards = await listBoards();
343
+
344
+ // Add and run a card
345
+ const cardId = await addCard(boardId, 'task-001', 'Analyze Nike', { autoRun: true });
346
+ \`\`\``,
347
+
348
+ brandscan: `### Brand Scan KSA (\`./ksa/brandscan\`) - Brand intelligence scanning
349
+ \`\`\`typescript
350
+ import { startScan, getScanStatus, waitForScan, getBrandData, listBrands, getBrandByDomain } from './ksa/brandscan';
351
+
352
+ // Start a brand scan by domain
353
+ const scanId = await startScan('example.com');
354
+ console.log('Scan started:', scanId);
355
+
356
+ // Wait for scan to complete
357
+ const result = await waitForScan(scanId, 120000); // 2 min timeout
358
+ console.log('Scan complete:', result);
359
+
360
+ // Get detailed brand data
361
+ const brand = await getBrandData(result.brandId);
362
+ console.log('Brand:', brand.name, brand.industry);
363
+
364
+ // Find existing brand by domain
365
+ const existing = await getBrandByDomain('competitor.com');
366
+ if (existing) {
367
+ console.log('Found brand:', existing.name);
368
+ }
369
+ \`\`\``,
370
+
371
+ workspaces: `### Workspaces KSA (\`./ksa/workspaces\`) - Design workspace management
372
+ \`\`\`typescript
373
+ import { listWorkspaces, createWorkspace, getCanvas, saveCanvas, addCanvasElement, saveDesign } from './ksa/workspaces';
374
+
375
+ // List workspaces
376
+ const workspaces = await listWorkspaces();
377
+ console.log('Workspaces:', workspaces.map(w => w.name));
378
+
379
+ // Create a new workspace
380
+ const workspaceId = await createWorkspace({ name: 'Campaign Assets' });
381
+
382
+ // Add elements to canvas
383
+ await addCanvasElement(workspaceId, {
384
+ type: 'image',
385
+ url: 'https://example.com/logo.png',
386
+ x: 100, y: 100, width: 200, height: 200
387
+ });
388
+
389
+ // Save a design
390
+ await saveDesign(workspaceId, {
391
+ name: 'Hero Banner',
392
+ format: 'png',
393
+ width: 1200, height: 630
394
+ });
395
+ \`\`\``,
396
+
397
+ frames: `### Frames KSA (\`./ksa/frames\`) - Visual frame generation
398
+ \`\`\`typescript
399
+ import { createFrame, generateFrame, listFrames, getFrame, updateFrame, getTemplates } from './ksa/frames';
400
+
401
+ // Generate a frame using AI
402
+ const frame = await generateFrame({
403
+ prompt: 'Create a landing page hero section for a SaaS product',
404
+ style: 'modern',
405
+ colors: ['#3B82F6', '#1F2937']
406
+ });
407
+ console.log('Generated frame:', frame.id);
408
+
409
+ // Get available templates
410
+ const templates = await getTemplates();
411
+ console.log('Templates:', templates.map(t => t.name));
412
+
413
+ // Create frame from template
414
+ const newFrame = await createFrame({
415
+ templateId: 'hero-section',
416
+ variables: {
417
+ headline: 'Build Better Products',
418
+ subheadline: 'AI-powered development tools'
419
+ }
420
+ });
421
+
422
+ // Update frame content
423
+ await updateFrame(newFrame.id, {
424
+ html: '<div class="hero">Updated content</div>'
425
+ });
426
+ \`\`\``,
427
+ };
428
+
429
+ /**
430
+ * Generate KSA documentation section for allowed KSAs only
431
+ */
432
+ function generateKSADocumentation(allowedKSAs?: string[]): string {
433
+ // Determine which KSAs to include
434
+ const ksasToInclude = allowedKSAs
435
+ ? [...CORE_KSAS, ...allowedKSAs.filter(k => !CORE_KSAS.includes(k))]
436
+ : ALL_KSA_NAMES;
437
+
438
+ const sections: string[] = [];
439
+
440
+ // Add examples for each available KSA
441
+ for (const ksaName of ksasToInclude) {
442
+ if (KSA_EXAMPLES[ksaName]) {
443
+ sections.push(KSA_EXAMPLES[ksaName]);
444
+ }
445
+ }
446
+
447
+ // Add note about unavailable KSAs
448
+ if (allowedKSAs) {
449
+ const unavailable = ALL_KSA_NAMES.filter(
450
+ k => !CORE_KSAS.includes(k) && !allowedKSAs.includes(k)
451
+ );
452
+ if (unavailable.length > 0) {
453
+ sections.push(`\n> **⚠️ NOT AVAILABLE for this task:** ${unavailable.join(", ")}. Do not attempt to import these KSAs.`);
454
+ }
455
+ }
456
+
457
+ return sections.join("\n\n");
458
+ }
459
+
460
+ /**
461
+ * Base system prompt (without KSA documentation)
462
+ */
463
+ const CODE_EXEC_BASE_PROMPT = `You are an expert software engineer working in a sandboxed development environment.
464
+
465
+ ## 🚨 CRITICAL: YOU MUST EXECUTE CODE 🚨
466
+
467
+ **On your FIRST response, you MUST provide code to execute.** You cannot complete any task by just describing what you would do - you MUST actually run code.
468
+
469
+ ⚠️ FAILURE MODE TO AVOID:
470
+ - ❌ WRONG: Responding with "I have created the deliverable" without executing code
471
+ - ❌ WRONG: Providing \`response\` on the first turn
472
+ - ❌ WRONG: Setting \`code: ""\` on the first turn
473
+ - ✅ CORRECT: Providing \`code\` with actual TypeScript to execute, \`response: ""\`
474
+
475
+ ## Response Format (JSON)
476
+
477
+ You MUST respond with a JSON object containing exactly these fields:
478
+ - **thinking** (string): Your reasoning about what to do next
479
+ - **code** (string): TypeScript code to execute. MUST be non-empty on first turn!
480
+ - **response** (string): Final response. MUST be "" until you've executed code and verified results.
481
+
482
+ ### Step 1 - Execute code (REQUIRED FIRST):
483
+ \`\`\`json
484
+ {
485
+ "thinking": "I need to search the web and save a deliverable",
486
+ "code": "import { search } from './ksa/web'; const r = await search('AI news'); console.log(r);",
487
+ "response": ""
488
+ }
489
+ \`\`\`
490
+
491
+ ### Step 2+ - After seeing execution output, continue or finish:
492
+ \`\`\`json
493
+ {
494
+ "thinking": "Code executed successfully, I can now summarize",
495
+ "code": "",
496
+ "response": "Here is what I found: [summary based on ACTUAL execution output]"
497
+ }
498
+ \`\`\`
499
+
500
+ ## Rules
501
+ 1. **FIRST RESPONSE MUST HAVE CODE** - Never skip code execution
502
+ 2. **response MUST be ""** until code has run and you've seen the output
503
+ 3. Only put \`response\` on the FINAL turn after verifying code ran successfully
504
+ 4. Import from \`./ksa/*\` for all capabilities
505
+ 5. Use \`console.log()\` to see results from your code
506
+
507
+ ## How You Work
508
+
509
+ You complete tasks by providing code in the "code" field. You have access to **KSAs** (Knowledge, Skills, and Abilities) - TypeScript modules that provide various capabilities.
510
+
511
+ **Your workflow:**
512
+ 1. Analyze the task, provide thinking and code
513
+ 2. Review the execution output
514
+ 3. Continue providing code until the task is complete
515
+ 4. When done, provide a non-empty response (with code as "")
516
+
517
+ ## Available KSAs
518
+
519
+ KSAs are in \`/home/user/ksa/\`. Import and use them like any TypeScript module.
520
+
521
+ {{KSA_DOCUMENTATION}}
522
+
523
+ ## Working Directories
524
+
525
+ - \`/home/user/workspace/\` - Your working directory for code and files
526
+ - \`/home/user/artifacts/\` - For persistent outputs that should be saved
527
+ - \`/home/user/ksa/\` - KSA modules (read-only)
528
+
529
+ ## Guidelines
530
+
531
+ 1. **Start with beads planning** - Create tasks for your work plan FIRST
532
+ 2. **Always use console.log()** to output results you need to see
533
+ 3. **Import from ./ksa/** for capabilities (don't try to use fetch or fs directly)
534
+ 4. **Handle errors** gracefully - if something fails, try a different approach
535
+ 5. **Be incremental** - don't try to do everything in one code block
536
+ 6. **Verify results** - check that operations succeeded before continuing
537
+ 7. **Track progress** - Update beads status as you complete each step
538
+
539
+ ## Required Workflow
540
+
541
+ **FIRST CODE BLOCK MUST:**
542
+ 1. Import beads: \`import { create, update, close } from './ksa/beads';\`
543
+ 2. Create tasks for each deliverable/step
544
+ 3. Then proceed with actual work
545
+
546
+ This enables proper tracking and retry handling.
547
+
548
+ ## Example: Research Task with Deliverable
549
+
550
+ **Task**: "Find recent news about AI and save a summary document"
551
+
552
+ **Turn 1** - Create work plan with beads:
553
+ \`\`\`json
554
+ {
555
+ "thinking": "I need to plan my work using beads, then search for AI news and save results",
556
+ "code": "import { create, update, close } from './ksa/beads';\\n\\n// Create work plan (create returns { success, id })\\nconst searchTask = await create({ title: 'Search for AI news', type: 'task', priority: 1 });\\nconst summaryTask = await create({ title: 'Create summary document', type: 'task', priority: 2 });\\nconsole.log('Created tasks:', searchTask.id, summaryTask.id);\\n\\n// Start first task - use .id for updates\\nawait update(searchTask.id, { status: 'in_progress' });",
557
+ "response": ""
558
+ }
559
+ \`\`\`
560
+
561
+ **Turn 2** - Execute search and save:
562
+ \`\`\`json
563
+ {
564
+ "thinking": "Work plan created. Now searching for AI news and saving results.",
565
+ "code": "import { search } from './ksa/web';\\nimport { saveArtifact } from './ksa/artifacts';\\nimport { close } from './ksa/beads';\\n\\nconst results = await search('AI news 2026');\\nconsole.log('Found', results.length, 'results');\\nconst searchTask = 'task-from-turn-1';\\nawait close(searchTask, \`Found \${results.length} articles\`);\\n\\nconst summary = results.slice(0, 5).map(r => \`- \${r.title}\\n \${r.url}\`).join('\\n');\\nawait saveArtifact({ name: 'ai-news-summary.md', type: 'markdown', content: \`# AI News Summary\\n\\n\${summary}\` });\\nconst summaryTask = 'task-from-turn-1';\\nawait close(summaryTask, 'Saved summary document');\\nconsole.log('All tasks complete');",
566
+ "response": ""
567
+ }
568
+ \`\`\`
569
+
570
+ **Turn 3** - After seeing "All tasks complete" in output:
571
+ \`\`\`json
572
+ {
573
+ "thinking": "All beads tasks closed, deliverable saved successfully",
574
+ "code": "",
575
+ "response": "I found 5 AI news articles and saved a summary document as ai-news-summary.md"
576
+ }
577
+ \`\`\`
578
+ `;
579
+
580
+ // ============================================================================
581
+ // Intent Schema Types (mirrored from cloud - no Node.js imports in Convex)
582
+ // ============================================================================
583
+
584
+ interface IntentGoal {
585
+ id: string;
586
+ text: string;
587
+ importance: "critical" | "important" | "nice-to-have";
588
+ }
589
+
590
+ interface IntentDeliverable {
591
+ id: string;
592
+ type: string;
593
+ name: string;
594
+ description: string;
595
+ }
596
+
597
+ interface IntentSchema {
598
+ intent: {
599
+ summary: string;
600
+ objective: string;
601
+ context: string[];
602
+ domain?: string;
603
+ };
604
+ ksas: {
605
+ priority: string[];
606
+ secondary: string[];
607
+ notNeeded: string[];
608
+ reasoning: string;
609
+ };
610
+ plan: {
611
+ goals: IntentGoal[];
612
+ deliverables: IntentDeliverable[];
613
+ steps: string[];
614
+ };
615
+ policy: {
616
+ enabledKSAs: string[];
617
+ disabledKSAs: string[];
618
+ allowExternalCalls: boolean;
619
+ requireApprovalFor?: string[];
620
+ };
621
+ meta: {
622
+ model: string;
623
+ generatedAt: number;
624
+ confidence: "high" | "medium" | "low";
625
+ latencyMs?: number;
626
+ };
627
+ }
628
+
629
+ /**
630
+ * Generate intent schema guidance section for the system prompt.
631
+ * This provides structured guidance based on pre-analysis of the user request.
632
+ */
633
+ function generateIntentSchemaGuidance(schema: IntentSchema): string {
634
+ const lines: string[] = [
635
+ "## 🎯 Pre-Analyzed Intent (Use This as Your Guide)",
636
+ "",
637
+ `**Objective:** ${schema.intent.objective}`,
638
+ "",
639
+ ];
640
+
641
+ // Context elements
642
+ if (schema.intent.context.length > 0) {
643
+ lines.push("**Key Context:**");
644
+ for (const ctx of schema.intent.context) {
645
+ lines.push(`- ${ctx}`);
646
+ }
647
+ lines.push("");
648
+ }
649
+
650
+ // Priority KSAs
651
+ if (schema.ksas.priority.length > 0) {
652
+ lines.push(
653
+ `**Priority KSAs (Import First):** ${schema.ksas.priority.join(", ")}`
654
+ );
655
+ lines.push(`> *${schema.ksas.reasoning}*`);
656
+ lines.push("");
657
+ }
658
+
659
+ // Goals
660
+ if (schema.plan.goals.length > 0) {
661
+ lines.push("**Goals to Accomplish:**");
662
+ for (const goal of schema.plan.goals) {
663
+ const importance =
664
+ goal.importance === "critical"
665
+ ? "🔴"
666
+ : goal.importance === "important"
667
+ ? "🟡"
668
+ : "🟢";
669
+ lines.push(`${importance} ${goal.text}`);
670
+ }
671
+ lines.push("");
672
+ }
673
+
674
+ // Deliverables
675
+ if (schema.plan.deliverables.length > 0) {
676
+ lines.push("**Expected Deliverables:**");
677
+ for (const d of schema.plan.deliverables) {
678
+ lines.push(`- **${d.name}** (${d.type}): ${d.description}`);
679
+ }
680
+ lines.push("");
681
+ }
682
+
683
+ // Suggested steps
684
+ if (schema.plan.steps.length > 0) {
685
+ lines.push("**Suggested Approach:**");
686
+ schema.plan.steps.forEach((step, i) => {
687
+ lines.push(`${i + 1}. ${step}`);
688
+ });
689
+ lines.push("");
690
+ }
691
+
692
+ // Policy notes
693
+ if (schema.policy.disabledKSAs.length > 0) {
694
+ lines.push(
695
+ `> ⚠️ **Blocked KSAs (do not use):** ${schema.policy.disabledKSAs.join(", ")}`
696
+ );
697
+ }
698
+ if (schema.policy.requireApprovalFor?.length) {
699
+ lines.push(
700
+ `> ℹ️ **Requires approval:** ${schema.policy.requireApprovalFor.join(", ")}`
701
+ );
702
+ }
703
+
704
+ return lines.join("\n");
705
+ }
706
+
707
+ /**
708
+ * Get the system prompt with dynamic KSA documentation
709
+ * @param options.allowedKSAs - If provided, only include documentation for these KSAs (core always included)
710
+ * @param options.additions - Additional context to append
711
+ * @param options.intentSchema - Pre-analyzed intent schema for structured guidance
712
+ */
713
+ export function getCodeExecSystemPrompt(options?: {
714
+ allowedKSAs?: string[];
715
+ additions?: string;
716
+ intentSchema?: IntentSchema;
717
+ }): string {
718
+ // Generate dynamic KSA documentation based on what's allowed
719
+ const ksaDocumentation = generateKSADocumentation(options?.allowedKSAs);
720
+
721
+ // Replace the placeholder with dynamic content
722
+ let prompt = CODE_EXEC_BASE_PROMPT.replace("{{KSA_DOCUMENTATION}}", ksaDocumentation);
723
+
724
+ // Add intent schema guidance FIRST (high priority)
725
+ if (options?.intentSchema) {
726
+ const intentGuidance = generateIntentSchemaGuidance(options.intentSchema);
727
+ prompt += `\n\n${intentGuidance}`;
728
+ }
729
+
730
+ // Add any additional context
731
+ if (options?.additions) {
732
+ prompt += `\n\n## Additional Context\n\n${options.additions}`;
733
+ }
734
+
735
+ return prompt;
736
+ }
737
+
738
+ // For backwards compatibility - the full prompt with all KSAs
739
+ export const CODE_EXEC_SYSTEM_PROMPT = getCodeExecSystemPrompt();
740
+
741
+ // ============================================================================
742
+ // KSA Instructions Generation
743
+ // ============================================================================
744
+
745
+ /** Display names for KSAs */
746
+ const KSA_DISPLAY_NAMES: Record<string, string> = {
747
+ web: "Web Research",
748
+ news: "News Monitoring",
749
+ social: "Social Media",
750
+ ads: "Ad Library Search",
751
+ companies: "Company Intelligence",
752
+ browser: "Browser Automation",
753
+ pdf: "PDF Generation",
754
+ email: "Email",
755
+ file: "File Operations",
756
+ artifacts: "Artifacts",
757
+ context: "Context",
758
+ beads: "Task Tracking",
759
+ };
760
+
761
+ /**
762
+ * Generate system prompt additions from skill configurations.
763
+ * This embeds user-specific instructions and config settings into the system prompt
764
+ * so the agent knows how to use each KSA according to user preferences.
765
+ *
766
+ * @param skillConfigs - Map of KSA name to user config (e.g., { web: { depth: 'thorough', instructions: '...' } })
767
+ * @returns String to add to system prompt, or empty string if no configs
768
+ */
769
+ export function generateKSAInstructions(
770
+ skillConfigs: Record<string, Record<string, unknown>>
771
+ ): string {
772
+ const sections: string[] = [];
773
+
774
+ for (const [ksaName, config] of Object.entries(skillConfigs)) {
775
+ // Skip if no meaningful config (only _isPreset/_baseKSA markers or empty)
776
+ const meaningfulKeys = Object.keys(config).filter(
777
+ (k) => !k.startsWith("_")
778
+ );
779
+ if (meaningfulKeys.length === 0) continue;
780
+
781
+ // Skip if only has instructions that's empty
782
+ if (
783
+ meaningfulKeys.length === 1 &&
784
+ meaningfulKeys[0] === "instructions" &&
785
+ !config.instructions
786
+ ) {
787
+ continue;
788
+ }
789
+
790
+ const displayName = KSA_DISPLAY_NAMES[ksaName] || ksaName;
791
+ const lines: string[] = [`### ${displayName} Configuration`];
792
+ lines.push(`When using the ${ksaName} KSA:`);
793
+
794
+ // Add user instructions first (most important)
795
+ if (config.instructions && typeof config.instructions === "string") {
796
+ lines.push(`- **User Instructions:** ${config.instructions}`);
797
+ }
798
+
799
+ // Add relevant config settings
800
+ if (config.depth) {
801
+ lines.push(`- Research depth: ${config.depth}`);
802
+ }
803
+ if (config.searchType) {
804
+ lines.push(`- Search type: ${config.searchType}`);
805
+ }
806
+ if (config.fastMode !== undefined) {
807
+ lines.push(`- Fast mode: ${config.fastMode ? "enabled" : "disabled"}`);
808
+ }
809
+ if (Array.isArray(config.platforms) && config.platforms.length > 0) {
810
+ lines.push(`- Platforms: ${config.platforms.join(", ")}`);
811
+ }
812
+ if (Array.isArray(config.contentTypes) && config.contentTypes.length > 0) {
813
+ lines.push(`- Content types: ${config.contentTypes.join(", ")}`);
814
+ }
815
+ if (config.postsLimit) {
816
+ lines.push(`- Posts limit: ${config.postsLimit}`);
817
+ }
818
+ if (config.enrichmentLevel) {
819
+ lines.push(`- Enrichment level: ${config.enrichmentLevel}`);
820
+ }
821
+ if (config.includeTechStack !== undefined) {
822
+ lines.push(
823
+ `- Include tech stack: ${config.includeTechStack ? "yes" : "no"}`
824
+ );
825
+ }
826
+ if (config.template) {
827
+ lines.push(`- Template: ${config.template}`);
828
+ }
829
+ if (config.pageSize) {
830
+ lines.push(`- Page size: ${config.pageSize}`);
831
+ }
832
+ if (config.sandboxMode !== undefined) {
833
+ lines.push(
834
+ `- Sandbox mode: ${config.sandboxMode ? "enabled (test only)" : "disabled (live)"}`
835
+ );
836
+ }
837
+ if (Array.isArray(config.includeSources) && config.includeSources.length > 0) {
838
+ lines.push(`- Include sources: ${config.includeSources.join(", ")}`);
839
+ }
840
+ if (Array.isArray(config.excludeSources) && config.excludeSources.length > 0) {
841
+ lines.push(`- Exclude sources: ${config.excludeSources.join(", ")}`);
842
+ }
843
+
844
+ sections.push(lines.join("\n"));
845
+ }
846
+
847
+ if (sections.length === 0) {
848
+ return "";
849
+ }
850
+
851
+ return `## Skill Configurations\n\nThe following KSAs have been configured with specific settings for this task:\n\n${sections.join("\n\n")}`;
852
+ }