lua-cli 3.1.0-alpha.3 → 3.1.0-alpha.5

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 (125) hide show
  1. package/README.md +0 -4
  2. package/dist/api/cdn.api.service.d.ts +18 -0
  3. package/dist/api/cdn.api.service.js +43 -0
  4. package/dist/api/custom.data.api.service.d.ts +4 -3
  5. package/dist/api/custom.data.api.service.js +4 -3
  6. package/dist/api/developer.api.service.d.ts +54 -1
  7. package/dist/api/developer.api.service.js +89 -0
  8. package/dist/api/job.api.service.d.ts +33 -100
  9. package/dist/api/job.api.service.js +27 -11
  10. package/dist/api/lazy-instances.d.ts +16 -0
  11. package/dist/api/lazy-instances.js +32 -0
  12. package/dist/api/postprocessor.api.service.d.ts +3 -13
  13. package/dist/api/postprocessor.api.service.js +2 -4
  14. package/dist/api/preprocessor.api.service.d.ts +1 -8
  15. package/dist/api/preprocessor.api.service.js +1 -2
  16. package/dist/api/webhook.api.service.d.ts +1 -3
  17. package/dist/api/webhook.api.service.js +1 -1
  18. package/dist/api/whatsapp-templates.api.service.d.ts +40 -0
  19. package/dist/api/whatsapp-templates.api.service.js +78 -0
  20. package/dist/api-exports.d.ts +153 -6
  21. package/dist/api-exports.js +177 -21
  22. package/dist/cli/command-definitions.js +34 -7
  23. package/dist/commands/admin.js +1 -1
  24. package/dist/commands/channels.js +1 -1
  25. package/dist/commands/chat.js +2 -4
  26. package/dist/commands/compile.js +23 -4
  27. package/dist/commands/evals.d.ts +8 -0
  28. package/dist/commands/evals.js +41 -0
  29. package/dist/commands/index.d.ts +2 -0
  30. package/dist/commands/index.js +2 -0
  31. package/dist/commands/init.d.ts +10 -1
  32. package/dist/commands/init.js +23 -46
  33. package/dist/commands/jobs.js +5 -5
  34. package/dist/commands/mcp.d.ts +18 -0
  35. package/dist/commands/mcp.js +393 -0
  36. package/dist/commands/push.js +174 -23
  37. package/dist/common/data.entry.instance.d.ts +1 -1
  38. package/dist/common/data.entry.instance.js +4 -4
  39. package/dist/common/job.instance.d.ts +59 -7
  40. package/dist/common/job.instance.js +84 -19
  41. package/dist/config/constants.d.ts +1 -0
  42. package/dist/config/constants.js +1 -0
  43. package/dist/index.js +1 -0
  44. package/dist/interfaces/agent.d.ts +0 -3
  45. package/dist/interfaces/cdn.d.ts +24 -0
  46. package/dist/interfaces/cdn.js +5 -0
  47. package/dist/interfaces/compile.d.ts +1 -0
  48. package/dist/interfaces/custom.data.d.ts +3 -3
  49. package/dist/interfaces/index.d.ts +2 -1
  50. package/dist/interfaces/init.d.ts +0 -1
  51. package/dist/interfaces/jobs.d.ts +88 -132
  52. package/dist/interfaces/jobs.js +1 -1
  53. package/dist/interfaces/mcp.d.ts +64 -0
  54. package/dist/interfaces/mcp.js +5 -0
  55. package/dist/interfaces/postprocessors.d.ts +0 -3
  56. package/dist/interfaces/preprocessors.d.ts +0 -3
  57. package/dist/interfaces/webhooks.d.ts +0 -5
  58. package/dist/interfaces/whatsapp-templates.d.ts +104 -0
  59. package/dist/interfaces/whatsapp-templates.js +5 -0
  60. package/dist/types/api-contracts.d.ts +68 -14
  61. package/dist/types/compile.types.d.ts +5 -6
  62. package/dist/types/index.d.ts +2 -2
  63. package/dist/types/index.js +3 -1
  64. package/dist/types/skill.d.ts +181 -103
  65. package/dist/types/skill.js +123 -91
  66. package/dist/utils/agent-management.d.ts +3 -5
  67. package/dist/utils/agent-management.js +6 -8
  68. package/dist/utils/bundling.d.ts +4 -11
  69. package/dist/utils/bundling.js +24 -33
  70. package/dist/utils/compile.d.ts +17 -9
  71. package/dist/utils/compile.js +72 -88
  72. package/dist/utils/deployment.js +13 -7
  73. package/dist/utils/dev-api.js +1 -4
  74. package/dist/utils/dev-server.js +1 -1
  75. package/dist/utils/files.d.ts +11 -4
  76. package/dist/utils/files.js +17 -14
  77. package/dist/utils/init-agent.d.ts +1 -2
  78. package/dist/utils/init-agent.js +4 -6
  79. package/dist/utils/init-helpers.d.ts +4 -4
  80. package/dist/utils/init-helpers.js +10 -11
  81. package/dist/utils/job-management.js +0 -2
  82. package/dist/utils/mcp-server-management.d.ts +23 -0
  83. package/dist/utils/mcp-server-management.js +212 -0
  84. package/dist/utils/postprocessor-management.js +2 -4
  85. package/dist/utils/preprocessor-management.js +2 -4
  86. package/dist/utils/sandbox.d.ts +4 -2
  87. package/dist/utils/sandbox.js +38 -9
  88. package/dist/utils/webhook-management.js +1 -3
  89. package/dist/web/app.css +1505 -14
  90. package/dist/web/app.js +79 -64
  91. package/package.json +2 -6
  92. package/template/QUICKSTART.md +57 -774
  93. package/template/README.md +80 -907
  94. package/template/examples/README.md +106 -0
  95. package/template/{src → examples}/jobs/AbandonedBasketProcessorJob.ts +67 -14
  96. package/template/{src → examples}/jobs/DailyCleanupJob.ts +0 -3
  97. package/template/{src → examples}/jobs/DataMigrationJob.ts +0 -3
  98. package/template/{src → examples}/jobs/HealthCheckJob.ts +0 -3
  99. package/template/{src → examples}/postprocessors/modifyResponse.ts +3 -4
  100. package/template/examples/preprocessors/messageMatching.ts +35 -0
  101. package/template/{src → examples}/skills/basket.skill.ts +0 -1
  102. package/template/{src → examples}/skills/product.skill.ts +0 -1
  103. package/template/{src → examples}/skills/tools/GameScoreTrackerTool.ts +11 -15
  104. package/template/{src → examples}/skills/tools/OrderTool.ts +25 -0
  105. package/template/examples/skills/tools/PremiumFeatureTool.ts +98 -0
  106. package/template/{src → examples}/skills/tools/UserDataTool.ts +34 -0
  107. package/template/{src → examples}/skills/user.skill.ts +0 -1
  108. package/template/examples/webhooks/FileUploadWebhook.ts +86 -0
  109. package/template/{src → examples}/webhooks/PaymentWebhook.ts +12 -9
  110. package/template/examples/webhooks/UserEventWebhook.ts +105 -0
  111. package/template/package-lock.json +7895 -0
  112. package/template/package.json +1 -1
  113. package/template/src/index.ts +40 -22
  114. package/template/src/preprocessors/messageMatching.ts +0 -22
  115. package/template/src/webhooks/UserEventWebhook.ts +0 -77
  116. /package/template/{src → examples}/services/ApiService.ts +0 -0
  117. /package/template/{src → examples}/services/GetWeather.ts +0 -0
  118. /package/template/{src → examples}/skills/tools/BasketTool.ts +0 -0
  119. /package/template/{src → examples}/skills/tools/CreateInlineJob.ts +0 -0
  120. /package/template/{src → examples}/skills/tools/CreatePostTool.ts +0 -0
  121. /package/template/{src → examples}/skills/tools/CustomDataTool.ts +0 -0
  122. /package/template/{src → examples}/skills/tools/GetWeatherTool.ts +0 -0
  123. /package/template/{src → examples}/skills/tools/PaymentTool.ts +0 -0
  124. /package/template/{src → examples}/skills/tools/ProductsTool.ts +0 -0
  125. /package/template/{src → examples}/skills/tools/SmartBasketTool.ts +0 -0
