lua-cli 3.14.1 → 3.15.1

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 (164) hide show
  1. package/dist/api/agent.api.service.d.ts +12 -0
  2. package/dist/api/agent.api.service.d.ts.map +1 -1
  3. package/dist/api/agent.api.service.js +13 -0
  4. package/dist/api/agent.api.service.js.map +1 -1
  5. package/dist/api/http.client.d.ts.map +1 -1
  6. package/dist/api/http.client.js +31 -4
  7. package/dist/api/http.client.js.map +1 -1
  8. package/dist/api/persona.api.service.d.ts +3 -2
  9. package/dist/api/persona.api.service.d.ts.map +1 -1
  10. package/dist/api/persona.api.service.js +1 -1
  11. package/dist/api/persona.api.service.js.map +1 -1
  12. package/dist/api/postprocessor.api.service.d.ts +3 -0
  13. package/dist/api/postprocessor.api.service.d.ts.map +1 -1
  14. package/dist/api/postprocessor.api.service.js.map +1 -1
  15. package/dist/api/preprocessor.api.service.d.ts +4 -0
  16. package/dist/api/preprocessor.api.service.d.ts.map +1 -1
  17. package/dist/api/preprocessor.api.service.js +1 -0
  18. package/dist/api/preprocessor.api.service.js.map +1 -1
  19. package/dist/api-exports.d.ts +2 -1
  20. package/dist/api-exports.d.ts.map +1 -1
  21. package/dist/api-exports.js.map +1 -1
  22. package/dist/cli/command-definitions.d.ts.map +1 -1
  23. package/dist/cli/command-definitions.js +56 -0
  24. package/dist/cli/command-definitions.js.map +1 -1
  25. package/dist/commands/chat.d.ts.map +1 -1
  26. package/dist/commands/chat.js.map +1 -1
  27. package/dist/commands/init.d.ts +6 -0
  28. package/dist/commands/init.d.ts.map +1 -1
  29. package/dist/commands/init.js +325 -85
  30. package/dist/commands/init.js.map +1 -1
  31. package/dist/commands/persona.d.ts.map +1 -1
  32. package/dist/commands/persona.js +3 -2
  33. package/dist/commands/persona.js.map +1 -1
  34. package/dist/commands/production.d.ts.map +1 -1
  35. package/dist/commands/production.js +8 -2
  36. package/dist/commands/production.js.map +1 -1
  37. package/dist/commands/push.d.ts.map +1 -1
  38. package/dist/commands/push.js +94 -14
  39. package/dist/commands/push.js.map +1 -1
  40. package/dist/commands/pushBackup.d.ts +58 -4
  41. package/dist/commands/pushBackup.d.ts.map +1 -1
  42. package/dist/commands/pushBackup.js +202 -125
  43. package/dist/commands/pushBackup.js.map +1 -1
  44. package/dist/commands/source.d.ts +72 -0
  45. package/dist/commands/source.d.ts.map +1 -0
  46. package/dist/commands/source.js +124 -0
  47. package/dist/commands/source.js.map +1 -0
  48. package/dist/commands/sync.d.ts.map +1 -1
  49. package/dist/commands/sync.js +3 -2
  50. package/dist/commands/sync.js.map +1 -1
  51. package/dist/compiler/plugins/agent.plugin.d.ts.map +1 -1
  52. package/dist/compiler/plugins/agent.plugin.js +10 -5
  53. package/dist/compiler/plugins/agent.plugin.js.map +1 -1
  54. package/dist/compiler/plugins/postprocessor.plugin.d.ts.map +1 -1
  55. package/dist/compiler/plugins/postprocessor.plugin.js +8 -2
  56. package/dist/compiler/plugins/postprocessor.plugin.js.map +1 -1
  57. package/dist/compiler/plugins/preprocessor.plugin.d.ts.map +1 -1
  58. package/dist/compiler/plugins/preprocessor.plugin.js +6 -3
  59. package/dist/compiler/plugins/preprocessor.plugin.js.map +1 -1
  60. package/dist/compiler/plugins/skill.plugin.d.ts.map +1 -1
  61. package/dist/compiler/plugins/skill.plugin.js +6 -2
  62. package/dist/compiler/plugins/skill.plugin.js.map +1 -1
  63. package/dist/compiler/source-writer.d.ts +13 -8
  64. package/dist/compiler/source-writer.d.ts.map +1 -1
  65. package/dist/compiler/source-writer.js +24 -12
  66. package/dist/compiler/source-writer.js.map +1 -1
  67. package/dist/compiler/types.d.ts +11 -5
  68. package/dist/compiler/types.d.ts.map +1 -1
  69. package/dist/compiler/types.js.map +1 -1
  70. package/dist/compiler/utils/ast-helpers.d.ts +11 -0
  71. package/dist/compiler/utils/ast-helpers.d.ts.map +1 -1
  72. package/dist/compiler/utils/ast-helpers.js +27 -0
  73. package/dist/compiler/utils/ast-helpers.js.map +1 -1
  74. package/dist/errors/auth.error.d.ts +19 -1
  75. package/dist/errors/auth.error.d.ts.map +1 -1
  76. package/dist/errors/auth.error.js +4 -1
  77. package/dist/errors/auth.error.js.map +1 -1
  78. package/dist/instances/user.instance.d.ts +2 -1
  79. package/dist/instances/user.instance.d.ts.map +1 -1
  80. package/dist/instances/user.instance.js.map +1 -1
  81. package/dist/interfaces/agent.d.ts +34 -4
  82. package/dist/interfaces/agent.d.ts.map +1 -1
  83. package/dist/interfaces/backup.d.ts +24 -0
  84. package/dist/interfaces/backup.d.ts.map +1 -1
  85. package/dist/interfaces/chat.d.ts +3 -22
  86. package/dist/interfaces/chat.d.ts.map +1 -1
  87. package/dist/interfaces/init.d.ts +3 -2
  88. package/dist/interfaces/init.d.ts.map +1 -1
  89. package/dist/interfaces/persona.d.ts +2 -1
  90. package/dist/interfaces/persona.d.ts.map +1 -1
  91. package/dist/interfaces/skills.d.ts +4 -3
  92. package/dist/interfaces/skills.d.ts.map +1 -1
  93. package/dist/primitives/agent.handler.d.ts +3 -3
  94. package/dist/primitives/agent.handler.d.ts.map +1 -1
  95. package/dist/primitives/agent.handler.js +15 -1
  96. package/dist/primitives/agent.handler.js.map +1 -1
  97. package/dist/primitives/postprocessor.handler.d.ts +5 -0
  98. package/dist/primitives/postprocessor.handler.d.ts.map +1 -1
  99. package/dist/primitives/postprocessor.handler.js +13 -0
  100. package/dist/primitives/postprocessor.handler.js.map +1 -1
  101. package/dist/primitives/preprocessor.handler.d.ts +2 -2
  102. package/dist/primitives/preprocessor.handler.d.ts.map +1 -1
  103. package/dist/primitives/preprocessor.handler.js +3 -2
  104. package/dist/primitives/preprocessor.handler.js.map +1 -1
  105. package/dist/primitives/skill.handler.d.ts.map +1 -1
  106. package/dist/primitives/skill.handler.js +8 -2
  107. package/dist/primitives/skill.handler.js.map +1 -1
  108. package/dist/services/auth.d.ts.map +1 -1
  109. package/dist/services/auth.js +4 -1
  110. package/dist/services/auth.js.map +1 -1
  111. package/dist/services/sandbox.service.d.ts +2 -1
  112. package/dist/services/sandbox.service.d.ts.map +1 -1
  113. package/dist/services/sandbox.service.js.map +1 -1
  114. package/dist/types/api-contracts.d.ts +4 -3
  115. package/dist/types/api-contracts.d.ts.map +1 -1
  116. package/dist/types/skill.d.ts +12 -3
  117. package/dist/types/skill.d.ts.map +1 -1
  118. package/dist/types/skill.js +17 -0
  119. package/dist/types/skill.js.map +1 -1
  120. package/dist/types/yaml.types.d.ts +6 -0
  121. package/dist/types/yaml.types.d.ts.map +1 -1
  122. package/dist/utils/artifact-loader.d.ts +2 -1
  123. package/dist/utils/artifact-loader.d.ts.map +1 -1
  124. package/dist/utils/artifact-loader.js.map +1 -1
  125. package/dist/utils/backup-helpers.d.ts.map +1 -1
  126. package/dist/utils/backup-helpers.js +12 -2
  127. package/dist/utils/backup-helpers.js.map +1 -1
  128. package/dist/utils/build-manifest-from-disk.d.ts +17 -0
  129. package/dist/utils/build-manifest-from-disk.d.ts.map +1 -0
  130. package/dist/utils/build-manifest-from-disk.js +111 -0
  131. package/dist/utils/build-manifest-from-disk.js.map +1 -0
  132. package/dist/utils/cli.d.ts.map +1 -1
  133. package/dist/utils/cli.js +34 -1
  134. package/dist/utils/cli.js.map +1 -1
  135. package/dist/utils/init-agent.d.ts +2 -1
  136. package/dist/utils/init-agent.d.ts.map +1 -1
  137. package/dist/utils/init-agent.js.map +1 -1
  138. package/dist/utils/init-helpers.d.ts +2 -1
  139. package/dist/utils/init-helpers.d.ts.map +1 -1
  140. package/dist/utils/init-helpers.js +3 -1
  141. package/dist/utils/init-helpers.js.map +1 -1
  142. package/dist/utils/init-prompts.d.ts +20 -1
  143. package/dist/utils/init-prompts.d.ts.map +1 -1
  144. package/dist/utils/init-prompts.js +88 -0
  145. package/dist/utils/init-prompts.js.map +1 -1
  146. package/dist/utils/sync-display.d.ts +2 -1
  147. package/dist/utils/sync-display.d.ts.map +1 -1
  148. package/dist/utils/sync-display.js +5 -1
  149. package/dist/utils/sync-display.js.map +1 -1
  150. package/dist/utils/sync-fetch.d.ts +6 -4
  151. package/dist/utils/sync-fetch.d.ts.map +1 -1
  152. package/dist/utils/sync-fetch.js +31 -1
  153. package/dist/utils/sync-fetch.js.map +1 -1
  154. package/docs/api/LuaAgent.md +21 -3
  155. package/docs/api/LuaSkill.md +22 -4
  156. package/node_modules/@lua/shared-types/dist/index.d.mts +120 -1
  157. package/node_modules/@lua/shared-types/dist/index.d.ts +120 -1
  158. package/node_modules/@lua/shared-types/dist/index.js +137 -2
  159. package/node_modules/@lua/shared-types/dist/index.mjs +129 -1
  160. package/node_modules/@lua/shared-types/src/chat-history.types.ts +112 -0
  161. package/node_modules/@lua/shared-types/src/index.ts +2 -0
  162. package/node_modules/@lua/shared-types/src/persona-text.type.ts +92 -0
  163. package/package.json +1 -1
  164. package/template/package.json +1 -1
