lua-cli 3.17.6 → 3.20.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.
@@ -9,537 +9,318 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
- // src/interfaces/baskets.ts
13
- var BasketStatus;
14
- var init_baskets = __esm({
15
- "src/interfaces/baskets.ts"() {
16
- "use strict";
17
- BasketStatus = /* @__PURE__ */ (function(BasketStatus2) {
18
- BasketStatus2["ACTIVE"] = "active";
19
- BasketStatus2["CHECKED_OUT"] = "checked_out";
20
- BasketStatus2["ABANDONED"] = "abandoned";
21
- BasketStatus2["EXPIRED"] = "expired";
22
- return BasketStatus2;
23
- })({});
12
+ // ../shared-types/dist/index.mjs
13
+ import { z } from "zod";
14
+ function isPersonaTextObject(value) {
15
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
16
+ const obj = value;
17
+ for (const k of Object.keys(obj)) {
18
+ if (k !== "base" && k !== "voice" && k !== "text") return false;
19
+ if (obj[k] !== void 0 && typeof obj[k] !== "string") return false;
24
20
  }
25
- });
26
-
27
- // src/config/constants.ts
28
- import { join } from "path";
29
- import { homedir } from "os";
30
- var CLI_CONFIG_DIR, VERSION_CHECK_FILE, TELEMETRY_FILE, CLI_CACHE_FILE, BASE_URLS, CREDENTIALS_FILE, SANDBOX_STORAGE_FILE, AUTH_STORAGE_FILE;
31
- var init_constants = __esm({
32
- "src/config/constants.ts"() {
33
- "use strict";
34
- CLI_CONFIG_DIR = join(homedir(), ".lua-cli");
35
- VERSION_CHECK_FILE = join(CLI_CONFIG_DIR, "version-check.json");
36
- TELEMETRY_FILE = join(CLI_CONFIG_DIR, "telemetry.json");
37
- CLI_CACHE_FILE = join(CLI_CONFIG_DIR, "cache.json");
38
- BASE_URLS = {
39
- API: process.env.LUA_API_URL || "https://api.heylua.ai",
40
- AUTH: process.env.LUA_AUTH_URL || "https://auth.heylua.ai",
41
- CHAT: process.env.LUA_API_URL || "https://api.heylua.ai",
42
- WEBHOOK: "https://webhook.heylua.ai",
43
- CDN: "https://cdn.heylua.ai"
21
+ return true;
22
+ }
23
+ function flattenPersonaText(input, isVoice = false) {
24
+ if (input == null) return "";
25
+ if (typeof input === "string") return input;
26
+ const parts = [];
27
+ if (input.base) parts.push(input.base);
28
+ const channelText = isVoice ? input.voice : input.text;
29
+ if (channelText) parts.push(channelText);
30
+ return parts.join("\n\n");
31
+ }
32
+ function flattenPersonaTextAll(input) {
33
+ if (input == null) return "";
34
+ if (typeof input === "string") return input;
35
+ const parts = [];
36
+ if (input.base) parts.push(input.base);
37
+ if (input.voice) parts.push(input.voice);
38
+ if (input.text) parts.push(input.text);
39
+ return parts.join("\n\n");
40
+ }
41
+ function hasPersonaTextContent(input) {
42
+ if (input == null) return false;
43
+ if (typeof input === "string") return input.trim().length > 0;
44
+ return Boolean(input.base?.trim() || input.voice?.trim() || input.text?.trim());
45
+ }
46
+ function personaToLiteral(persona) {
47
+ const formatValue = /* @__PURE__ */ __name2((v) => {
48
+ if (v.includes("\n")) {
49
+ return "`" + v.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$") + "`";
50
+ }
51
+ return '"' + v.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
52
+ }, "formatValue");
53
+ if (typeof persona === "string") return formatValue(persona);
54
+ const parts = [];
55
+ if (persona.base !== void 0) parts.push(`base: ${formatValue(persona.base)}`);
56
+ if (persona.voice !== void 0) parts.push(`voice: ${formatValue(persona.voice)}`);
57
+ if (persona.text !== void 0) parts.push(`text: ${formatValue(persona.text)}`);
58
+ return `{ ${parts.join(", ")} }`;
59
+ }
60
+ function aiGenerateInputFromSimplified(prompt, content) {
61
+ if (content === void 0) {
62
+ return {
63
+ prompt
44
64
  };
45
- CREDENTIALS_FILE = join(CLI_CONFIG_DIR, "credentials");
46
- SANDBOX_STORAGE_FILE = join(CLI_CONFIG_DIR, "sandbox.json");
47
- AUTH_STORAGE_FILE = join(CLI_CONFIG_DIR, "auth.json");
48
65
  }
49
- });
50
-
51
- // src/errors/auth.error.ts
52
- var AuthenticationError;
53
- var init_auth_error = __esm({
54
- "src/errors/auth.error.ts"() {
55
- "use strict";
56
- AuthenticationError = class _AuthenticationError extends Error {
57
- static {
58
- __name(this, "AuthenticationError");
66
+ return {
67
+ system: prompt,
68
+ messages: [
69
+ {
70
+ role: "user",
71
+ content
59
72
  }
60
- statusCode = 401;
61
- isAuthenticationError = true;
62
- reason;
63
- serverMessage;
64
- /**
65
- * If true, the error message already contains complete remediation steps
66
- * and `withErrorHandling` should not append its own hint block. Use this
67
- * when the throw site has more context about the right fix than the
68
- * generic per-reason hints (e.g. `getToken()` listing all three ways to
69
- * configure a key — keychain, env var, .env file).
70
- */
71
- suppressDefaultRemediation;
72
- constructor(message = "Invalid API key", reason = "unknown", serverMessage, suppressDefaultRemediation = false) {
73
- super(message);
74
- this.name = "AuthenticationError";
75
- this.reason = reason;
76
- this.serverMessage = serverMessage;
77
- this.suppressDefaultRemediation = suppressDefaultRemediation;
78
- if (Error.captureStackTrace) {
79
- Error.captureStackTrace(this, _AuthenticationError);
80
- }
73
+ ]
74
+ };
75
+ }
76
+ function isAllowedReviewableExecuteTool(tool) {
77
+ return REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST.includes(tool);
78
+ }
79
+ function isReviewableMcpSendTool(tool) {
80
+ return tool.length > REVIEWABLE_MCP_SEND_TOOL_SUFFIX.length && tool.endsWith(REVIEWABLE_MCP_SEND_TOOL_SUFFIX);
81
+ }
82
+ function isReviewableExecuteTool(tool) {
83
+ return isAllowedReviewableExecuteTool(tool) || isReviewableMcpSendTool(tool);
84
+ }
85
+ function isInteractiveChannel(channel) {
86
+ if (!channel) return true;
87
+ return !NON_INTERACTIVE_CHANNELS.includes(channel);
88
+ }
89
+ function isInteractiveTurn(turn) {
90
+ if (typeof turn.interactive === "boolean") return turn.interactive;
91
+ return isInteractiveChannel(turn.channel);
92
+ }
93
+ function removeNavigateBlock(input) {
94
+ return input.replace(/::: navigate[\s\S]*?:::/g, "").trim();
95
+ }
96
+ function transformChatHistoryContentParts(parts) {
97
+ const content = [];
98
+ for (const rawPart of parts ?? []) {
99
+ const part = rawPart;
100
+ if (part?.type === "reasoning") {
101
+ const detailsText = Array.isArray(part.details) ? part.details.filter((d) => d?.type === "text" && typeof d.text === "string").map((d) => d.text).join("") : "";
102
+ const reasoningText = [
103
+ part.reasoning,
104
+ detailsText,
105
+ part.text
106
+ ].find((v) => typeof v === "string" && v.trim().length > 0) ?? "";
107
+ if (reasoningText) content.push({
108
+ type: "reasoning",
109
+ text: reasoningText
110
+ });
111
+ continue;
112
+ }
113
+ if (part?.type === "tool-invocation") {
114
+ const inv = part.toolInvocation;
115
+ if (typeof inv?.toolName === "string" && inv.toolName.length > 0) {
116
+ content.push({
117
+ type: "tool",
118
+ toolName: inv.toolName,
119
+ toolCallId: inv.toolCallId,
120
+ input: inv.args,
121
+ output: inv.result,
122
+ toolState: inv.state
123
+ });
81
124
  }
82
- /**
83
- * Checks if an error is an AuthenticationError
84
- * @param error - The error to check
85
- * @returns True if the error is an AuthenticationError
86
- */
87
- static isAuthenticationError(error) {
88
- return error instanceof _AuthenticationError || error instanceof Error && "isAuthenticationError" in error && error.isAuthenticationError === true;
125
+ continue;
126
+ }
127
+ if (part?.type === "source") {
128
+ const src = part.source;
129
+ if (src?.sourceType === "document") {
130
+ content.push({
131
+ type: "source-document",
132
+ sourceId: src.id,
133
+ mediaType: src.mediaType,
134
+ title: src.title,
135
+ filename: src.filename,
136
+ providerMetadata: src.providerMetadata
137
+ });
138
+ } else if (typeof src?.url === "string" && src.url.length > 0) {
139
+ content.push({
140
+ type: "source-url",
141
+ sourceId: src.id,
142
+ url: src.url,
143
+ title: src.title,
144
+ providerMetadata: src.providerMetadata
145
+ });
89
146
  }
90
- };
91
- }
92
- });
93
-
94
- // src/api/http.client.ts
95
- import { randomUUID } from "crypto";
96
- var HttpClient;
97
- var init_http_client = __esm({
98
- "src/api/http.client.ts"() {
99
- "use strict";
100
- init_auth_error();
101
- HttpClient = class {
102
- static {
103
- __name(this, "HttpClient");
147
+ continue;
148
+ }
149
+ if (part?.type === "source-url") {
150
+ if (typeof part.url === "string" && part.url.length > 0) {
151
+ content.push({
152
+ type: "source-url",
153
+ sourceId: part.sourceId,
154
+ url: part.url,
155
+ title: part.title,
156
+ providerMetadata: part.providerMetadata
157
+ });
104
158
  }
105
- baseUrl;
106
- /**
107
- * Creates an instance of HttpClient
108
- * @param baseUrl - The base URL for all API requests
109
- */
110
- constructor(baseUrl) {
111
- this.baseUrl = baseUrl;
159
+ continue;
160
+ }
161
+ if (part?.type === "source-document") {
162
+ content.push({
163
+ type: "source-document",
164
+ sourceId: part.sourceId,
165
+ mediaType: part.mediaType,
166
+ title: part.title,
167
+ filename: part.filename,
168
+ providerMetadata: part.providerMetadata
169
+ });
170
+ continue;
171
+ }
172
+ if (typeof part?.type === "string" && part.type.startsWith("data-lua-")) {
173
+ content.push({
174
+ type: part.type,
175
+ payload: rawPart.data
176
+ });
177
+ continue;
178
+ }
179
+ if (part?.type !== "text" && part?.type !== "file") continue;
180
+ if (part.type === "text" && typeof part.text === "string") {
181
+ const rawText = part.text || "";
182
+ if (rawText.includes("::: hide")) continue;
183
+ const audioMatch = rawText.match(/::: audio\s*!\[(.*?)\]\((.*?)\)\s*:::/);
184
+ const videoMatch = rawText.match(/::: video\s*!\[(.*?)\]\((.*?)\)\s*:::/);
185
+ if (audioMatch) {
186
+ content.push({
187
+ type: "audio",
188
+ data: audioMatch[2],
189
+ mediaType: audioMatch[1]
190
+ });
191
+ } else if (videoMatch) {
192
+ content.push({
193
+ type: "video",
194
+ video: videoMatch[2],
195
+ mediaType: videoMatch[1]
196
+ });
197
+ } else {
198
+ let text = rawText.replace(/\\\\\\n/g, "\n");
199
+ text = removeNavigateBlock(text);
200
+ content.push({
201
+ type: "text",
202
+ text
203
+ });
112
204
  }
113
- /**
114
- * Makes an HTTP request with standardized error handling
115
- * @param url - The full URL to request
116
- * @param options - Fetch API request options
117
- * @returns Promise resolving to an ApiResponse with typed data
118
- * @private
119
- */
120
- async request(url, options = {}) {
121
- const controller = new AbortController();
122
- const timeoutId = setTimeout(() => controller.abort(), 3e4);
123
- try {
124
- const response = await fetch(url, {
125
- ...options,
126
- signal: controller.signal,
127
- headers: {
128
- "Content-Type": "application/json",
129
- ...options.headers
130
- }
131
- });
132
- clearTimeout(timeoutId);
133
- if (!response.ok) {
134
- let errorData;
135
- try {
136
- errorData = await response.json();
137
- } catch (jsonError) {
138
- errorData = {};
139
- }
140
- if (response.status === 401) {
141
- const serverMessage = typeof errorData.message === "string" ? errorData.message : void 0;
142
- if (serverMessage && /not an admin/i.test(serverMessage)) {
143
- throw new AuthenticationError(`Access denied for this agent: ${serverMessage}`, "no_agent_access", serverMessage);
144
- }
145
- const isExplicitCredential = !!serverMessage && /(invalid|expired|missing|no)\s+(api[\s_-]?key|token|credential)/i.test(serverMessage);
146
- const isBareAuthRejection = !serverMessage || /^unauthorized$/i.test(serverMessage);
147
- if (isExplicitCredential || isBareAuthRejection) {
148
- throw new AuthenticationError("Authentication failed. Your API key may be invalid or expired.", "invalid_credentials", serverMessage);
149
- }
150
- throw new AuthenticationError(`Authentication failed: ${serverMessage}`, "unknown", serverMessage);
151
- }
152
- if (response.status === 403) {
153
- const detail = errorData.message || "You do not have permission to access this resource.";
154
- throw new Error(`Access denied (403): ${detail}
155
- Check that your API key has access to this agent/organization.`);
156
- }
157
- return {
158
- success: false,
159
- error: {
160
- message: errorData.message || `HTTP ${response.status}: ${response.statusText}`,
161
- statusCode: response.status,
162
- error: errorData.error,
163
- ...errorData
164
- }
165
- };
166
- }
167
- let data;
168
- try {
169
- data = await response.json();
170
- } catch (jsonError) {
171
- data = {};
172
- }
173
- if (typeof data === "object" && data !== null && "success" in data) {
174
- return data;
175
- }
176
- return {
177
- success: true,
178
- data
179
- };
180
- } catch (error) {
181
- clearTimeout(timeoutId);
182
- if (AuthenticationError.isAuthenticationError(error)) {
183
- throw error;
184
- }
185
- if (error instanceof Error && error.message.startsWith("Access denied (403)")) {
186
- throw error;
187
- }
188
- if (error instanceof DOMException && error.name === "AbortError") {
189
- return {
190
- success: false,
191
- error: {
192
- message: "Request timeout (30s)",
193
- statusCode: 0
194
- }
195
- };
196
- }
197
- return {
198
- success: false,
199
- error: {
200
- message: error instanceof Error ? error.message : "Network request failed",
201
- statusCode: 0
202
- }
203
- };
204
- }
205
- }
206
- /**
207
- * Checks if an HTTP status code is retryable
208
- * @param statusCode - The HTTP status code (0 for network errors)
209
- * @returns True if the request should be retried
210
- * @private
211
- */
212
- isRetryableStatus(statusCode) {
213
- return statusCode === 0 || statusCode === 429 || statusCode >= 500 && statusCode <= 504;
214
- }
215
- /**
216
- * Calculates exponential backoff with full jitter (AWS best practice)
217
- * @param attempt - The retry attempt number (0-based)
218
- * @param baseMs - Base delay in milliseconds
219
- * @param maxMs - Maximum delay cap in milliseconds
220
- * @returns Delay in milliseconds with random jitter
221
- * @private
222
- */
223
- calculateBackoff(attempt, baseMs = 1e3, maxMs = 15e3) {
224
- const exponential = Math.min(maxMs, baseMs * Math.pow(2, attempt));
225
- return Math.max(100, Math.random() * exponential);
226
- }
227
- /**
228
- * Wraps request with retry logic for transient failures
229
- * @param url - The full URL to request
230
- * @param options - Fetch API request options
231
- * @param maxRetries - Maximum number of retry attempts (default 3)
232
- * @returns Promise resolving to an ApiResponse with typed data
233
- * @private
234
- */
235
- async retryableRequest(url, options = {}, maxRetries = 3) {
236
- if (options.method === "POST") {
237
- const headers = options.headers || {};
238
- if (!headers["X-Idempotency-Key"]) {
239
- headers["X-Idempotency-Key"] = randomUUID();
240
- options = {
241
- ...options,
242
- headers: {
243
- ...options.headers,
244
- ...headers
245
- }
246
- };
247
- }
248
- }
249
- let lastResult = null;
250
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
251
- try {
252
- const result = await this.request(url, options);
253
- if (result.success || !result.error || !this.isRetryableStatus(result.error.statusCode || 0)) {
254
- return result;
255
- }
256
- lastResult = result;
257
- } catch (error) {
258
- throw error;
259
- }
260
- if (attempt < maxRetries) {
261
- const backoff = this.calculateBackoff(attempt);
262
- await new Promise((resolve) => setTimeout(resolve, backoff));
263
- }
264
- }
265
- return lastResult;
266
- }
267
- /**
268
- * Performs an HTTP GET request
269
- * @param url - The relative URL path to request (will be appended to baseUrl)
270
- * @param headers - Optional HTTP headers to include in the request
271
- * @returns Promise resolving to an ApiResponse with typed data
272
- * @protected
273
- */
274
- async httpGet(url, headers) {
275
- return this.retryableRequest(this.baseUrl + url, {
276
- method: "GET",
277
- headers
278
- });
279
- }
280
- /**
281
- * Performs an HTTP POST request
282
- * @param url - The relative URL path to request (will be appended to baseUrl)
283
- * @param data - Optional request body data (will be JSON stringified)
284
- * @param headers - Optional HTTP headers to include in the request
285
- * @returns Promise resolving to an ApiResponse with typed data
286
- * @protected
287
- */
288
- async httpPost(url, data, headers) {
289
- return this.retryableRequest(this.baseUrl + url, {
290
- method: "POST",
291
- body: data ? JSON.stringify(data) : void 0,
292
- headers
205
+ } else if (part.type === "file") {
206
+ const mediaType = part.mimeType || "";
207
+ if (mediaType.startsWith("image/")) {
208
+ content.push({
209
+ type: "image",
210
+ image: part.data,
211
+ mediaType
293
212
  });
294
- }
295
- /**
296
- * Performs an HTTP PUT request
297
- * @param url - The relative URL path to request (will be appended to baseUrl)
298
- * @param data - Optional request body data (will be JSON stringified)
299
- * @param headers - Optional HTTP headers to include in the request
300
- * @returns Promise resolving to an ApiResponse with typed data
301
- * @protected
302
- */
303
- async httpPut(url, data, headers) {
304
- return this.retryableRequest(this.baseUrl + url, {
305
- method: "PUT",
306
- body: data ? JSON.stringify(data) : void 0,
307
- headers
213
+ } else if (mediaType.startsWith("video/")) {
214
+ content.push({
215
+ type: "video",
216
+ video: part.data,
217
+ mediaType
308
218
  });
309
- }
310
- /**
311
- * Performs an HTTP DELETE request
312
- * @param url - The relative URL path to request (will be appended to baseUrl)
313
- * @param headers - Optional HTTP headers to include in the request
314
- * @returns Promise resolving to an ApiResponse with typed data
315
- * @protected
316
- */
317
- async httpDelete(url, headers) {
318
- return this.retryableRequest(this.baseUrl + url, {
319
- method: "DELETE",
320
- headers
219
+ } else if (mediaType.startsWith("audio/")) {
220
+ content.push({
221
+ type: "audio",
222
+ data: part.data,
223
+ mediaType
321
224
  });
322
- }
323
- /**
324
- * Performs an HTTP PATCH request
325
- * @param url - The relative URL path to request (will be appended to baseUrl)
326
- * @param data - Optional request body data (will be JSON stringified)
327
- * @param headers - Optional HTTP headers to include in the request
328
- * @returns Promise resolving to an ApiResponse with typed data
329
- * @protected
330
- */
331
- async httpPatch(url, data, headers) {
332
- return this.retryableRequest(this.baseUrl + url, {
333
- method: "PATCH",
334
- body: data ? JSON.stringify(data) : void 0,
335
- headers
225
+ } else {
226
+ content.push({
227
+ type: "file",
228
+ data: part.data,
229
+ mediaType
336
230
  });
337
231
  }
338
- };
232
+ }
339
233
  }
340
- });
341
-
342
- // src/api/auth.api.service.ts
343
- var init_auth_api_service = __esm({
344
- "src/api/auth.api.service.ts"() {
345
- "use strict";
346
- init_http_client();
234
+ return content;
235
+ }
236
+ function isSyntheticSideRow(id) {
237
+ return id.startsWith(SCREENSHOT_MESSAGE_ID_PREFIX) || id.startsWith(RICH_PARTS_MESSAGE_ID_PREFIX);
238
+ }
239
+ function mergeRichPartMirrorMessages(messages, sameTurnGroup) {
240
+ const merged = [];
241
+ for (const message of messages) {
242
+ if (message.role === "assistant" && message.id.startsWith(RICH_PARTS_MESSAGE_ID_PREFIX)) {
243
+ let folded = false;
244
+ for (let i = merged.length - 1; i >= 0; i--) {
245
+ const target = merged[i];
246
+ if (sameTurnGroup && !sameTurnGroup(target, message)) continue;
247
+ if (isSyntheticSideRow(target.id)) continue;
248
+ if (target.role !== "assistant") break;
249
+ const seen = /* @__PURE__ */ new Set();
250
+ for (const part of target.content) {
251
+ for (const key of citationDedupeKeys(part)) seen.add(key);
252
+ }
253
+ const incoming = [];
254
+ for (const part of message.content) {
255
+ const keys = citationDedupeKeys(part);
256
+ if (keys.length > 0 && keys.some((k) => seen.has(k))) continue;
257
+ for (const key of keys) seen.add(key);
258
+ incoming.push(part);
259
+ }
260
+ merged[i] = {
261
+ ...target,
262
+ content: [
263
+ ...target.content,
264
+ ...incoming
265
+ ]
266
+ };
267
+ folded = true;
268
+ break;
269
+ }
270
+ if (folded) continue;
271
+ }
272
+ merged.push(message);
347
273
  }
348
- });
349
-
350
- // src/services/auth.ts
351
- import "dotenv/config";
352
- import { readFileSync, writeFileSync, mkdirSync, unlinkSync } from "fs";
353
- function getToken() {
354
- if (process.env.LUA_API_KEY) {
355
- return process.env.LUA_API_KEY;
274
+ return merged;
275
+ }
276
+ function citationDedupeKeys(part) {
277
+ if (part.type !== "source-url" && part.type !== "source-document") return [];
278
+ const keys = [];
279
+ if (typeof part.sourceId === "string" && part.sourceId.length > 0) keys.push(`id:${part.sourceId}`);
280
+ if (typeof part.url === "string" && part.url.length > 0) keys.push(`url:${part.url}`);
281
+ return keys;
282
+ }
283
+ function foldRichPartsIntoMessages(messages, records, makeMessage, sameTurnGroup) {
284
+ if (messages.length === 0 || records.length === 0) return messages;
285
+ const messageTime = /* @__PURE__ */ __name2((m) => m.createdAt ? new Date(m.createdAt).getTime() : Number.NEGATIVE_INFINITY, "messageTime");
286
+ const finiteTimes = messages.map(messageTime).filter(Number.isFinite);
287
+ const oldest = finiteTimes.length > 0 ? Math.min(...finiteTimes) : Number.NEGATIVE_INFINITY;
288
+ const synthetic = [];
289
+ for (const record of records) {
290
+ const time = new Date(record.createdAt).getTime();
291
+ if (!Number.isFinite(time) || time < oldest) continue;
292
+ const content = transformChatHistoryContentParts(record.parts);
293
+ if (content.length === 0) continue;
294
+ synthetic.push({
295
+ time,
296
+ message: makeMessage({
297
+ id: `${RICH_PARTS_MESSAGE_ID_PREFIX}${record.threadId}:${record.messageId}`,
298
+ role: "assistant",
299
+ createdAt: new Date(time).toISOString(),
300
+ content
301
+ }, record)
302
+ });
356
303
  }
357
- try {
358
- const token = readFileSync(CREDENTIALS_FILE, "utf8").trim();
359
- if (token) return token;
360
- } catch {
304
+ if (synthetic.length === 0) return messages;
305
+ synthetic.sort((a, b) => a.time - b.time);
306
+ const combined = [];
307
+ let next = 0;
308
+ for (const message of messages) {
309
+ const time = messageTime(message);
310
+ while (next < synthetic.length && synthetic[next].time < time) {
311
+ combined.push(synthetic[next++].message);
312
+ }
313
+ combined.push(message);
361
314
  }
362
- throw new AuthenticationError('No API key found.\n\n Authenticate using one of these methods:\n\n \u279C lua auth configure\n \u279C export LUA_API_KEY="your-api-key-here"\n \u279C Add LUA_API_KEY=... to a .env file\n\n \u{1F511} Get your API key at https://admin.heylua.ai', "invalid_credentials", void 0, true);
315
+ while (next < synthetic.length) combined.push(synthetic[next++].message);
316
+ return mergeRichPartMirrorMessages(combined, sameTurnGroup);
363
317
  }
364
- var init_auth = __esm({
365
- "src/services/auth.ts"() {
366
- "use strict";
367
- init_auth_api_service();
368
- init_constants();
369
- init_auth_error();
370
- __name(getToken, "getToken");
371
- }
372
- });
373
-
374
- // src/config/compile.constants.ts
375
- var COMPILE_DIRS, COMPILE_FILES, SKILL_DEFAULTS, YAML_FORMAT;
376
- var init_compile_constants = __esm({
377
- "src/config/compile.constants.ts"() {
378
- "use strict";
379
- COMPILE_DIRS = {
380
- DIST: "dist",
381
- DIST_V2: "dist-v2",
382
- LUA: ".lua",
383
- TOOLS: "tools"
384
- };
385
- COMPILE_FILES = {
386
- DEPLOYMENT_JSON: "deployment.json",
387
- DEPLOY_JSON: "deploy.json",
388
- MANIFEST_JSON: "manifest.json",
389
- INDEX_TS: "index.ts",
390
- INDEX_JS: "index.js",
391
- PACKAGE_JSON: "package.json",
392
- TSCONFIG_JSON: "tsconfig.json",
393
- LUA_SKILL_YAML: "lua.skill.yaml"
394
- };
395
- SKILL_DEFAULTS = {
396
- NAME: "lua-skill",
397
- VERSION: "1.0.0",
398
- DESCRIPTION: "",
399
- CONTEXT: ""
400
- };
401
- YAML_FORMAT = {
402
- INDENT: 2,
403
- LINE_WIDTH: -1,
404
- NO_REFS: true
405
- };
406
- }
407
- });
408
-
409
- // ../shared-types/dist/index.mjs
410
- import { z } from "zod";
411
- function isPersonaTextObject(value) {
412
- if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
413
- const obj = value;
414
- for (const k of Object.keys(obj)) {
415
- if (k !== "base" && k !== "voice" && k !== "text") return false;
416
- if (obj[k] !== void 0 && typeof obj[k] !== "string") return false;
417
- }
418
- return true;
419
- }
420
- function flattenPersonaText(input, isVoice = false) {
421
- if (input == null) return "";
422
- if (typeof input === "string") return input;
423
- const parts = [];
424
- if (input.base) parts.push(input.base);
425
- const channelText = isVoice ? input.voice : input.text;
426
- if (channelText) parts.push(channelText);
427
- return parts.join("\n\n");
428
- }
429
- function flattenPersonaTextAll(input) {
430
- if (input == null) return "";
431
- if (typeof input === "string") return input;
432
- const parts = [];
433
- if (input.base) parts.push(input.base);
434
- if (input.voice) parts.push(input.voice);
435
- if (input.text) parts.push(input.text);
436
- return parts.join("\n\n");
437
- }
438
- function hasPersonaTextContent(input) {
439
- if (input == null) return false;
440
- if (typeof input === "string") return input.trim().length > 0;
441
- return Boolean(input.base?.trim() || input.voice?.trim() || input.text?.trim());
442
- }
443
- function personaToLiteral(persona) {
444
- const formatValue = /* @__PURE__ */ __name2((v) => {
445
- if (v.includes("\n")) {
446
- return "`" + v.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$") + "`";
447
- }
448
- return '"' + v.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
449
- }, "formatValue");
450
- if (typeof persona === "string") return formatValue(persona);
451
- const parts = [];
452
- if (persona.base !== void 0) parts.push(`base: ${formatValue(persona.base)}`);
453
- if (persona.voice !== void 0) parts.push(`voice: ${formatValue(persona.voice)}`);
454
- if (persona.text !== void 0) parts.push(`text: ${formatValue(persona.text)}`);
455
- return `{ ${parts.join(", ")} }`;
456
- }
457
- function aiGenerateInputFromSimplified(prompt, content) {
458
- if (content === void 0) {
459
- return {
460
- prompt
461
- };
462
- }
463
- return {
464
- system: prompt,
465
- messages: [
466
- {
467
- role: "user",
468
- content
469
- }
470
- ]
471
- };
472
- }
473
- function removeNavigateBlock(input) {
474
- return input.replace(/::: navigate[\s\S]*?:::/g, "").trim();
475
- }
476
- function transformChatHistoryContentParts(parts) {
477
- const content = [];
478
- for (const rawPart of parts ?? []) {
479
- const part = rawPart;
480
- if (part?.type !== "text" && part?.type !== "file") continue;
481
- if (part.type === "text" && typeof part.text === "string") {
482
- const rawText = part.text || "";
483
- if (rawText.includes("::: hide")) continue;
484
- const audioMatch = rawText.match(/::: audio\s*!\[(.*?)\]\((.*?)\)\s*:::/);
485
- const videoMatch = rawText.match(/::: video\s*!\[(.*?)\]\((.*?)\)\s*:::/);
486
- if (audioMatch) {
487
- content.push({
488
- type: "audio",
489
- data: audioMatch[2],
490
- mediaType: audioMatch[1]
491
- });
492
- } else if (videoMatch) {
493
- content.push({
494
- type: "video",
495
- video: videoMatch[2],
496
- mediaType: videoMatch[1]
497
- });
498
- } else {
499
- let text = rawText.replace(/\\\\\\n/g, "\n");
500
- text = removeNavigateBlock(text);
501
- content.push({
502
- type: "text",
503
- text
504
- });
505
- }
506
- } else if (part.type === "file") {
507
- const mediaType = part.mimeType || "";
508
- if (mediaType.startsWith("image/")) {
509
- content.push({
510
- type: "image",
511
- image: part.data,
512
- mediaType
513
- });
514
- } else if (mediaType.startsWith("video/")) {
515
- content.push({
516
- type: "video",
517
- video: part.data,
518
- mediaType
519
- });
520
- } else if (mediaType.startsWith("audio/")) {
521
- content.push({
522
- type: "audio",
523
- data: part.data,
524
- mediaType
525
- });
526
- } else {
527
- content.push({
528
- type: "file",
529
- data: part.data,
530
- mediaType
531
- });
532
- }
533
- }
534
- }
535
- return content;
536
- }
537
- function buildDefaultPersona(agentName) {
538
- return DEFAULT_PERSONA_GUIDE.replace(AGENT_NAME_TOKEN, () => agentName || "My Agent");
539
- }
540
- var __defProp2, __name2, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema;
541
- var init_dist = __esm({
542
- "../shared-types/dist/index.mjs"() {
318
+ function buildDefaultPersona(agentName) {
319
+ return DEFAULT_PERSONA_GUIDE.replace(AGENT_NAME_TOKEN, () => agentName || "My Agent");
320
+ }
321
+ var __defProp2, __name2, CHANNEL_SEND_CHANNELS, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, REASONING_EFFORT_VALUES, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema;
322
+ var init_dist = __esm({
323
+ "../shared-types/dist/index.mjs"() {
543
324
  "use strict";
544
325
  __defProp2 = Object.defineProperty;
545
326
  __name2 = /* @__PURE__ */ __name((target, value) => __defProp2(target, "name", { value, configurable: true }), "__name");
@@ -555,10 +336,270 @@ var init_dist = __esm({
555
336
  __name2(personaToLiteral, "personaToLiteral");
556
337
  __name(aiGenerateInputFromSimplified, "aiGenerateInputFromSimplified");
557
338
  __name2(aiGenerateInputFromSimplified, "aiGenerateInputFromSimplified");
339
+ CHANNEL_SEND_CHANNELS = [
340
+ "whatsapp",
341
+ "sms",
342
+ "email",
343
+ "webchat",
344
+ "teams",
345
+ "instagram",
346
+ "messenger"
347
+ ];
348
+ REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST = [
349
+ "sendChannelMessage",
350
+ "sendWhatsappTemplate",
351
+ "sendEmail",
352
+ "sendWhatsappMessage",
353
+ "sendSms",
354
+ "sendWebchatMessage",
355
+ "sendTeamsMessage",
356
+ "sendInstagramMessage",
357
+ "sendMessengerMessage"
358
+ ];
359
+ __name(isAllowedReviewableExecuteTool, "isAllowedReviewableExecuteTool");
360
+ __name2(isAllowedReviewableExecuteTool, "isAllowedReviewableExecuteTool");
361
+ REVIEWABLE_MCP_SEND_TOOL_SUFFIX = "_create_messaging_message";
362
+ __name(isReviewableMcpSendTool, "isReviewableMcpSendTool");
363
+ __name2(isReviewableMcpSendTool, "isReviewableMcpSendTool");
364
+ __name(isReviewableExecuteTool, "isReviewableExecuteTool");
365
+ __name2(isReviewableExecuteTool, "isReviewableExecuteTool");
366
+ NON_INTERACTIVE_CHANNELS = [
367
+ "trigger",
368
+ "agent-invocation"
369
+ ];
370
+ __name(isInteractiveChannel, "isInteractiveChannel");
371
+ __name2(isInteractiveChannel, "isInteractiveChannel");
372
+ __name(isInteractiveTurn, "isInteractiveTurn");
373
+ __name2(isInteractiveTurn, "isInteractiveTurn");
558
374
  __name(removeNavigateBlock, "removeNavigateBlock");
559
375
  __name2(removeNavigateBlock, "removeNavigateBlock");
560
376
  __name(transformChatHistoryContentParts, "transformChatHistoryContentParts");
561
377
  __name2(transformChatHistoryContentParts, "transformChatHistoryContentParts");
378
+ RICH_PARTS_MESSAGE_ID_PREFIX = "rich-parts:";
379
+ SCREENSHOT_MESSAGE_ID_PREFIX = "screenshot:";
380
+ __name(isSyntheticSideRow, "isSyntheticSideRow");
381
+ __name2(isSyntheticSideRow, "isSyntheticSideRow");
382
+ __name(mergeRichPartMirrorMessages, "mergeRichPartMirrorMessages");
383
+ __name2(mergeRichPartMirrorMessages, "mergeRichPartMirrorMessages");
384
+ __name(citationDedupeKeys, "citationDedupeKeys");
385
+ __name2(citationDedupeKeys, "citationDedupeKeys");
386
+ __name(foldRichPartsIntoMessages, "foldRichPartsIntoMessages");
387
+ __name2(foldRichPartsIntoMessages, "foldRichPartsIntoMessages");
388
+ BROWSER_COMMANDS = [
389
+ // health + lifecycle / navigation
390
+ {
391
+ name: "health",
392
+ description: "Check the local browser engine is installed and responsive."
393
+ },
394
+ {
395
+ name: "session_open",
396
+ description: "Open/attach a browser session (its own cookies/auth). Args: url?, headed?, profile?, confirmActions?."
397
+ },
398
+ {
399
+ name: "navigate",
400
+ description: "Navigate the session to a URL. Args: url, waitUntil?."
401
+ },
402
+ {
403
+ name: "back",
404
+ description: "Go back in history."
405
+ },
406
+ {
407
+ name: "forward",
408
+ description: "Go forward in history."
409
+ },
410
+ {
411
+ name: "reload",
412
+ description: "Reload the current page."
413
+ },
414
+ {
415
+ name: "pushstate",
416
+ description: "SPA client-side navigation. Args: url."
417
+ },
418
+ {
419
+ name: "close",
420
+ description: "Close the session\u2019s browser."
421
+ },
422
+ // perception
423
+ {
424
+ name: "snapshot",
425
+ description: "Accessibility-tree snapshot with element refs (@e1\u2026) \u2014 see what to click/fill. Args: interactiveOnly?, selector?, urls?, compact?, depth?."
426
+ },
427
+ {
428
+ name: "get",
429
+ description: "Read from the page. Args: what(text|html|value|attr|title|url|count|box|styles), selector?, attr?."
430
+ },
431
+ {
432
+ name: "is",
433
+ description: "Check element state. Args: check(visible|enabled|checked), selector."
434
+ },
435
+ // interaction
436
+ {
437
+ name: "click",
438
+ description: "Click an element. Args: selector(@eN or CSS), newTab?."
439
+ },
440
+ {
441
+ name: "dblclick",
442
+ description: "Double-click an element. Args: selector."
443
+ },
444
+ {
445
+ name: "fill",
446
+ description: "Clear and fill a field. Args: selector, text."
447
+ },
448
+ {
449
+ name: "type",
450
+ description: "Type into an element. Args: selector, text."
451
+ },
452
+ {
453
+ name: "press",
454
+ description: "Press a key/chord (Enter, Control+a). Args: key."
455
+ },
456
+ {
457
+ name: "hover",
458
+ description: "Hover an element. Args: selector."
459
+ },
460
+ {
461
+ name: "focus",
462
+ description: "Focus an element. Args: selector."
463
+ },
464
+ {
465
+ name: "select",
466
+ description: "Select a dropdown option. Args: selector, value."
467
+ },
468
+ {
469
+ name: "check",
470
+ description: "Check a checkbox. Args: selector."
471
+ },
472
+ {
473
+ name: "uncheck",
474
+ description: "Uncheck a checkbox. Args: selector."
475
+ },
476
+ {
477
+ name: "scroll",
478
+ description: "Scroll. Args: direction(up|down|left|right), px?, selector?."
479
+ },
480
+ {
481
+ name: "scrollintoview",
482
+ description: "Scroll an element into view. Args: selector."
483
+ },
484
+ {
485
+ name: "drag",
486
+ description: "Drag and drop. Args: source, target."
487
+ },
488
+ {
489
+ name: "upload",
490
+ description: "Upload local file(s) to a file input. Args: selector, files[]."
491
+ },
492
+ {
493
+ name: "find",
494
+ description: "Act by semantic locator. Args: by(role|text|label|placeholder|alt|title|testid), query, action(click|fill|type|hover|focus|check|uncheck|text), value?, name?, exact?."
495
+ },
496
+ // AI fallbacks
497
+ {
498
+ name: "act",
499
+ description: "Act on the page: ref+action (deterministic) or natural-language instruction (engine AI). Args: ref?, action?, value?, instruction?."
500
+ },
501
+ {
502
+ name: "extract",
503
+ description: "Extract data by natural-language instruction (engine AI). Args: instruction."
504
+ },
505
+ // wait
506
+ {
507
+ name: "wait",
508
+ description: "Wait for a condition. Provide one of: selector(+state), ms, text, url, load, fn."
509
+ },
510
+ // tabs / frames
511
+ {
512
+ name: "tab",
513
+ description: "Manage tabs. Args: action(list|new|switch|close), target?, url?, label?."
514
+ },
515
+ {
516
+ name: "window_new",
517
+ description: "Open a new browser window. Args: url?."
518
+ },
519
+ {
520
+ name: "frame",
521
+ description: 'Switch frame context. Args: target(@eN | CSS | "main").'
522
+ },
523
+ // capture
524
+ {
525
+ name: "screenshot",
526
+ description: "Screenshot the page. Args: fullPage?, path?."
527
+ },
528
+ {
529
+ name: "pdf",
530
+ description: "Save the page as PDF. Args: path."
531
+ },
532
+ // state
533
+ {
534
+ name: "cookies",
535
+ description: "Manage cookies. Args: action(get|set|clear), name?, value?."
536
+ },
537
+ {
538
+ name: "storage",
539
+ description: "Manage web storage. Args: area(local|session), action(get|set|clear), key?, value?."
540
+ },
541
+ {
542
+ name: "set",
543
+ description: "Configure the browser. Args: setting(viewport|device|geo|headers|credentials|media), args[]."
544
+ },
545
+ // files / clipboard
546
+ {
547
+ name: "download",
548
+ description: "Download a file (click a selector to trigger, or wait for one). Args: selector?, path?."
549
+ },
550
+ {
551
+ name: "clipboard",
552
+ description: "Clipboard. Args: action(read|write|copy|paste), text?."
553
+ },
554
+ // auth (use-only)
555
+ {
556
+ name: "auth",
557
+ description: "Use a saved login profile. Args: action(login|list|show), name?. (Credentials are saved via the desktop, never the agent.)"
558
+ },
559
+ // confirmation gate
560
+ {
561
+ name: "confirm",
562
+ description: "Approve a pending confirmation_required action. Args: id."
563
+ },
564
+ {
565
+ name: "deny",
566
+ description: "Reject a pending confirmation_required action. Args: id."
567
+ },
568
+ // network / debug / input / state-files
569
+ {
570
+ name: "network",
571
+ description: "Inspect/control network. Args: action(route|unroute|requests|har) + relevant fields."
572
+ },
573
+ {
574
+ name: "console",
575
+ description: "View browser console messages. Args: clear?."
576
+ },
577
+ {
578
+ name: "errors",
579
+ description: "View uncaught page JS errors. Args: clear?."
580
+ },
581
+ {
582
+ name: "mouse",
583
+ description: "Low-level mouse. Args: action(move|down|up|wheel), x?, y?, button?, dy?, dx?."
584
+ },
585
+ {
586
+ name: "keyboard",
587
+ description: "Low-level keyboard at focus. Args: action(type|inserttext|keydown|keyup), text?, key?."
588
+ },
589
+ {
590
+ name: "state",
591
+ description: "Persist/restore storage+auth state to a file. Args: action(save|load|list|clear), path?."
592
+ }
593
+ ];
594
+ BROWSER_COMMAND_NAMES = BROWSER_COMMANDS.map((c) => c.name);
595
+ REASONING_EFFORT_VALUES = [
596
+ "off",
597
+ "minimal",
598
+ "low",
599
+ "medium",
600
+ "high",
601
+ "max"
602
+ ];
562
603
  AGENT_NAME_TOKEN = "[Your Agent Name]";
563
604
  DEFAULT_PERSONA_GUIDE = `# ${AGENT_NAME_TOKEN} - Persona
564
605
 
@@ -796,7 +837,15 @@ Feel free to add, remove, or rename sections. Your persona can be a single parag
796
837
  // to the LLM as a ToolError — fills the 2–3s gap before the LLM's own
797
838
  // recovery response. Persona-specific (keep it short and on-brand);
798
839
  // absent → no spoken fallback (the LLM's recovery is the only signal).
799
- onToolFailureSay: z.string().min(1).max(200).optional()
840
+ onToolFailureSay: z.string().min(1).max(200).optional(),
841
+ // Tool names to withhold from this voice agent's session. Matches ANY
842
+ // resolved tool: the platform base tools (searchKnowledgeBase, searchWeb,
843
+ // geocoding, the send* structured-output family), MCP/device tools, and the
844
+ // agent's own skill tools compiled into the artifact. Typical use is dropping
845
+ // the on-screen send* tools (`sendPayment`, `sendListItems`, …) on a
846
+ // screenless phone agent where they have nowhere to render. Names that match
847
+ // nothing are ignored (warn, not error) so a typo can't fail the push.
848
+ excludeTools: z.array(z.string().min(1)).optional()
800
849
  });
801
850
  LuaVoiceConfigSchema = LuaVoiceConfigInnerSchema.superRefine((cfg, ctx) => {
802
851
  const isRealtime = cfg.llm.kind === "realtime";
@@ -839,11 +888,408 @@ Feel free to add, remove, or rename sections. Your persona can be a single parag
839
888
  });
840
889
  }
841
890
  }
842
- });
843
- LuaVoiceRefSchema = z.object({
844
- voiceId: z.string().min(1),
845
- version: z.string().optional()
846
- });
891
+ });
892
+ LuaVoiceRefSchema = z.object({
893
+ voiceId: z.string().min(1),
894
+ version: z.string().optional()
895
+ });
896
+ }
897
+ });
898
+
899
+ // src/interfaces/baskets.ts
900
+ var BasketStatus;
901
+ var init_baskets = __esm({
902
+ "src/interfaces/baskets.ts"() {
903
+ "use strict";
904
+ BasketStatus = /* @__PURE__ */ (function(BasketStatus2) {
905
+ BasketStatus2["ACTIVE"] = "active";
906
+ BasketStatus2["CHECKED_OUT"] = "checked_out";
907
+ BasketStatus2["ABANDONED"] = "abandoned";
908
+ BasketStatus2["EXPIRED"] = "expired";
909
+ return BasketStatus2;
910
+ })({});
911
+ }
912
+ });
913
+
914
+ // src/config/constants.ts
915
+ import { join } from "path";
916
+ import { homedir } from "os";
917
+ var CLI_CONFIG_DIR, VERSION_CHECK_FILE, TELEMETRY_FILE, CLI_CACHE_FILE, BASE_URLS, CREDENTIALS_FILE, SANDBOX_STORAGE_FILE, AUTH_STORAGE_FILE;
918
+ var init_constants = __esm({
919
+ "src/config/constants.ts"() {
920
+ "use strict";
921
+ CLI_CONFIG_DIR = join(homedir(), ".lua-cli");
922
+ VERSION_CHECK_FILE = join(CLI_CONFIG_DIR, "version-check.json");
923
+ TELEMETRY_FILE = join(CLI_CONFIG_DIR, "telemetry.json");
924
+ CLI_CACHE_FILE = join(CLI_CONFIG_DIR, "cache.json");
925
+ BASE_URLS = {
926
+ API: process.env.LUA_API_URL || "https://api.heylua.ai",
927
+ AUTH: process.env.LUA_AUTH_URL || "https://auth.heylua.ai",
928
+ CHAT: process.env.LUA_API_URL || "https://api.heylua.ai",
929
+ WEBHOOK: process.env.LUA_WEBHOOK_URL || "https://webhook.heylua.ai",
930
+ CDN: "https://cdn.heylua.ai"
931
+ };
932
+ CREDENTIALS_FILE = join(CLI_CONFIG_DIR, "credentials");
933
+ SANDBOX_STORAGE_FILE = join(CLI_CONFIG_DIR, "sandbox.json");
934
+ AUTH_STORAGE_FILE = join(CLI_CONFIG_DIR, "auth.json");
935
+ }
936
+ });
937
+
938
+ // src/errors/auth.error.ts
939
+ var AuthenticationError;
940
+ var init_auth_error = __esm({
941
+ "src/errors/auth.error.ts"() {
942
+ "use strict";
943
+ AuthenticationError = class _AuthenticationError extends Error {
944
+ static {
945
+ __name(this, "AuthenticationError");
946
+ }
947
+ statusCode = 401;
948
+ isAuthenticationError = true;
949
+ reason;
950
+ serverMessage;
951
+ /**
952
+ * If true, the error message already contains complete remediation steps
953
+ * and `withErrorHandling` should not append its own hint block. Use this
954
+ * when the throw site has more context about the right fix than the
955
+ * generic per-reason hints (e.g. `getToken()` listing all three ways to
956
+ * configure a key — keychain, env var, .env file).
957
+ */
958
+ suppressDefaultRemediation;
959
+ constructor(message = "Invalid API key", reason = "unknown", serverMessage, suppressDefaultRemediation = false) {
960
+ super(message);
961
+ this.name = "AuthenticationError";
962
+ this.reason = reason;
963
+ this.serverMessage = serverMessage;
964
+ this.suppressDefaultRemediation = suppressDefaultRemediation;
965
+ if (Error.captureStackTrace) {
966
+ Error.captureStackTrace(this, _AuthenticationError);
967
+ }
968
+ }
969
+ /**
970
+ * Checks if an error is an AuthenticationError
971
+ * @param error - The error to check
972
+ * @returns True if the error is an AuthenticationError
973
+ */
974
+ static isAuthenticationError(error) {
975
+ return error instanceof _AuthenticationError || error instanceof Error && "isAuthenticationError" in error && error.isAuthenticationError === true;
976
+ }
977
+ };
978
+ }
979
+ });
980
+
981
+ // src/api/http.client.ts
982
+ import { randomUUID } from "crypto";
983
+ var HttpClient;
984
+ var init_http_client = __esm({
985
+ "src/api/http.client.ts"() {
986
+ "use strict";
987
+ init_auth_error();
988
+ HttpClient = class {
989
+ static {
990
+ __name(this, "HttpClient");
991
+ }
992
+ baseUrl;
993
+ /**
994
+ * Creates an instance of HttpClient
995
+ * @param baseUrl - The base URL for all API requests
996
+ */
997
+ constructor(baseUrl) {
998
+ this.baseUrl = baseUrl;
999
+ }
1000
+ /**
1001
+ * Makes an HTTP request with standardized error handling
1002
+ * @param url - The full URL to request
1003
+ * @param options - Fetch API request options
1004
+ * @returns Promise resolving to an ApiResponse with typed data
1005
+ * @private
1006
+ */
1007
+ async request(url, options = {}) {
1008
+ const controller = new AbortController();
1009
+ const timeoutId = setTimeout(() => controller.abort(), 3e4);
1010
+ try {
1011
+ const response = await fetch(url, {
1012
+ ...options,
1013
+ signal: controller.signal,
1014
+ headers: {
1015
+ "Content-Type": "application/json",
1016
+ ...options.headers
1017
+ }
1018
+ });
1019
+ clearTimeout(timeoutId);
1020
+ if (!response.ok) {
1021
+ let errorData;
1022
+ try {
1023
+ errorData = await response.json();
1024
+ } catch (jsonError) {
1025
+ errorData = {};
1026
+ }
1027
+ if (response.status === 401) {
1028
+ const serverMessage = typeof errorData.message === "string" ? errorData.message : void 0;
1029
+ if (serverMessage && /not an admin/i.test(serverMessage)) {
1030
+ throw new AuthenticationError(`Access denied for this agent: ${serverMessage}`, "no_agent_access", serverMessage);
1031
+ }
1032
+ const isExplicitCredential = !!serverMessage && /(invalid|expired|missing|no)\s+(api[\s_-]?key|token|credential)/i.test(serverMessage);
1033
+ const isBareAuthRejection = !serverMessage || /^unauthorized$/i.test(serverMessage);
1034
+ if (isExplicitCredential || isBareAuthRejection) {
1035
+ throw new AuthenticationError("Authentication failed. Your API key may be invalid or expired.", "invalid_credentials", serverMessage);
1036
+ }
1037
+ throw new AuthenticationError(`Authentication failed: ${serverMessage}`, "unknown", serverMessage);
1038
+ }
1039
+ if (response.status === 403) {
1040
+ const detail = errorData.message || "You do not have permission to access this resource.";
1041
+ throw new Error(`Access denied (403): ${detail}
1042
+ Check that your API key has access to this agent/organization.`);
1043
+ }
1044
+ return {
1045
+ success: false,
1046
+ error: {
1047
+ message: errorData.message || `HTTP ${response.status}: ${response.statusText}`,
1048
+ statusCode: response.status,
1049
+ error: errorData.error,
1050
+ ...errorData
1051
+ }
1052
+ };
1053
+ }
1054
+ let data;
1055
+ try {
1056
+ data = await response.json();
1057
+ } catch (jsonError) {
1058
+ data = {};
1059
+ }
1060
+ if (typeof data === "object" && data !== null && "success" in data) {
1061
+ return data;
1062
+ }
1063
+ return {
1064
+ success: true,
1065
+ data
1066
+ };
1067
+ } catch (error) {
1068
+ clearTimeout(timeoutId);
1069
+ if (AuthenticationError.isAuthenticationError(error)) {
1070
+ throw error;
1071
+ }
1072
+ if (error instanceof Error && error.message.startsWith("Access denied (403)")) {
1073
+ throw error;
1074
+ }
1075
+ if (error instanceof DOMException && error.name === "AbortError") {
1076
+ return {
1077
+ success: false,
1078
+ error: {
1079
+ message: "Request timeout (30s)",
1080
+ statusCode: 0
1081
+ }
1082
+ };
1083
+ }
1084
+ return {
1085
+ success: false,
1086
+ error: {
1087
+ message: error instanceof Error ? error.message : "Network request failed",
1088
+ statusCode: 0
1089
+ }
1090
+ };
1091
+ }
1092
+ }
1093
+ /**
1094
+ * Checks if an HTTP status code is retryable
1095
+ * @param statusCode - The HTTP status code (0 for network errors)
1096
+ * @returns True if the request should be retried
1097
+ * @private
1098
+ */
1099
+ isRetryableStatus(statusCode) {
1100
+ return statusCode === 0 || statusCode === 429 || statusCode >= 500 && statusCode <= 504;
1101
+ }
1102
+ /**
1103
+ * Calculates exponential backoff with full jitter (AWS best practice)
1104
+ * @param attempt - The retry attempt number (0-based)
1105
+ * @param baseMs - Base delay in milliseconds
1106
+ * @param maxMs - Maximum delay cap in milliseconds
1107
+ * @returns Delay in milliseconds with random jitter
1108
+ * @private
1109
+ */
1110
+ calculateBackoff(attempt, baseMs = 1e3, maxMs = 15e3) {
1111
+ const exponential = Math.min(maxMs, baseMs * Math.pow(2, attempt));
1112
+ return Math.max(100, Math.random() * exponential);
1113
+ }
1114
+ /**
1115
+ * Wraps request with retry logic for transient failures
1116
+ * @param url - The full URL to request
1117
+ * @param options - Fetch API request options
1118
+ * @param maxRetries - Maximum number of retry attempts (default 3)
1119
+ * @returns Promise resolving to an ApiResponse with typed data
1120
+ * @private
1121
+ */
1122
+ async retryableRequest(url, options = {}, maxRetries = 3) {
1123
+ if (options.method === "POST") {
1124
+ const headers = options.headers || {};
1125
+ if (!headers["X-Idempotency-Key"]) {
1126
+ headers["X-Idempotency-Key"] = randomUUID();
1127
+ options = {
1128
+ ...options,
1129
+ headers: {
1130
+ ...options.headers,
1131
+ ...headers
1132
+ }
1133
+ };
1134
+ }
1135
+ }
1136
+ let lastResult = null;
1137
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1138
+ try {
1139
+ const result = await this.request(url, options);
1140
+ if (result.success || !result.error || !this.isRetryableStatus(result.error.statusCode || 0)) {
1141
+ return result;
1142
+ }
1143
+ lastResult = result;
1144
+ } catch (error) {
1145
+ throw error;
1146
+ }
1147
+ if (attempt < maxRetries) {
1148
+ const backoff = this.calculateBackoff(attempt);
1149
+ await new Promise((resolve) => setTimeout(resolve, backoff));
1150
+ }
1151
+ }
1152
+ return lastResult;
1153
+ }
1154
+ /**
1155
+ * Performs an HTTP GET request
1156
+ * @param url - The relative URL path to request (will be appended to baseUrl)
1157
+ * @param headers - Optional HTTP headers to include in the request
1158
+ * @returns Promise resolving to an ApiResponse with typed data
1159
+ * @protected
1160
+ */
1161
+ async httpGet(url, headers) {
1162
+ return this.retryableRequest(this.baseUrl + url, {
1163
+ method: "GET",
1164
+ headers
1165
+ });
1166
+ }
1167
+ /**
1168
+ * Performs an HTTP POST request
1169
+ * @param url - The relative URL path to request (will be appended to baseUrl)
1170
+ * @param data - Optional request body data (will be JSON stringified)
1171
+ * @param headers - Optional HTTP headers to include in the request
1172
+ * @returns Promise resolving to an ApiResponse with typed data
1173
+ * @protected
1174
+ */
1175
+ async httpPost(url, data, headers) {
1176
+ return this.retryableRequest(this.baseUrl + url, {
1177
+ method: "POST",
1178
+ body: data ? JSON.stringify(data) : void 0,
1179
+ headers
1180
+ });
1181
+ }
1182
+ /**
1183
+ * Performs an HTTP PUT request
1184
+ * @param url - The relative URL path to request (will be appended to baseUrl)
1185
+ * @param data - Optional request body data (will be JSON stringified)
1186
+ * @param headers - Optional HTTP headers to include in the request
1187
+ * @returns Promise resolving to an ApiResponse with typed data
1188
+ * @protected
1189
+ */
1190
+ async httpPut(url, data, headers) {
1191
+ return this.retryableRequest(this.baseUrl + url, {
1192
+ method: "PUT",
1193
+ body: data ? JSON.stringify(data) : void 0,
1194
+ headers
1195
+ });
1196
+ }
1197
+ /**
1198
+ * Performs an HTTP DELETE request
1199
+ * @param url - The relative URL path to request (will be appended to baseUrl)
1200
+ * @param headers - Optional HTTP headers to include in the request
1201
+ * @returns Promise resolving to an ApiResponse with typed data
1202
+ * @protected
1203
+ */
1204
+ async httpDelete(url, headers) {
1205
+ return this.retryableRequest(this.baseUrl + url, {
1206
+ method: "DELETE",
1207
+ headers
1208
+ });
1209
+ }
1210
+ /**
1211
+ * Performs an HTTP PATCH request
1212
+ * @param url - The relative URL path to request (will be appended to baseUrl)
1213
+ * @param data - Optional request body data (will be JSON stringified)
1214
+ * @param headers - Optional HTTP headers to include in the request
1215
+ * @returns Promise resolving to an ApiResponse with typed data
1216
+ * @protected
1217
+ */
1218
+ async httpPatch(url, data, headers) {
1219
+ return this.retryableRequest(this.baseUrl + url, {
1220
+ method: "PATCH",
1221
+ body: data ? JSON.stringify(data) : void 0,
1222
+ headers
1223
+ });
1224
+ }
1225
+ };
1226
+ }
1227
+ });
1228
+
1229
+ // src/api/auth.api.service.ts
1230
+ var init_auth_api_service = __esm({
1231
+ "src/api/auth.api.service.ts"() {
1232
+ "use strict";
1233
+ init_http_client();
1234
+ }
1235
+ });
1236
+
1237
+ // src/services/auth.ts
1238
+ import "dotenv/config";
1239
+ import { readFileSync, writeFileSync, mkdirSync, unlinkSync } from "fs";
1240
+ function getToken() {
1241
+ if (process.env.LUA_API_KEY) {
1242
+ return process.env.LUA_API_KEY;
1243
+ }
1244
+ try {
1245
+ const token = readFileSync(CREDENTIALS_FILE, "utf8").trim();
1246
+ if (token) return token;
1247
+ } catch {
1248
+ }
1249
+ throw new AuthenticationError('No API key found.\n\n Authenticate using one of these methods:\n\n \u279C lua auth configure\n \u279C export LUA_API_KEY="your-api-key-here"\n \u279C Add LUA_API_KEY=... to a .env file\n\n \u{1F511} Get your API key at https://admin.heylua.ai', "invalid_credentials", void 0, true);
1250
+ }
1251
+ var init_auth = __esm({
1252
+ "src/services/auth.ts"() {
1253
+ "use strict";
1254
+ init_auth_api_service();
1255
+ init_constants();
1256
+ init_auth_error();
1257
+ __name(getToken, "getToken");
1258
+ }
1259
+ });
1260
+
1261
+ // src/config/compile.constants.ts
1262
+ var COMPILE_DIRS, COMPILE_FILES, SKILL_DEFAULTS, YAML_FORMAT;
1263
+ var init_compile_constants = __esm({
1264
+ "src/config/compile.constants.ts"() {
1265
+ "use strict";
1266
+ COMPILE_DIRS = {
1267
+ DIST: "dist",
1268
+ DIST_V2: "dist-v2",
1269
+ LUA: ".lua",
1270
+ TOOLS: "tools"
1271
+ };
1272
+ COMPILE_FILES = {
1273
+ DEPLOYMENT_JSON: "deployment.json",
1274
+ DEPLOY_JSON: "deploy.json",
1275
+ MANIFEST_JSON: "manifest.json",
1276
+ INDEX_TS: "index.ts",
1277
+ INDEX_JS: "index.js",
1278
+ PACKAGE_JSON: "package.json",
1279
+ TSCONFIG_JSON: "tsconfig.json",
1280
+ LUA_SKILL_YAML: "lua.skill.yaml"
1281
+ };
1282
+ SKILL_DEFAULTS = {
1283
+ NAME: "lua-skill",
1284
+ VERSION: "1.0.0",
1285
+ DESCRIPTION: "",
1286
+ CONTEXT: ""
1287
+ };
1288
+ YAML_FORMAT = {
1289
+ INDENT: 2,
1290
+ LINE_WIDTH: -1,
1291
+ NO_REFS: true
1292
+ };
847
1293
  }
848
1294
  });
849
1295
 
@@ -857,6 +1303,7 @@ var init_types = __esm({
857
1303
  PrimitiveKind2["SKILL"] = "skill";
858
1304
  PrimitiveKind2["JOB"] = "job";
859
1305
  PrimitiveKind2["WEBHOOK"] = "webhook";
1306
+ PrimitiveKind2["TRIGGER"] = "trigger";
860
1307
  PrimitiveKind2["PREPROCESSOR"] = "preprocessor";
861
1308
  PrimitiveKind2["POSTPROCESSOR"] = "postprocessor";
862
1309
  PrimitiveKind2["MCP_SERVER"] = "mcp-server";
@@ -1730,7 +2177,8 @@ var init_skill_handler = __esm({
1730
2177
  const response = await api.publishSkillVersion(entityId, version);
1731
2178
  return {
1732
2179
  success: response.success,
1733
- error: response.error?.message
2180
+ error: response.error?.message,
2181
+ agentVersion: response.data?.agentVersion
1734
2182
  };
1735
2183
  }
1736
2184
  prepareForPush(manifest, name, projectPath = process.cwd(), bundleAccumulator) {
@@ -4607,6 +5055,12 @@ var init_agents_api_service = __esm({
4607
5055
  } : {},
4608
5056
  ...body.threadId !== void 0 ? {
4609
5057
  threadId: body.threadId
5058
+ } : {},
5059
+ ...body.webhookPayload !== void 0 ? {
5060
+ webhookPayload: body.webhookPayload
5061
+ } : {},
5062
+ ...body.clientContext !== void 0 ? {
5063
+ clientContext: body.clientContext
4610
5064
  } : {}
4611
5065
  };
4612
5066
  }
@@ -4989,6 +5443,30 @@ var init_voice_api_service = __esm({
4989
5443
  });
4990
5444
  }
4991
5445
  /**
5446
+ * Create a voice room + client access token for a custom frontend
5447
+ * (standard livekit-client). The session runs under a synthetic identity;
5448
+ * pass `userId` to scope conversation memory + transcript to your own end
5449
+ * user. Wraps `POST /developer/voice/:agentId/session`.
5450
+ */
5451
+ async createSession(input = {}) {
5452
+ return this.httpPost(`/developer/voice/${this.agentId}/session`, input, {
5453
+ Authorization: `Bearer ${this.apiKey}`
5454
+ });
5455
+ }
5456
+ /**
5457
+ * Sandbox helper: throws on non-success and returns the unwrapped output.
5458
+ */
5459
+ async createSessionForSandbox(input = {}) {
5460
+ const result = await this.createSession(input);
5461
+ if (!result.success) {
5462
+ throw new Error(result.error?.message || "Voice session creation failed");
5463
+ }
5464
+ if (!result.data) {
5465
+ throw new Error("Voice session creation failed: empty response");
5466
+ }
5467
+ return result.data;
5468
+ }
5469
+ /**
4992
5470
  * Sandbox helper: throws on non-success and returns the unwrapped output.
4993
5471
  * Mirrors the shape `AgentsApiService.invokeForSandbox` exposes so the
4994
5472
  * `Voice` namespace in `api-exports.ts` stays a one-liner.
@@ -5007,6 +5485,134 @@ var init_voice_api_service = __esm({
5007
5485
  }
5008
5486
  });
5009
5487
 
5488
+ // src/api/channels-send.api.service.ts
5489
+ var ChannelsSendApiService;
5490
+ var init_channels_send_api_service = __esm({
5491
+ "src/api/channels-send.api.service.ts"() {
5492
+ "use strict";
5493
+ init_http_client();
5494
+ ChannelsSendApiService = class extends HttpClient {
5495
+ static {
5496
+ __name(this, "ChannelsSendApiService");
5497
+ }
5498
+ apiKey;
5499
+ agentId;
5500
+ constructor(baseUrl, apiKey, agentId) {
5501
+ super(baseUrl), this.apiKey = apiKey, this.agentId = agentId;
5502
+ }
5503
+ /** POST /developer/agents/:agentId/channels/send */
5504
+ async send(input) {
5505
+ return this.httpPost(`/developer/agents/${this.agentId}/channels/send`, input, {
5506
+ Authorization: `Bearer ${this.apiKey}`
5507
+ });
5508
+ }
5509
+ /** POST /developer/agents/:agentId/channels/whatsapp/template */
5510
+ async sendWhatsAppTemplate(input) {
5511
+ return this.httpPost(`/developer/agents/${this.agentId}/channels/whatsapp/template`, input, {
5512
+ Authorization: `Bearer ${this.apiKey}`
5513
+ });
5514
+ }
5515
+ /** POST /developer/agents/:agentId/channels/whatsapp/reaction */
5516
+ async sendWhatsAppReaction(input) {
5517
+ return this.httpPost(`/developer/agents/${this.agentId}/channels/whatsapp/reaction`, input, {
5518
+ Authorization: `Bearer ${this.apiKey}`
5519
+ });
5520
+ }
5521
+ /** POST /developer/agents/:agentId/channels/email/send */
5522
+ async sendEmail(input) {
5523
+ return this.httpPost(`/developer/agents/${this.agentId}/channels/email/send`, input, {
5524
+ Authorization: `Bearer ${this.apiKey}`
5525
+ });
5526
+ }
5527
+ /**
5528
+ * Sandbox helper: throws on non-success, returns unwrapped output.
5529
+ * A 200 with `persisted: false` is NOT an error — it passes through.
5530
+ */
5531
+ async sendForSandbox(input) {
5532
+ const result = await this.send(input);
5533
+ if (!result.success) {
5534
+ throw new Error(result.error?.message || "Channel send failed");
5535
+ }
5536
+ if (!result.data) {
5537
+ throw new Error("Channel send failed: empty response");
5538
+ }
5539
+ return result.data;
5540
+ }
5541
+ /** Sandbox helper for WhatsApp template sends. */
5542
+ async sendWhatsAppTemplateForSandbox(input) {
5543
+ const result = await this.sendWhatsAppTemplate(input);
5544
+ if (!result.success) {
5545
+ throw new Error(result.error?.message || "WhatsApp template send failed");
5546
+ }
5547
+ if (!result.data) {
5548
+ throw new Error("WhatsApp template send failed: empty response");
5549
+ }
5550
+ return result.data;
5551
+ }
5552
+ /** Sandbox helper for WhatsApp reaction sends. */
5553
+ async sendWhatsAppReactionForSandbox(input) {
5554
+ const result = await this.sendWhatsAppReaction(input);
5555
+ if (!result.success) {
5556
+ throw new Error(result.error?.message || "WhatsApp reaction send failed");
5557
+ }
5558
+ if (!result.data) {
5559
+ throw new Error("WhatsApp reaction send failed: empty response");
5560
+ }
5561
+ return result.data;
5562
+ }
5563
+ /** Sandbox helper for email sends. */
5564
+ async sendEmailForSandbox(input) {
5565
+ const result = await this.sendEmail(input);
5566
+ if (!result.success) {
5567
+ throw new Error(result.error?.message || "Email send failed");
5568
+ }
5569
+ if (!result.data) {
5570
+ throw new Error("Email send failed: empty response");
5571
+ }
5572
+ return result.data;
5573
+ }
5574
+ };
5575
+ }
5576
+ });
5577
+
5578
+ // src/api/directory.api.service.ts
5579
+ var DirectoryApiService;
5580
+ var init_directory_api_service = __esm({
5581
+ "src/api/directory.api.service.ts"() {
5582
+ "use strict";
5583
+ init_http_client();
5584
+ DirectoryApiService = class extends HttpClient {
5585
+ static {
5586
+ __name(this, "DirectoryApiService");
5587
+ }
5588
+ apiKey;
5589
+ agentId;
5590
+ constructor(baseUrl, apiKey, agentId) {
5591
+ super(baseUrl), this.apiKey = apiKey, this.agentId = agentId;
5592
+ }
5593
+ /** POST /developer/agents/:agentId/directory/resolve */
5594
+ async resolve(name) {
5595
+ return this.httpPost(`/developer/agents/${this.agentId}/directory/resolve`, {
5596
+ name
5597
+ }, {
5598
+ Authorization: `Bearer ${this.apiKey}`
5599
+ });
5600
+ }
5601
+ /** Sandbox helper: throws on non-success, returns unwrapped result. */
5602
+ async resolveForSandbox(name) {
5603
+ const result = await this.resolve(name);
5604
+ if (!result.success) {
5605
+ throw new Error(result.error?.message || "Directory resolve failed");
5606
+ }
5607
+ if (!result.data) {
5608
+ throw new Error("Directory resolve failed: empty response");
5609
+ }
5610
+ return result.data;
5611
+ }
5612
+ };
5613
+ }
5614
+ });
5615
+
5010
5616
  // src/api/device.api.service.ts
5011
5617
  var device_api_service_exports = {};
5012
5618
  __export(device_api_service_exports, {
@@ -5099,9 +5705,11 @@ __export(lazy_instances_exports, {
5099
5705
  getAiInstance: () => getAiInstance,
5100
5706
  getBasketsInstance: () => getBasketsInstance,
5101
5707
  getCdnInstance: () => getCdnInstance,
5708
+ getChannelsSendInstance: () => getChannelsSendInstance,
5102
5709
  getDataInstance: () => getDataInstance,
5103
5710
  getDeveloperInstance: () => getDeveloperInstance,
5104
5711
  getDeviceInstance: () => getDeviceInstance,
5712
+ getDirectoryInstance: () => getDirectoryInstance,
5105
5713
  getJobInstance: () => getJobInstance,
5106
5714
  getOrderInstance: () => getOrderInstance,
5107
5715
  getProductsInstance: () => getProductsInstance,
@@ -5209,6 +5817,20 @@ async function getVoiceInstance() {
5209
5817
  }
5210
5818
  return _voiceInstance;
5211
5819
  }
5820
+ async function getChannelsSendInstance() {
5821
+ if (!_channelsSendInstance) {
5822
+ const creds = await getCredentials();
5823
+ _channelsSendInstance = new ChannelsSendApiService(BASE_URLS.API, creds.apiKey, creds.agentId);
5824
+ }
5825
+ return _channelsSendInstance;
5826
+ }
5827
+ async function getDirectoryInstance() {
5828
+ if (!_directoryInstance) {
5829
+ const creds = await getCredentials();
5830
+ _directoryInstance = new DirectoryApiService(BASE_URLS.API, creds.apiKey, creds.agentId);
5831
+ }
5832
+ return _directoryInstance;
5833
+ }
5212
5834
  function clearAllInstances() {
5213
5835
  _userInstance = null;
5214
5836
  _dataInstance = null;
@@ -5223,8 +5845,10 @@ function clearAllInstances() {
5223
5845
  _cdnInstance = null;
5224
5846
  _developerInstance = null;
5225
5847
  _voiceInstance = null;
5848
+ _channelsSendInstance = null;
5849
+ _directoryInstance = null;
5226
5850
  }
5227
- var _userInstance, _dataInstance, _productsInstance, _basketsInstance, _orderInstance, _webhookInstance, _jobInstance, _aiInstance, _agentsInstance, _whatsAppTemplatesInstance, _cdnInstance, _developerInstance, _voiceInstance, _deviceInstance;
5851
+ var _userInstance, _dataInstance, _productsInstance, _basketsInstance, _orderInstance, _webhookInstance, _jobInstance, _aiInstance, _agentsInstance, _whatsAppTemplatesInstance, _cdnInstance, _developerInstance, _voiceInstance, _channelsSendInstance, _directoryInstance, _deviceInstance;
5228
5852
  var init_lazy_instances = __esm({
5229
5853
  "src/api/lazy-instances.ts"() {
5230
5854
  "use strict";
@@ -5243,6 +5867,8 @@ var init_lazy_instances = __esm({
5243
5867
  init_cdn_api_service();
5244
5868
  init_developer_api_service();
5245
5869
  init_voice_api_service();
5870
+ init_channels_send_api_service();
5871
+ init_directory_api_service();
5246
5872
  _userInstance = null;
5247
5873
  _dataInstance = null;
5248
5874
  _productsInstance = null;
@@ -5256,6 +5882,8 @@ var init_lazy_instances = __esm({
5256
5882
  _cdnInstance = null;
5257
5883
  _developerInstance = null;
5258
5884
  _voiceInstance = null;
5885
+ _channelsSendInstance = null;
5886
+ _directoryInstance = null;
5259
5887
  __name(getUserInstance, "getUserInstance");
5260
5888
  __name(getDataInstance, "getDataInstance");
5261
5889
  __name(getProductsInstance, "getProductsInstance");
@@ -5271,6 +5899,8 @@ var init_lazy_instances = __esm({
5271
5899
  __name(getDeviceInstance, "getDeviceInstance");
5272
5900
  __name(getDeveloperInstance, "getDeveloperInstance");
5273
5901
  __name(getVoiceInstance, "getVoiceInstance");
5902
+ __name(getChannelsSendInstance, "getChannelsSendInstance");
5903
+ __name(getDirectoryInstance, "getDirectoryInstance");
5274
5904
  __name(clearAllInstances, "clearAllInstances");
5275
5905
  }
5276
5906
  });
@@ -5289,6 +5919,7 @@ function assertValidToolName(name) {
5289
5919
  __name(assertValidToolName, "assertValidToolName");
5290
5920
 
5291
5921
  // src/types/skill.ts
5922
+ init_dist();
5292
5923
  var env = /* @__PURE__ */ __name((key) => {
5293
5924
  if (process.env[key]) {
5294
5925
  return process.env[key];
@@ -5557,6 +6188,39 @@ var LuaWebhook = class {
5557
6188
  return this.executeFunction(event);
5558
6189
  }
5559
6190
  };
6191
+ var LuaTrigger = class {
6192
+ static {
6193
+ __name(this, "LuaTrigger");
6194
+ }
6195
+ name;
6196
+ description;
6197
+ source;
6198
+ inputSchema;
6199
+ verify;
6200
+ filter;
6201
+ transform;
6202
+ constructor(config) {
6203
+ if (!config.name || !config.name.trim()) {
6204
+ throw new Error("LuaTrigger requires a non-empty `name` (used as the server-side identifier).");
6205
+ }
6206
+ if (!config.verify && !config.filter && !config.transform) {
6207
+ throw new Error("LuaTrigger requires at least one of verify, filter, or transform.");
6208
+ }
6209
+ this.name = config.name;
6210
+ this.description = config.description;
6211
+ this.source = config.source ?? "webhook";
6212
+ this.inputSchema = config.inputSchema;
6213
+ this.verify = config.verify;
6214
+ this.filter = config.filter;
6215
+ this.transform = config.transform;
6216
+ }
6217
+ getName() {
6218
+ return this.name;
6219
+ }
6220
+ getDescription() {
6221
+ return this.description;
6222
+ }
6223
+ };
5560
6224
  var PreProcessor = class {
5561
6225
  static {
5562
6226
  __name(this, "PreProcessor");
@@ -5700,6 +6364,18 @@ function validateModelSettings(settings) {
5700
6364
  throw new Error("Agent modelSettings.stopSequences must be a string array");
5701
6365
  }
5702
6366
  }
6367
+ if (settings.reasoning !== void 0) {
6368
+ if (typeof settings.reasoning !== "object" || settings.reasoning === null || Array.isArray(settings.reasoning)) {
6369
+ throw new Error("Agent modelSettings.reasoning must be an object");
6370
+ }
6371
+ const { effort, show } = settings.reasoning;
6372
+ if (effort !== void 0 && !REASONING_EFFORT_VALUES.includes(effort)) {
6373
+ throw new Error(`Agent modelSettings.reasoning.effort must be one of: ${REASONING_EFFORT_VALUES.join(", ")}`);
6374
+ }
6375
+ if (show !== void 0 && typeof show !== "boolean") {
6376
+ throw new Error("Agent modelSettings.reasoning.show must be a boolean");
6377
+ }
6378
+ }
5703
6379
  }
5704
6380
  __name(validateModelSettings, "validateModelSettings");
5705
6381
  var LuaAgent = class {
@@ -5712,6 +6388,7 @@ var LuaAgent = class {
5712
6388
  modelSettings;
5713
6389
  skills;
5714
6390
  webhooks;
6391
+ triggers;
5715
6392
  jobs;
5716
6393
  preProcessors;
5717
6394
  postProcessors;
@@ -5721,6 +6398,7 @@ var LuaAgent = class {
5721
6398
  voices;
5722
6399
  batching;
5723
6400
  governance;
6401
+ browser;
5724
6402
  /**
5725
6403
  * Creates a new LuaAgent instance.
5726
6404
  *
@@ -5752,6 +6430,7 @@ var LuaAgent = class {
5752
6430
  }
5753
6431
  this.skills = config.skills || [];
5754
6432
  this.webhooks = config.webhooks || [];
6433
+ this.triggers = config.triggers || [];
5755
6434
  this.jobs = config.jobs || [];
5756
6435
  this.preProcessors = config.preProcessors || [];
5757
6436
  this.postProcessors = config.postProcessors || [];
@@ -5761,10 +6440,15 @@ var LuaAgent = class {
5761
6440
  this.voices = config.voices;
5762
6441
  this.batching = config.batching;
5763
6442
  this.governance = config.governance;
6443
+ this.browser = config.browser;
5764
6444
  }
5765
6445
  getName() {
5766
6446
  return this.name;
5767
6447
  }
6448
+ /** Browser switch (LuaBrowser) — `true`/config when the agent can browse. */
6449
+ getBrowser() {
6450
+ return this.browser;
6451
+ }
5768
6452
  getPersona() {
5769
6453
  return this.persona;
5770
6454
  }
@@ -5780,6 +6464,9 @@ var LuaAgent = class {
5780
6464
  getWebhooks() {
5781
6465
  return this.webhooks;
5782
6466
  }
6467
+ getTriggers() {
6468
+ return this.triggers;
6469
+ }
5783
6470
  getJobs() {
5784
6471
  return this.jobs;
5785
6472
  }
@@ -5879,6 +6566,7 @@ var LuaVoice = class {
5879
6566
  preemptiveGeneration;
5880
6567
  interruption;
5881
6568
  sttLanguage;
6569
+ excludeTools;
5882
6570
  tools;
5883
6571
  onEnter;
5884
6572
  onUserTurnCompleted;
@@ -5901,6 +6589,7 @@ var LuaVoice = class {
5901
6589
  this.preemptiveGeneration = config.preemptiveGeneration;
5902
6590
  this.interruption = config.interruption;
5903
6591
  this.sttLanguage = config.sttLanguage;
6592
+ this.excludeTools = config.excludeTools;
5904
6593
  this.tools = Object.freeze([
5905
6594
  ...config.tools ?? []
5906
6595
  ]);
@@ -5915,6 +6604,7 @@ function defineVoice(config) {
5915
6604
  __name(defineVoice, "defineVoice");
5916
6605
 
5917
6606
  // src/api-exports.ts
6607
+ init_dist();
5918
6608
  init_baskets();
5919
6609
 
5920
6610
  // src/interfaces/orders.ts
@@ -6398,6 +7088,44 @@ var Voice = {
6398
7088
  const { getVoiceInstance: getVoiceInstance2 } = await Promise.resolve().then(() => (init_lazy_instances(), lazy_instances_exports));
6399
7089
  const voice = await getVoiceInstance2();
6400
7090
  return voice.dispatchForSandbox(input);
7091
+ },
7092
+ async createSession(input) {
7093
+ const { getVoiceInstance: getVoiceInstance2 } = await Promise.resolve().then(() => (init_lazy_instances(), lazy_instances_exports));
7094
+ const voice = await getVoiceInstance2();
7095
+ return voice.createSessionForSandbox(input ?? {});
7096
+ }
7097
+ };
7098
+ var Channels = {
7099
+ async send(input) {
7100
+ const { getChannelsSendInstance: getChannelsSendInstance2 } = await Promise.resolve().then(() => (init_lazy_instances(), lazy_instances_exports));
7101
+ const channels = await getChannelsSendInstance2();
7102
+ return channels.sendForSandbox(input);
7103
+ },
7104
+ whatsapp: {
7105
+ async sendTemplate(input) {
7106
+ const { getChannelsSendInstance: getChannelsSendInstance2 } = await Promise.resolve().then(() => (init_lazy_instances(), lazy_instances_exports));
7107
+ const channels = await getChannelsSendInstance2();
7108
+ return channels.sendWhatsAppTemplateForSandbox(input);
7109
+ },
7110
+ async sendReaction(input) {
7111
+ const { getChannelsSendInstance: getChannelsSendInstance2 } = await Promise.resolve().then(() => (init_lazy_instances(), lazy_instances_exports));
7112
+ const channels = await getChannelsSendInstance2();
7113
+ return channels.sendWhatsAppReactionForSandbox(input);
7114
+ }
7115
+ },
7116
+ email: {
7117
+ async send(input) {
7118
+ const { getChannelsSendInstance: getChannelsSendInstance2 } = await Promise.resolve().then(() => (init_lazy_instances(), lazy_instances_exports));
7119
+ const channels = await getChannelsSendInstance2();
7120
+ return channels.sendEmailForSandbox(input);
7121
+ }
7122
+ }
7123
+ };
7124
+ var Team = {
7125
+ async findMember(name) {
7126
+ const { getDirectoryInstance: getDirectoryInstance2 } = await Promise.resolve().then(() => (init_lazy_instances(), lazy_instances_exports));
7127
+ const directory = await getDirectoryInstance2();
7128
+ return directory.resolveForSandbox(name);
6401
7129
  }
6402
7130
  };
6403
7131
  var Templates = {
@@ -6531,6 +7259,10 @@ function defineDeviceTrigger(config) {
6531
7259
  return new LuaDeviceTrigger(config);
6532
7260
  }
6533
7261
  __name(defineDeviceTrigger, "defineDeviceTrigger");
7262
+ function defineTrigger(config) {
7263
+ return new LuaTrigger(config);
7264
+ }
7265
+ __name(defineTrigger, "defineTrigger");
6534
7266
  export {
6535
7267
  AI,
6536
7268
  Agents,
@@ -6538,6 +7270,8 @@ export {
6538
7270
  BasketStatus,
6539
7271
  Baskets,
6540
7272
  CDN,
7273
+ CHANNEL_SEND_CHANNELS,
7274
+ Channels,
6541
7275
  Data,
6542
7276
  DataEntryInstance,
6543
7277
  JobInstance,
@@ -6551,6 +7285,7 @@ export {
6551
7285
  PostProcessor as LuaPostprocessor,
6552
7286
  PreProcessor as LuaPreprocessor,
6553
7287
  LuaSkill,
7288
+ LuaTrigger,
6554
7289
  LuaVoice,
6555
7290
  LuaVoiceTool,
6556
7291
  LuaWebhook,
@@ -6561,6 +7296,7 @@ export {
6561
7296
  PreProcessor,
6562
7297
  ProductInstance,
6563
7298
  Products,
7299
+ Team,
6564
7300
  Templates,
6565
7301
  ToolFlag,
6566
7302
  User,
@@ -6568,6 +7304,7 @@ export {
6568
7304
  Voice,
6569
7305
  defineDevice,
6570
7306
  defineDeviceTrigger,
7307
+ defineTrigger,
6571
7308
  defineVoice,
6572
7309
  env
6573
7310
  };