@@ -0,0 +1,106 @@
1
+ # 📚 Example Code
2
+
3
+ This folder contains example implementations of all Lua AI Agent features. Use these as reference when building your own agent.
4
+
5
+ ## 📁 What's Inside
6
+
7
+ ### skills/
8
+ Example skills and tools demonstrating platform APIs:
9
+
10
+ | File | What it shows |
11
+ |------|--------------|
12
+ | `tools/GetWeatherTool.ts` | External API integration |
13
+ | `tools/UserDataTool.ts` | User API - get/update user data, chat history, AI generation |
14
+ | `tools/ProductsTool.ts` | Products API - CRUD operations |
15
+ | `tools/BasketTool.ts` | Baskets API - shopping cart |
16
+ | `tools/OrderTool.ts` | Orders API - order management, status & data updates |
17
+ | `tools/CustomDataTool.ts` | Data API - custom collections with semantic search |
18
+ | `tools/PaymentTool.ts` | Stripe integration, environment variables |
19
+ | `tools/SmartBasketTool.ts` | Dynamic job creation from tools |
20
+ | `tools/GameScoreTrackerTool.ts` | Jobs API - interval jobs, deactivation, getAll |
21
+
22
+ ### webhooks/
23
+ HTTP endpoints for external integrations:
24
+
25
+ | File | What it shows |
26
+ |------|--------------|
27
+ | `PaymentWebhook.ts` | Stripe payment notifications, Orders API |
28
+ | `UserEventWebhook.ts` | External events, **Templates API** (WhatsApp) |
29
+ | `FileUploadWebhook.ts` | **CDN API** - file uploads, Data API |
30
+
31
+ ### jobs/
32
+ Scheduled background tasks:
33
+
34
+ | File | What it shows |
35
+ |------|--------------|
36
+ | `HealthCheckJob.ts` | Interval schedule, health monitoring |
37
+ | `DailyCleanupJob.ts` | Cron schedule, data cleanup |
38
+ | `DataMigrationJob.ts` | One-time schedule, batch processing |
39
+ | `AbandonedBasketProcessorJob.ts` | **Templates API** (WhatsApp), batch reminders |
40
+
41
+ ### preprocessors/
42
+ Message filtering before the agent:
43
+
44
+ | File | What it shows |
45
+ |------|--------------|
46
+ | `messageMatching.ts` | Modify/block messages based on content |
47
+
48
+ ### postprocessors/
49
+ Response transformation after the agent:
50
+
51
+ | File | What it shows |
52
+ |------|--------------|
53
+ | `modifyResponse.ts` | Transform agent responses |
54
+
55
+ ### services/
56
+ Helper utilities:
57
+
58
+ | File | What it shows |
59
+ |------|--------------|
60
+ | `ApiService.ts` | HTTP client wrapper |
61
+ | `GetWeather.ts` | Mock service for testing |
62
+
63
+ ## 🎯 API Coverage
64
+
65
+ | API | Where it's demonstrated |
66
+ |-----|------------------------|
67
+ | **User** | `UserDataTool.ts` |
68
+ | **Products** | `ProductsTool.ts` |
69
+ | **Baskets** | `BasketTool.ts`, `SmartBasketTool.ts` |
70
+ | **Orders** | `OrderTool.ts`, `PaymentWebhook.ts` |
71
+ | **Data** | `CustomDataTool.ts`, `FileUploadWebhook.ts` |
72
+ | **Jobs** | `SmartBasketTool.ts`, `GameScoreTrackerTool.ts` |
73
+ | **AI** | `UserDataTool.ts` |
74
+ | **Templates** | `UserEventWebhook.ts`, `AbandonedBasketProcessorJob.ts` |
75
+ | **CDN** | `FileUploadWebhook.ts` |
76
+
77
+ ## 🚀 How to Use
78
+
79
+ ### Option 1: Copy what you need
80
+
81
+ ```bash
82
+ # Copy a tool
83
+ cp examples/skills/tools/GetWeatherTool.ts src/skills/tools/
84
+
85
+ # Copy a webhook
86
+ cp examples/webhooks/PaymentWebhook.ts src/webhooks/
87
+
88
+ # Copy a job
89
+ cp examples/jobs/HealthCheckJob.ts src/jobs/
90
+ ```
91
+
92
+ ### Option 2: Move entire directories
93
+
94
+ ```bash
95
+ mv examples/skills src/
96
+ mv examples/webhooks src/
97
+ mv examples/jobs src/
98
+ ```
99
+
100
+ Then import them in your `src/index.ts`.
101
+
102
+ ## 📖 Documentation
103
+
104
+ - **API Reference:** https://docs.heylua.ai/api
105
+ - **Examples Guide:** https://docs.heylua.ai/examples
106
+ - **Best Practices:** https://docs.heylua.ai/template/best-practices
@@ -1,25 +1,27 @@
1
1
  /**
2
2
  * Abandoned Basket Processor Job
3
3
  *
4
- * This job runs every 15 minutes to process basket checkout reminders.
5
- * Works together with CreateSmartBasketTool to send abandoned cart notifications.
4
+ * Processes abandoned cart reminders and sends WhatsApp notifications.
6
5
  *
7
6
  * Flow:
8
7
  * 1. User creates basket → Tool schedules reminder in custom data
9
8
  * 2. This job runs every 15 minutes
10
9
  * 3. Checks which reminders are due
11
10
  * 4. Checks if baskets were checked out
12
- * 5. Sends reminders for abandoned baskets
11
+ * 5. Sends WhatsApp template message for abandoned baskets
12
+ *
13
+ * Demonstrates:
14
+ * - Interval job scheduling
15
+ * - Batch processing pattern
16
+ * - Templates API integration (WhatsApp)
17
+ * - Data API for tracking state
13
18
  */