@@ -1,6 +1,59 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
+ // src/persona-text.type.ts
5
+ function isPersonaTextObject(value) {
6
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
7
+ const obj = value;
8
+ for (const k of Object.keys(obj)) {
9
+ if (k !== "base" && k !== "voice" && k !== "text") return false;
10
+ if (obj[k] !== void 0 && typeof obj[k] !== "string") return false;
11
+ }
12
+ return true;
13
+ }
14
+ __name(isPersonaTextObject, "isPersonaTextObject");
15
+ function flattenPersonaText(input, isVoice = false) {
16
+ if (input == null) return "";
17
+ if (typeof input === "string") return input;
18
+ const parts = [];
19
+ if (input.base) parts.push(input.base);
20
+ const channelText = isVoice ? input.voice : input.text;
21
+ if (channelText) parts.push(channelText);
22
+ return parts.join("\n\n");
23
+ }
24
+ __name(flattenPersonaText, "flattenPersonaText");
25
+ function flattenPersonaTextAll(input) {
26
+ if (input == null) return "";
27
+ if (typeof input === "string") return input;
28
+ const parts = [];
29
+ if (input.base) parts.push(input.base);
30
+ if (input.voice) parts.push(input.voice);
31
+ if (input.text) parts.push(input.text);
32
+ return parts.join("\n\n");
33
+ }
34
+ __name(flattenPersonaTextAll, "flattenPersonaTextAll");
35
+ function hasPersonaTextContent(input) {
36
+ if (input == null) return false;
37
+ if (typeof input === "string") return input.trim().length > 0;
38
+ return Boolean(input.base?.trim() || input.voice?.trim() || input.text?.trim());
39
+ }
40
+ __name(hasPersonaTextContent, "hasPersonaTextContent");
41
+ function personaToLiteral(persona) {
42
+ const formatValue = /* @__PURE__ */ __name((v) => {
43
+ if (v.includes("\n")) {
44
+ return "`" + v.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$") + "`";
45
+ }
46
+ return '"' + v.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
47
+ }, "formatValue");
48
+ if (typeof persona === "string") return formatValue(persona);
49
+ const parts = [];
50
+ if (persona.base !== void 0) parts.push(`base: ${formatValue(persona.base)}`);
51
+ if (persona.voice !== void 0) parts.push(`voice: ${formatValue(persona.voice)}`);
52
+ if (persona.text !== void 0) parts.push(`text: ${formatValue(persona.text)}`);
53
+ return `{ ${parts.join(", ")} }`;
54
+ }
55
+ __name(personaToLiteral, "personaToLiteral");
56
+
4
57
  // src/unstructured-types.ts
5
58
  var UNSTRUCTURED_SUPPORTED_MEDIA_TYPES = /* @__PURE__ */ new Set([
6
59
  // PDF
@@ -74,6 +127,74 @@ function aiGenerateInputFromSimplified(prompt, content) {
74
127
  }
75
128
  __name(aiGenerateInputFromSimplified, "aiGenerateInputFromSimplified");
76
129
 
130
+ // src/chat-history.types.ts
131
+ function removeNavigateBlock(input) {
132
+ return input.replace(/::: navigate[\s\S]*?:::/g, "").trim();
133
+ }
134
+ __name(removeNavigateBlock, "removeNavigateBlock");
135
+ function transformChatHistoryContentParts(parts) {
136
+ const content = [];
137
+ for (const rawPart of parts ?? []) {
138
+ const part = rawPart;
139
+ if (part?.type !== "text" && part?.type !== "file") continue;
140
+ if (part.type === "text" && typeof part.text === "string") {
141
+ const rawText = part.text || "";
142
+ if (rawText.includes("::: hide")) continue;
143
+ const audioMatch = rawText.match(/::: audio\s*!\[(.*?)\]\((.*?)\)\s*:::/);
144
+ const videoMatch = rawText.match(/::: video\s*!\[(.*?)\]\((.*?)\)\s*:::/);
145
+ if (audioMatch) {
146
+ content.push({
147
+ type: "audio",
148
+ data: audioMatch[2],
149
+ mediaType: audioMatch[1]
150
+ });
151
+ } else if (videoMatch) {
152
+ content.push({
153
+ type: "video",
154
+ video: videoMatch[2],
155
+ mediaType: videoMatch[1]
156
+ });
157
+ } else {
158
+ let text = rawText.replace(/\\\\\\n/g, "\n");
159
+ text = removeNavigateBlock(text);
160
+ content.push({
161
+ type: "text",
162
+ text
163
+ });
164
+ }
165
+ } else if (part.type === "file") {
166
+ const mediaType = part.mimeType || "";
167
+ if (mediaType.startsWith("image/")) {
168
+ content.push({
169
+ type: "image",
170
+ image: part.data,
171
+ mediaType
172
+ });
173
+ } else if (mediaType.startsWith("video/")) {
174
+ content.push({
175
+ type: "video",
176
+ video: part.data,
177
+ mediaType
178
+ });
179
+ } else if (mediaType.startsWith("audio/")) {
180
+ content.push({
181
+ type: "audio",
182
+ data: part.data,
183
+ mediaType
184
+ });
185
+ } else {
186
+ content.push({
187
+ type: "file",
188
+ data: part.data,
189
+ mediaType
190
+ });
191
+ }
192
+ }
193
+ }
194
+ return content;
195
+ }
196
+ __name(transformChatHistoryContentParts, "transformChatHistoryContentParts");
197
+
77
198
  // src/sandbox-contract.ts
78
199
  var REQUIRED_PRIMITIVE_SHIMS = [
79
200
  // Class-based
@@ -262,5 +383,12 @@ export {
262
383
  REQUIRED_UTILITIES,
263
384
  UNSTRUCTURED_SUPPORTED_MEDIA_TYPES,
264
385
  aiGenerateInputFromSimplified,
265
- buildDefaultPersona
386
+ buildDefaultPersona,
387
+ flattenPersonaText,
388
+ flattenPersonaTextAll,
389
+ hasPersonaTextContent,
390
+ isPersonaTextObject,
391
+ personaToLiteral,
392
+ removeNavigateBlock,
393
+ transformChatHistoryContentParts
266
394
  };
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Wire-format types for `GET /chat/history/:agentId` and the VM-sandbox
3
+ * `User.getChatHistory()` API. Both paths return the same shape.
4
+ *
5
+ * Shared between lua-core (HTTP controller + VM devtools) and lua-cli
6
+ * (HTTP client typing for `lua test`). Also shared with lua-api's
7
+ * `thread.service.ts` (admin org-thread listing) so the parser never
8
+ * diverges across services.
9
+ */
10
+
11
+ /**
12
+ * One content part of a chat-history message.
13
+ *
14
+ * `text` parts carry plain text. Media parts (`image` / `video` / `audio`
15
+ * / `file`) carry a URL or base64 payload in `data` (or `image` / `video`
16
+ * for legacy reasons) plus a `mediaType` MIME string.
17
+ */
18
+ export interface ChatHistoryContent {
19
+ type: 'text' | 'image' | 'video' | 'audio' | 'file';
20
+ text?: string;
21
+ image?: string;
22
+ video?: string;
23
+ data?: string;
24
+ mediaType?: string;
25
+ latitude?: number;
26
+ longitude?: number;
27
+ }
28
+
29
+ /**
30
+ * One message returned by chat-history endpoints.
31
+ *
32
+ * Only `user` and `assistant` roles surface — system / tool messages are
33
+ * filtered out before transport.
34
+ */
35
+ export interface ChatHistoryMessage {
36
+ id: string;
37
+ role: 'user' | 'assistant';
38
+ /** ISO-8601 timestamp string. */
39
+ createdAt: string;
40
+ content: ChatHistoryContent[];
41
+ }
42
+
43
+ /**
44
+ * Strip `::: navigate …:::` blocks from a text string.
45
+ *
46
+ * Exposed because both lua-core's chat-history transform and any future
47
+ * external transform may need it independently.
48
+ */
49
+ export function removeNavigateBlock(input: string): string {
50
+ return input.replace(/::: navigate[\s\S]*?:::/g, '').trim();
51
+ }
52
+
53
+ /**
54
+ * Transform raw Mastra `MastraMessageContentV2.parts[]` into the
55
+ * `ChatHistoryContent[]` wire shape.
56
+ *
57
+ * Single source of truth for the conversion: used by lua-core's
58
+ * `toChatHistoryMessage` (HTTP + VM sandbox paths) and lua-api's
59
+ * `thread.service.ts` admin org-thread listing. Any future divergence
60
+ * starts here.
61
+ *
62
+ * Parts are deliberately typed `unknown` because callers may either pass
63
+ * Mastra v5 `MastraMessagePart` instances (lua-core, via `@mastra/core`)
64
+ * or raw JSON parsed from a SQL row (lua-api, reading `mastra_messages`
65
+ * directly). The function only inspects `.type`, `.text`, `.data`, and
66
+ * `.mimeType` via property access, so both shapes work.
67
+ */
68
+ export function transformChatHistoryContentParts(parts: unknown[] | undefined | null): ChatHistoryContent[] {
69
+ const content: ChatHistoryContent[] = [];
70
+
71
+ for (const rawPart of parts ?? []) {
72
+ const part = rawPart as { type?: string; text?: string; data?: string; mimeType?: string };
73
+ if (part?.type !== 'text' && part?.type !== 'file') continue;
74
+
75
+ if (part.type === 'text' && typeof part.text === 'string') {
76
+ const rawText = part.text || '';
77
+
78
+ // Filter out hidden text
79
+ if (rawText.includes('::: hide')) continue;
80
+
81
+ // Check for audio/video markdown patterns and surface as native
82
+ // audio/video parts (matches the ChatHistoryContent type contract).
83
+ const audioMatch = rawText.match(/::: audio\s*!\[(.*?)\]\((.*?)\)\s*:::/);
84
+ const videoMatch = rawText.match(/::: video\s*!\[(.*?)\]\((.*?)\)\s*:::/);
85
+
86
+ if (audioMatch) {
87
+ content.push({ type: 'audio', data: audioMatch[2], mediaType: audioMatch[1] });
88
+ } else if (videoMatch) {
89
+ content.push({ type: 'video', video: videoMatch[2], mediaType: videoMatch[1] });
90
+ } else {
91
+ // Cleanup text: de-escape \\\\\\n and strip navigate blocks
92
+ let text = rawText.replace(/\\\\\\n/g, '\n');
93
+ text = removeNavigateBlock(text);
94
+ content.push({ type: 'text', text });
95
+ }
96
+ } else if (part.type === 'file') {
97
+ const mediaType = part.mimeType || '';
98
+
99
+ if (mediaType.startsWith('image/')) {
100
+ content.push({ type: 'image', image: part.data, mediaType });
101
+ } else if (mediaType.startsWith('video/')) {
102
+ content.push({ type: 'video', video: part.data, mediaType });
103
+ } else if (mediaType.startsWith('audio/')) {
104
+ content.push({ type: 'audio', data: part.data, mediaType });
105
+ } else {
106
+ content.push({ type: 'file', data: part.data, mediaType });
107
+ }
108
+ }
109
+ }
110
+
111
+ return content;
112
+ }
@@ -1,8 +1,10 @@
1
1
  export * from './channel.type';
2
+ export * from './persona-text.type';
2
3
  export * from './unstructured-types';
3
4
  export * from './ai-generate.types';
4
5
  export * from './ai-generate.utils';
5
6
  export * from './agent-invocation.types';
7
+ export * from './chat-history.types';
6
8
  export * from './sandbox-contract';
7
9
  export * from './device.types';
8
10
  export * from './client-tools.types';
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Polymorphic shape used by `LuaAgent.persona`. String form is the legacy
3
+ * single-channel persona; object form lets the customer split prose per
4
+ * channel (`base` always rendered; `voice` appended on voice; `text`
5
+ * appended on text channels).
6
+ */
7
+ export type PersonaText = string | { base?: string; voice?: string; text?: string };
8
+
9
+ /**
10
+ * Same shape as `PersonaText`, exported separately because skill `context`
11
+ * and agent `persona` play different semantic roles. Sharing the structural
12
+ * shape today keeps the runtime lean; future divergence is a one-line change.
13
+ */
14
+ export type SkillContextText = string | { base?: string; voice?: string; text?: string };
15
+
16
+ export function isPersonaTextObject(value: unknown): value is { base?: string; voice?: string; text?: string } {
17
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) return false;
18
+ const obj = value as Record<string, unknown>;
19
+ for (const k of Object.keys(obj)) {
20
+ if (k !== 'base' && k !== 'voice' && k !== 'text') return false;
21
+ if (obj[k] !== undefined && typeof obj[k] !== 'string') return false;
22
+ }
23
+ return true;
24
+ }
25
+
26
+ /**
27
+ * Flatten `PersonaText` or `SkillContextText` to a plain string suitable for
28
+ * LLM prompt injection. `base` is always emitted; `voice` is appended on
29
+ * voice channels and `text` on text channels. Strict — missing channel keys
30
+ * render nothing (no cross-channel fallback).
31
+ */
32
+ export function flattenPersonaText(input: PersonaText | SkillContextText | null | undefined, isVoice = false): string {
33
+ if (input == null) return '';
34
+ if (typeof input === 'string') return input;
35
+ const parts: string[] = [];
36
+ if (input.base) parts.push(input.base);
37
+ const channelText = isVoice ? input.voice : input.text;
38
+ if (channelText) parts.push(channelText);
39
+ return parts.join('\n\n');
40
+ }
41
+
42
+ /**
43
+ * Channel-agnostic flatten — concatenates every non-empty channel's content
44
+ * (base + voice + text) for paths that aren't a render-time prompt injection,
45
+ * e.g. AI persona enhancement (storytelling, instructions, website-derived).
46
+ * Use this whenever the call has no channel context but still wants the full
47
+ * persona content — `flattenPersonaText` would silently drop voice or text
48
+ * branches because it defaults to text-channel rendering.
49
+ */
50
+ export function flattenPersonaTextAll(input: PersonaText | SkillContextText | null | undefined): string {
51
+ if (input == null) return '';
52
+ if (typeof input === 'string') return input;
53
+ const parts: string[] = [];
54
+ if (input.base) parts.push(input.base);
55
+ if (input.voice) parts.push(input.voice);
56
+ if (input.text) parts.push(input.text);
57
+ return parts.join('\n\n');
58
+ }
59
+
60
+ /**
61
+ * True when the persona has ANY non-empty content on any channel. Used at
62
+ * push/skip boundaries where channel context is not yet known.
63
+ */
64
+ export function hasPersonaTextContent(input: PersonaText | SkillContextText | null | undefined): boolean {
65
+ if (input == null) return false;
66
+ if (typeof input === 'string') return input.trim().length > 0;
67
+ return Boolean(input.base?.trim() || input.voice?.trim() || input.text?.trim());
68
+ }
69
+
70
+ /**
71
+ * Convert a PersonaText into a TypeScript source-code literal suitable
72
+ * for ts-morph setInitializer or template rendering.
73
+ *
74
+ * - String form → `"..."` (double-quoted) or template literal (when newlines present)
75
+ * - Object form → `{ base: ..., voice: ..., text: ... }` with same per-key rule
76
+ */
77
+ export function personaToLiteral(persona: PersonaText): string {
78
+ const formatValue = (v: string): string => {
79
+ if (v.includes('\n')) {
80
+ return '`' + v.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$/g, '\\$') + '`';
81
+ }
82
+ return '"' + v.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
83
+ };
84
+
85
+ if (typeof persona === 'string') return formatValue(persona);
86
+
87
+ const parts: string[] = [];
88
+ if (persona.base !== undefined) parts.push(`base: ${formatValue(persona.base)}`);
89
+ if (persona.voice !== undefined) parts.push(`voice: ${formatValue(persona.voice)}`);
90
+ if (persona.text !== undefined) parts.push(`text: ${formatValue(persona.text)}`);
91
+ return `{ ${parts.join(', ')} }`;
92
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lua-cli",
3
- "version": "3.14.1",
3
+ "version": "3.15.1",
4
4
  "description": "Build, test, and deploy AI agents with custom tools, webhooks, and scheduled jobs. Features LuaAgent unified configuration, streaming chat, and batch deployment.",
5
5
  "readmeFilename": "README.md",
6
6
  "main": "dist/api-exports.js",
@@ -20,7 +20,7 @@
20
20
  "inquirer": "^12.9.6",
21
21
  "stripe": "^17.5.0",
22
22
  "js-yaml": "^4.1.0",
23
- "lua-cli": "^3.14.0",
23
+ "lua-cli": "^3.15.0",
24
24
  "openai": "^5.23.0",
25
25
  "uuid": "^13.0.0",
26
26
  "zod": "^3.24.1"