14
19
 
15
- import { LuaJob, Data, Baskets } from "lua-cli";
20
+ import { LuaJob, Data, Baskets, Templates, env } from "lua-cli";
16
21
 
17
22
  const abandonedBasketProcessorJob = new LuaJob({
18
23
  name: "process-basket-reminders",
19
- version: "1.0.0",
20
- description: "Processes abandoned basket reminders",
21
- context: "Runs every 15 minutes to check for abandoned baskets and send recovery emails. " +
22
- "Works with basket creation tools that schedule reminders.",
24
+ description: "Processes abandoned basket reminders and sends WhatsApp notifications",
23
25
 
24
26
  // Run every 15 minutes
25
27
  schedule: {
@@ -30,6 +32,10 @@ const abandonedBasketProcessorJob = new LuaJob({
30
32
  timeout: 120, // 2 minutes
31
33
 
32
34
  execute: async () => {
35
+ // Configuration - set these in your environment variables
36
+ const WHATSAPP_CHANNEL_ID = env('WHATSAPP_CHANNEL_ID') || '';
37
+ const ABANDONED_CART_TEMPLATE_NAME = 'abandoned_cart_reminder';
38
+
33
39
  console.log('🔍 Processing basket reminders...');
34
40
 
35
41
  // Get all pending reminders
@@ -40,6 +46,7 @@ const abandonedBasketProcessorJob = new LuaJob({
40
46
  let processedCount = 0;
41
47
  let abandonedCount = 0;
42
48
  let checkedOutCount = 0;
49
+ let messagesSent = 0;
43
50
 
44
51
  for (const reminder of reminders) {
45
52
  // Skip if not yet time
@@ -75,9 +82,58 @@ const abandonedBasketProcessorJob = new LuaJob({
75
82
  totalValue,
76
83
  items: basket.items,
77
84
  abandonedAt: new Date().toISOString(),
78
- reminderSent: true
85
+ reminderSent: false
79
86
  });
80
87
 
88
+ // Send WhatsApp reminder if we have a phone number
89
+ if (reminder.data.phoneNumber && WHATSAPP_CHANNEL_ID) {
90
+ try {
91
+ // Find the abandoned cart template
92
+ const templatesResponse = await Templates.whatsapp.list(WHATSAPP_CHANNEL_ID, {
93
+ search: ABANDONED_CART_TEMPLATE_NAME
94
+ });
95
+
96
+ const template = templatesResponse.templates.find(
97
+ t => t.status === 'APPROVED'
98
+ );
99
+
100
+ if (template) {
101
+ // Send the template message
102
+ const result = await Templates.whatsapp.send(WHATSAPP_CHANNEL_ID, template.id, {
103
+ phoneNumbers: [reminder.data.phoneNumber],
104
+ values: {
105
+ body: {
106
+ item_count: String(basket.items?.length || 0),
107
+ total_value: `${basket.currency} ${totalValue.toFixed(2)}`
108
+ }
109
+ }
110
+ });
111
+
112
+ if (result.totalErrors === 0) {
113
+ console.log(`📱 WhatsApp reminder sent to ${reminder.data.phoneNumber}`);
114
+ messagesSent++;
115
+
116
+ // Update the abandoned basket record
117
+ const abandonedRecords = await Data.get('abandoned-baskets', {}, 1, 100);
118
+ const record = abandonedRecords.data?.find((r: any) => r.data.basketId === basket.id);
119
+ if (record) {
120
+ await Data.update('abandoned-baskets', record.id, {
121
+ ...record.data,
122
+ reminderSent: true,
123
+ reminderSentAt: new Date().toISOString()
124
+ });
125
+ }
126
+ } else {
127
+ console.warn(`⚠️ Failed to send WhatsApp: ${result.results[0]?.error}`);
128
+ }
129
+ } else {
130
+ console.warn(`⚠️ No approved template found: ${ABANDONED_CART_TEMPLATE_NAME}`);
131
+ }
132
+ } catch (templateError) {
133
+ console.error('Failed to send WhatsApp template:', templateError);
134
+ }
135
+ }
136
+
81
137
  // Update reminder status
82
138
  await Data.update('basket-reminders', reminder.id, {
83
139
  ...reminder.data,
@@ -86,9 +142,6 @@ const abandonedBasketProcessorJob = new LuaJob({
86
142
  processedAt: new Date().toISOString()
87
143
  });
88
144
 
89
- // TODO: Send actual reminder email/SMS
90
- console.log(`📧 Would send reminder: "You left ${basket.items?.length || 0} items in your cart!"`);
91
-
92
145
  } else {
93
146
  // Basket was checked out
94
147
  console.log(`✅ Basket ${basket.id} was checked out (${basket.status})`);
@@ -123,17 +176,17 @@ const abandonedBasketProcessorJob = new LuaJob({
123
176
  }
124
177
 
125
178
  console.log('✅ Reminder processing complete');
126
- console.log(`📊 Processed: ${processedCount}, Abandoned: ${abandonedCount}, Checked out: ${checkedOutCount}`);
179
+ console.log(`📊 Processed: ${processedCount}, Abandoned: ${abandonedCount}, Checked out: ${checkedOutCount}, Messages sent: ${messagesSent}`);
127
180
 
128
181
  return {
129
182
  success: true,
130
183
  processedCount,
131
184
  abandonedCount,
132
185
  checkedOutCount,
186
+ messagesSent,
133
187
  timestamp: new Date().toISOString()
134
188
  };
135
189
  }
136
190
  });
137
191
 
138
192
  export default abandonedBasketProcessorJob;
139
-
@@ -9,10 +9,7 @@ import { LuaJob, Data } from "lua-cli";
9
9
 
10
10
  const dailyCleanupJob = new LuaJob({
11
11
  name: "daily-cleanup",
12
- version: "1.0.0",
13
12
  description: "Daily database cleanup and maintenance",
14
- context: "Runs at 2 AM EST daily to remove old logs, expired sessions, and optimize data collections. " +
15
- "This job helps maintain database performance and storage costs.",
16
13
 
17
14
  // Cron schedule: Every day at 2 AM EST
18
15
  schedule: {
@@ -9,10 +9,7 @@ import { LuaJob, Data } from "lua-cli";
9
9
 
10
10
  const dataMigrationJob = new LuaJob({
11
11
  name: "migrate-user-schema",
12
- version: "1.0.0",
13
12
  description: "One-time migration to new user event schema",
14
- context: "Scheduled one-time task to migrate user events from old schema to new schema. " +
15
- "Runs once at the specified time and then automatically deactivates.",
16
13
 
17
14
  // One-time schedule: Run at specific date/time
18
15
  schedule: {
@@ -9,10 +9,7 @@ import { LuaJob, Data, Products } from "lua-cli";
9
9
 
10
10
  const healthCheckJob = new LuaJob({
11
11
  name: "health-check",
12
- version: "1.0.0",
13
12
  description: "System health monitoring",
14
- context: "Runs every 5 minutes to check API health, database connectivity, and service availability. " +
15
- "Logs health metrics and can trigger alerts if issues are detected.",
16
13
 
17
14
  // Interval schedule: Every 5 minutes
18
15
  schedule: {
@@ -1,10 +1,9 @@
1
- import { ChatMessage, PostProcessor, UserDataInstance } from "lua-cli";
1
+ import { PostProcessor, UserDataInstance } from "lua-cli";
2
2
 
3
3
 
4
4
  const modifyResponsePostProcessor = new PostProcessor({
5
5
  name: "modify-response",
6
6
  description: "Modifies the response to the user",
7
- context: "Modifies the response to the user",
8
7
  execute: async (user: UserDataInstance, message: string, response: string, channel: string) => {
9
8
  console.log("Modify response post processor", user, message, response, channel);
10
9
  console.log("User data", await user.data);
@@ -12,9 +11,9 @@ const modifyResponsePostProcessor = new PostProcessor({
12
11
  console.log("Response", response);
13
12
  console.log("Channel", channel);
14
13
  if (response.includes("test")) {
15
- return message.toUpperCase();
14
+ return { modifiedResponse: message.toUpperCase() };
16
15
  }
17
- return response;
16
+ return { modifiedResponse: response };
18
17
  }
19
18
  });
20
19
 
@@ -0,0 +1,35 @@
1
+ import { ChatMessage, PreProcessor, PreProcessorResult, UserDataInstance } from "lua-cli";
2
+
3
+
4
+ const messageMatchingPreProcessor = new PreProcessor({
5
+ name: "message-matching",
6
+ description: "Matches the message to the appropriate skill",
7
+ execute: async (user: UserDataInstance, messages: ChatMessage[], channel: string): Promise<PreProcessorResult> => {
8
+ console.log("Message matching pre processor", user, messages, channel);
9
+ console.log("User data", await user.data);
10
+ console.log("Messages", messages);
11
+ console.log("Channel", channel);
12
+
13
+ //check if message type text contains test and return the message
14
+ const testMessage = messages.find((message) => message.type === "text" && message.text.includes("test"));
15
+ if (testMessage) {
16
+ return {
17
+ action: 'proceed',
18
+ modifiedMessage: [{ type: "text", text: "Tell the user that you got their test message and nothing else" }]
19
+ };
20
+ }
21
+
22
+ // Example blocking logic
23
+ const shouldBlock = messages.some((message) => message.type === "text" && message.text.includes("block"));
24
+ if (shouldBlock) {
25
+ return {
26
+ action: 'block',
27
+ response: "Message blocked by preprocessor"
28
+ };
29
+ }
30
+
31
+ return { action: 'proceed' };
32
+ }
33
+ });
34
+
35
+ export default messageMatchingPreProcessor;
@@ -3,7 +3,6 @@ import { CreateBasketTool, GetBasketByIdTool, UpdateBasketStatusTool, UpdateBask
3
3
 
4
4
  const basketSkill = new LuaSkill({
5
5
  name: "basket-skill",
6
- version: "1.0.0",
7
6
  description: "Basket management skill",
8
7
  context: "Basket management skill",
9
8
  tools: [new CreateBasketTool(), new GetBasketByIdTool(), new UpdateBasketStatusTool(), new UpdateBasketMetadataTool(), new CheckoutBasketTool(), new GetBasketsTool(), new AddItemToBasketTool(), new RemoveItemFromBasketTool(), new ClearBasketTool()],
@@ -4,7 +4,6 @@ import { SearchProductsTool, CreateProductTool, UpdateProductTool, GetAllProduct
4
4
 
5
5
  const productSkill = new LuaSkill({
6
6
  name: "product-skill",
7
- version: "1.0.0",
8
7
  description: "Product management skill",
9
8
  context: "Product management skill",
10
9
  tools: [new SearchProductsTool(), new CreateProductTool(), new UpdateProductTool(), new GetAllProductsTool(), new GetProductByIdTool(), new DeleteProductTool()],
@@ -101,8 +101,8 @@ export class TrackGameScoresTool implements LuaTool {
101
101
  if (gameData.data.status === 'finished') {
102
102
  console.log('🏁 Game ' + gameId + ' is already finished. Stopping job.');
103
103
 
104
- // IMPORTANT: Job stops itself!
105
- await Jobs.deactivate(job.jobId);
104
+ // IMPORTANT: Job stops itself using the instance method!
105
+ await job.deactivate();
106
106
 
107
107
  return {
108
108
  action: 'job-stopped',
@@ -168,8 +168,8 @@ export class TrackGameScoresTool implements LuaTool {
168
168
  finishedAt: new Date().toISOString()
169
169
  });
170
170
 
171
- // IMPORTANT: Job stops itself when game ends!
172
- await Jobs.deactivate(job.jobId);
171
+ // IMPORTANT: Job stops itself when game ends using instance method!
172
+ await job.deactivate();
173
173
 
174
174
  console.log('⏹️ Score tracking job stopped for game ' + gameId);
175
175
 
@@ -208,7 +208,7 @@ export class TrackGameScoresTool implements LuaTool {
208
208
  }
209
209
  });
210
210
 
211
- console.log(`✅ Score tracking job created: ${job.jobId}`);
211
+ console.log(`✅ Score tracking job created: ${job.id}`);
212
212
  console.log(`📊 Job will update scores every ${input.updateIntervalSeconds} seconds`);
213
213
  console.log(`⏹️ Job will automatically stop when game ends`);
214
214
 
@@ -222,7 +222,7 @@ export class TrackGameScoresTool implements LuaTool {
222
222
  status: 'scheduled'
223
223
  },
224
224
  scoreTracker: {
225
- jobId: job.jobId,
225
+ jobId: job.id,
226
226
  jobName: job.name,
227
227
  updateInterval: input.updateIntervalSeconds,
228
228
  startsAt: jobStartTime.toISOString(),
@@ -309,14 +309,10 @@ export class StopGameTrackingTool implements LuaTool {
309
309
  async execute(input: { gameId: string }) {
310
310
  console.log(`⏹️ Stopping score tracking for game ${input.gameId}...`);
311
311
 
312
- // Find the tracking job
313
- const allJobs = await Jobs.getAll();
314
-
315
- if (!allJobs.success || !allJobs.data) {
316
- throw new Error('Failed to fetch jobs');
317
- }
312
+ // Find the tracking job - Jobs.getAll() returns array of JobInstance
313
+ const allJobs = await Jobs.getAll({ includeDynamic: true });
318
314
 
319
- const trackingJob = allJobs.data.jobs.find((j: any) =>
315
+ const trackingJob = allJobs.find((j: any) =>
320
316
  j.name === `track-game-${input.gameId}`
321
317
  );
322
318
 
@@ -328,8 +324,8 @@ export class StopGameTrackingTool implements LuaTool {
328
324
  };
329
325
  }
330
326
 
331
- // Deactivate the job
332
- const result = await Jobs.deactivate(trackingJob.id);
327
+ // Deactivate the job using instance method
328
+ await trackingJob.deactivate();
333
329
 
334
330
  // Update game status
335
331
  const games = await Data.get('games', {}, 1, 100);
@@ -52,3 +52,28 @@ export class GetOrderByIdTool implements LuaTool {
52
52
  return Orders.getById(input.orderId);
53
53
  }
54
54
  }
55
+
56
+ /**
57
+ * Update custom data on an order.
58
+ * Useful for adding tracking info, notes, or custom metadata.
59
+ */
60
+ export class UpdateOrderDataTool implements LuaTool {
61
+ name = "update_order_data";
62
+ description = "Update custom data on an order (tracking info, notes, etc.)";
63
+ inputSchema = z.object({
64
+ orderId: z.string().describe("The order ID to update"),
65
+ data: z.record(z.any()).describe("Custom data to add/update on the order")
66
+ });
67
+ async execute(input: z.infer<typeof this.inputSchema>) {
68
+ console.log(`📝 Updating order ${input.orderId} with data:`, input.data);
69
+
70
+ const result = await Orders.updateData(input.data, input.orderId);
71
+
72
+ return {
73
+ success: true,
74
+ orderId: input.orderId,
75
+ message: "Order data updated successfully",
76
+ updatedData: input.data
77
+ };
78
+ }
79
+ }
@@ -0,0 +1,98 @@
1
+ import { LuaTool, Products, User } from "lua-cli";
2
+ import { z } from "zod";
3
+
4
+ /**
5
+ * Example tool with a condition.
6
+ *
7
+ * The `condition` function determines if this tool should be available to the LLM.
8
+ * It runs BEFORE the tool is offered, so the LLM won't see this tool if condition returns false.
9
+ *
10
+ * Use cases:
11
+ * - Premium/paid features only for subscribed users
12
+ * - Features based on user verification status
13
+ * - Region-specific tools
14
+ * - A/B testing tool availability
15
+ * - Time-based feature access
16
+ *
17
+ * The condition has access to all Platform APIs: User, Data, Products, Baskets, Orders, etc.
18
+ */
19
+ export default class PremiumFeatureTool implements LuaTool {
20
+ name = "premium_advanced_search";
21
+ description = "Advanced search with filters and sorting - available to premium users only";
22
+
23
+ inputSchema = z.object({
24
+ query: z.string().describe("Search query"),
25
+ filters: z.object({
26
+ category: z.string().optional().describe("Filter by category"),
27
+ minPrice: z.number().optional().describe("Minimum price filter"),
28
+ maxPrice: z.number().optional().describe("Maximum price filter"),
29
+ }).optional().describe("Optional filters"),
30
+ sortBy: z.enum(["relevance", "price_asc", "price_desc", "newest"]).optional().describe("Sort order")
31
+ });
32
+
33
+ /**
34
+ * Condition function - determines if this tool should be available.
35
+ *
36
+ * This runs once per request when tools are being built.
37
+ * Return true to enable the tool, false to hide it from the LLM.
38
+ *
39
+ * If condition throws an error, the tool is disabled (fail-closed behavior).
40
+ */
41
+ condition = async () => {
42
+ // Get the current user's data
43
+ const user = await User.get();
44
+
45
+ // Check if user has premium subscription
46
+ // The 'data' field contains custom user data stored via User.update()
47
+ const isPremium = user.data?.subscription === "premium" || user.data?.isPremium === true;
48
+
49
+ // You can also check other conditions:
50
+ // - user.data?.trialEndsAt > Date.now() (trial period)
51
+ // - user.country?.code === "US" (region-based)
52
+ // - user.data?.featureFlags?.advancedSearch (feature flags)
53
+
54
+ return isPremium;
55
+ };
56
+
57
+ async execute(input: z.infer<typeof this.inputSchema>) {
58
+ // This only runs if condition returned true
59
+ const { query, filters, sortBy } = input;
60
+
61
+ // Example: Search products with advanced filters
62
+ const searchOptions: any = {
63
+ query,
64
+ limit: 20,
65
+ };
66
+
67
+ if (filters?.category) {
68
+ searchOptions.category = filters.category;
69
+ }
70
+
71
+ if (sortBy) {
72
+ searchOptions.sortBy = sortBy;
73
+ }
74
+
75
+ const searchResult = await Products.search(searchOptions);
76
+ const products = searchResult.products;
77
+
78
+ // Apply price filters if provided
79
+ let filteredResults = products;
80
+ if (filters?.minPrice !== undefined || filters?.maxPrice !== undefined) {
81
+ filteredResults = products.filter((product) => {
82
+ const price = product.price || 0;
83
+ if (filters.minPrice !== undefined && price < filters.minPrice) return false;
84
+ if (filters.maxPrice !== undefined && price > filters.maxPrice) return false;
85
+ return true;
86
+ });
87
+ }
88
+
89
+ return {
90
+ query,
91
+ totalResults: filteredResults.length,
92
+ results: filteredResults.slice(0, 10), // Return top 10
93
+ appliedFilters: filters,
94
+ sortedBy: sortBy || "relevance"
95
+ };
96
+ }
97
+ }
98
+
@@ -46,6 +46,40 @@ export class UpdateUserDataTool implements LuaTool {
46
46
  }
47
47
  }
48
48
 
49
+ /**
50
+ * Get the conversation history for the current user.
51
+ * Useful for analyzing past interactions or providing context.
52
+ */
53
+ export class GetChatHistoryTool implements LuaTool {
54
+ name = "get_chat_history";
55
+ description = "Get the conversation history for the current user";
56
+ inputSchema = z.object({
57
+ limit: z.number().optional().describe("Maximum number of messages to retrieve")
58
+ });
59
+
60
+ async execute(input: z.infer<typeof this.inputSchema>) {
61
+ const history = await User.getChatHistory();
62
+
63
+ // Optionally limit the results
64
+ const messages = input.limit ? history.slice(-input.limit) : history;
65
+
66
+ // Format the history for easier reading
67
+ const formattedHistory = messages.map(msg => ({
68
+ role: msg.role,
69
+ content: msg.content,
70
+ createdAt: msg.createdAt,
71
+ id: msg.id
72
+ }));
73
+
74
+ return {
75
+ success: true,
76
+ totalMessages: history.length,
77
+ returnedMessages: messages.length,
78
+ history: formattedHistory
79
+ };
80
+ }
81
+ }
82
+
49
83
  export class WritePoemTool implements LuaTool {
50
84
  name = "write_poem";
51
85
  description = "Write a poem about a given topic";
@@ -4,7 +4,6 @@ import { CreateInlineJobTool, GetUserDataTool, UpdateUserDataTool, WritePoemTool
4
4
 
5
5
  const userSkill = new LuaSkill({
6
6
  name: "user-skill",
7
- version: "1.0.0",
8
7
  description: "User management skill",
9
8
  context: "User management skill",
10
9
  tools: [new GetUserDataTool(), new UpdateUserDataTool(), new WritePoemTool(), new CreateInlineJobTool()],