@promptbook/cli 0.103.0-56 → 0.103.0-66

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 (50) hide show
  1. package/apps/agents-server/TODO.txt +5 -1
  2. package/apps/agents-server/package-lock.json +1220 -47
  3. package/apps/agents-server/package.json +4 -1
  4. package/apps/agents-server/src/app/actions.ts +3 -1
  5. package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +72 -6
  6. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +20 -7
  7. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +44 -0
  8. package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +7 -3
  9. package/apps/agents-server/src/app/agents/[agentName]/layout.tsx +41 -0
  10. package/apps/agents-server/src/app/agents/[agentName]/page.tsx +47 -100
  11. package/apps/agents-server/src/app/agents/[agentName]/website-integration/page.tsx +11 -2
  12. package/apps/agents-server/src/app/embed/page.tsx +2 -2
  13. package/apps/agents-server/src/app/layout.tsx +8 -24
  14. package/apps/agents-server/src/app/manifest.ts +8 -3
  15. package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +334 -0
  16. package/apps/agents-server/src/components/AgentProfile/AgentProfileFromSource.tsx +23 -0
  17. package/apps/agents-server/src/{app/agents/[agentName] → components/AgentProfile}/AgentQrCode.tsx +8 -1
  18. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +7 -6
  19. package/apps/agents-server/src/database/metadataDefaults.ts +6 -0
  20. package/esm/index.es.js +65 -10
  21. package/esm/index.es.js.map +1 -1
  22. package/esm/typings/src/_packages/components.index.d.ts +2 -2
  23. package/esm/typings/src/_packages/types.index.d.ts +6 -0
  24. package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +2 -1
  25. package/esm/typings/src/book-2.0/agent-source/createCommitmentRegex.d.ts +1 -1
  26. package/esm/typings/src/book-components/Chat/AgentChat/AgentChat.d.ts +3 -0
  27. package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +6 -0
  28. package/esm/typings/src/book-components/PromptbookAgent/PromptbookAgentIntegration.d.ts +52 -0
  29. package/esm/typings/src/book-components/PromptbookAgent/PromptbookAgentSeamlessIntegration.d.ts +14 -0
  30. package/esm/typings/src/book-components/icons/SendIcon.d.ts +3 -0
  31. package/esm/typings/src/commitments/CLOSED/CLOSED.d.ts +4 -0
  32. package/esm/typings/src/commitments/CLOSED/CLOSED.test.d.ts +4 -0
  33. package/esm/typings/src/commitments/USE_BROWSER/USE_BROWSER.d.ts +4 -0
  34. package/esm/typings/src/commitments/_base/BaseCommitmentDefinition.d.ts +6 -0
  35. package/esm/typings/src/llm-providers/agent/Agent.d.ts +3 -1
  36. package/esm/typings/src/other/templates/getTemplatesPipelineCollection.d.ts +1 -1
  37. package/esm/typings/src/types/typeAliases.d.ts +6 -0
  38. package/esm/typings/src/utils/color/Color.d.ts +1 -1
  39. package/esm/typings/src/utils/random/$generateBookBoilerplate.d.ts +6 -0
  40. package/esm/typings/src/utils/random/CzechNamePool.d.ts +7 -0
  41. package/esm/typings/src/utils/random/EnglishNamePool.d.ts +7 -0
  42. package/esm/typings/src/utils/random/NamePool.d.ts +17 -0
  43. package/esm/typings/src/utils/random/getNamePool.d.ts +10 -0
  44. package/esm/typings/src/version.d.ts +1 -1
  45. package/package.json +2 -2
  46. package/umd/index.umd.js +65 -10
  47. package/umd/index.umd.js.map +1 -1
  48. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileView.tsx +0 -233
  49. package/esm/typings/src/book-components/PromptbookAgent/PromptbookAgent.d.ts +0 -29
  50. /package/apps/agents-server/src/{app/agents/[agentName] → components/AgentProfile}/QrCodeModal.tsx +0 -0
@@ -0,0 +1,334 @@
1
+ 'use client';
2
+
3
+ import { AgentBasicInformation } from '@promptbook-local/types';
4
+ import { RepeatIcon } from 'lucide-react';
5
+ import { useMemo, useState } from 'react';
6
+ import spaceTrim from 'spacetrim';
7
+ import { Color } from '../../../../../src/utils/color/Color';
8
+ import { darken } from '../../../../../src/utils/color/operators/darken';
9
+ import { lighten } from '../../../../../src/utils/color/operators/lighten';
10
+ import { AgentQrCode } from './AgentQrCode';
11
+ import { QrCodeModal } from './QrCodeModal';
12
+
13
+ type AgentProfileProps = {
14
+ /**
15
+ * The agent to display
16
+ */
17
+ readonly agent: AgentBasicInformation;
18
+
19
+ /**
20
+ * URL of the agent page
21
+ *
22
+ * @default undefined - If not provided, some features like QR code for link might be disabled or use generic link
23
+ */
24
+ readonly agentUrl?: string;
25
+
26
+ /**
27
+ * Email of the agent
28
+ */
29
+ readonly agentEmail?: string;
30
+
31
+ /**
32
+ * Content for the menu (top right)
33
+ *
34
+ * @param props.onShowQrCode - Function to open QR code modal
35
+ */
36
+ readonly renderMenu?: (props: { onShowQrCode: () => void }) => React.ReactNode;
37
+
38
+ /**
39
+ * Content for the chat area
40
+ */
41
+ readonly children?: React.ReactNode;
42
+
43
+ /**
44
+ * Content for the secondary actions (links)
45
+ */
46
+ readonly actions?: React.ReactNode;
47
+
48
+ /**
49
+ * If true, hides the menu and actions for fullscreen/embedded view
50
+ */
51
+ readonly isHeadless?: boolean;
52
+
53
+ /**
54
+ * CSS class name
55
+ */
56
+ readonly className?: string;
57
+ };
58
+
59
+ export function AgentProfile(props: AgentProfileProps) {
60
+ const {
61
+ agent,
62
+ agentUrl = '',
63
+ agentEmail = '',
64
+ renderMenu,
65
+ children,
66
+ actions,
67
+ isHeadless = false,
68
+ className,
69
+ } = props;
70
+ const { meta, agentName } = agent;
71
+ const fullname = (meta.fullname as string) || agentName || 'Agent';
72
+ const personaDescription = agent.personaDescription || '';
73
+ const imageUrl = (meta.image as string) || null;
74
+
75
+ const [isQrModalOpen, setIsQrModalOpen] = useState(false);
76
+ const [isFlipped, setIsFlipped] = useState(false);
77
+
78
+ // Dynamic Font Loading
79
+ const fontString = meta.font;
80
+ let fontStyle: React.CSSProperties = {};
81
+
82
+ if (fontString) {
83
+ // [🧠] TODO: Properly parse font string to get family name
84
+ const primaryFont = fontString.split(',')[0].trim().replace(/['"]/g, '');
85
+ fontStyle = {
86
+ fontFamily: fontString,
87
+ };
88
+ }
89
+
90
+ // Compute Colors and Background
91
+ const { brandColorHex, brandColorLightHex, brandColorDarkHex, backgroundImage } = useMemo(() => {
92
+ // [🧠] Default color should be imported constant, but for now hardcoded fallback
93
+ const PROMPTBOOK_COLOR_HEX = '#f15b24'; // TODO: Import PROMPTBOOK_COLOR
94
+ const brandColorString = meta.color || PROMPTBOOK_COLOR_HEX;
95
+
96
+ let brandColor;
97
+ try {
98
+ brandColor = Color.fromSafe(brandColorString.split(',')[0].trim());
99
+ } catch {
100
+ brandColor = Color.fromHex(PROMPTBOOK_COLOR_HEX);
101
+ }
102
+
103
+ const brandColorHex = brandColor.toHex();
104
+ const brandColorLightHex = brandColor.then(lighten(0.2)).toHex();
105
+ const brandColorDarkHex = brandColor.then(darken(0.15)).toHex();
106
+
107
+ // Generate Noisy SVG Background
108
+ const color1 = brandColor;
109
+ // const color2 = brandColors[1] || brandColors[0]!; // Use secondary color if available?
110
+ // For simplicity using primary color for now or derive second one
111
+ const color2 = brandColor;
112
+
113
+ // [🧠] Make colors much lighter for the background
114
+ const color1Light = color1.then(lighten(0.3)).toHex();
115
+ const color1Main = color1.toHex();
116
+ const color1Dark = color1.then(darken(0.3)).toHex();
117
+
118
+ const color2Light = color2.then(lighten(0.3)).toHex();
119
+ const color2Main = color2.toHex();
120
+ const color2Dark = color2.then(darken(0.3)).toHex();
121
+
122
+ const svgContent = spaceTrim(`
123
+ <svg xmlns="http://www.w3.org/2000/svg"
124
+ viewBox="0 0 1920 1080"
125
+ width="1920" height="1080"
126
+ preserveAspectRatio="xMidYMid slice">
127
+ <defs>
128
+ <!-- Bottom-left -->
129
+ <radialGradient id="grad1" cx="0%" cy="100%" r="90%">
130
+ <stop offset="0%" stop-color="${color1Light}" />
131
+ <stop offset="50%" stop-color="${color1Main}" />
132
+ <stop offset="100%" stop-color="${color1Dark}" />
133
+ </radialGradient>
134
+
135
+ <!-- Bottom-right -->
136
+ <radialGradient id="grad2" cx="100%" cy="100%" r="90%">
137
+ <stop offset="0%" stop-color="${color2Light}" />
138
+ <stop offset="50%" stop-color="${color2Main}" />
139
+ <stop offset="100%" stop-color="${color2Dark}" />
140
+ </radialGradient>
141
+
142
+ <!-- White top fade -->
143
+ <linearGradient id="whiteTopGrad" x1="0%" y1="0%" x2="0%" y2="100%">
144
+ <stop offset="0%" stop-color="#ffffff" stop-opacity="1" />
145
+ <stop offset="100%" stop-color="#ffffff" stop-opacity="0.3" />
146
+ </linearGradient>
147
+
148
+ <!-- Strong grain -->
149
+ <filter id="grain" x="-10%" y="-10%" width="120%" height="120%">
150
+ <feTurbulence type="fractalNoise" baseFrequency="3.5" numOctaves="3" seed="8" result="noise" />
151
+ <feComponentTransfer>
152
+ <feFuncR type="linear" slope="3.5" intercept="-1.2" />
153
+ <feFuncG type="linear" slope="3.5" intercept="-1.2" />
154
+ <feFuncB type="linear" slope="3.5" intercept="-1.2" />
155
+ <feFuncA type="table" tableValues="0 0.8" />
156
+ </feComponentTransfer>
157
+ </filter>
158
+ </defs>
159
+
160
+ <!-- White base -->
161
+ <rect width="100%" height="100%" fill="#ffffff" />
162
+
163
+ <!-- Gradients -->
164
+ <rect width="100%" height="100%" fill="url(#grad1)" />
165
+ <rect width="100%" height="100%" fill="url(#grad2)" style="mix-blend-mode:screen; opacity:0.85" />
166
+
167
+ <!-- White fade on top -->
168
+ <rect width="100%" height="100%" fill="url(#whiteTopGrad)" />
169
+
170
+ <!-- Strong visible noise -->
171
+ <rect width="100%" height="100%" filter="url(#grain)"
172
+ style="mix-blend-mode:soft-light; opacity:1.2" />
173
+ </svg>
174
+ `);
175
+
176
+ const backgroundImage = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgContent)}`;
177
+
178
+ return { brandColorHex, brandColorLightHex, brandColorDarkHex, backgroundImage };
179
+ }, [meta.color]);
180
+
181
+ return (
182
+ <>
183
+ {fontString && (
184
+ <style jsx global>{`
185
+ @import url('https://fonts.googleapis.com/css2?family=${encodeURIComponent(
186
+ fontString.split(',')[0].trim().replace(/['"]/g, ''),
187
+ )}:wght@400;600;700&display=swap');
188
+ `}</style>
189
+ )}
190
+
191
+ {/* Full-screen background with agent color */}
192
+ <div
193
+ className={`w-full flex flex-col items-center justify-center p-6 md:p-12 relative overflow-hidden ${
194
+ isHeadless ? 'min-h-screen' : 'min-h-[calc(100vh-60px)]'
195
+ } ${className || ''}`}
196
+ style={{
197
+ background: `url("${backgroundImage}")`,
198
+ backgroundSize: 'cover',
199
+ backgroundPosition: 'center',
200
+ ...fontStyle,
201
+ }}
202
+ >
203
+ {/* Options menu in top right */}
204
+ {!isHeadless && renderMenu && (
205
+ <div className="absolute top-4 right-4 z-[9999]">
206
+ {renderMenu({ onShowQrCode: () => setIsQrModalOpen(true) })}
207
+ </div>
208
+ )}
209
+
210
+ {/* Main profile content */}
211
+ <div className="relative z-10 flex flex-col md:flex-row items-center md:items-start gap-8 md:gap-12 max-w-5xl w-full">
212
+ {/* Agent image card (Flippable) */}
213
+ <div className="flex-shrink-0 perspective-1000 group" style={{ perspective: '1000px' }}>
214
+ <div
215
+ className="relative w-72 md:w-80 transition-all duration-700 transform-style-3d cursor-pointer"
216
+ style={{
217
+ aspectRatio: '1 / 1.618', // Golden Ratio
218
+ transformStyle: 'preserve-3d',
219
+ transform: isFlipped ? 'rotateY(180deg)' : 'rotateY(0deg)',
220
+ }}
221
+ onClick={() => setIsFlipped(!isFlipped)}
222
+ >
223
+ {/* Front of Card (Image) */}
224
+ <div
225
+ className="absolute inset-0 w-full h-full backface-hidden rounded-3xl shadow-2xl overflow-hidden backdrop-blur-sm"
226
+ style={{
227
+ backfaceVisibility: 'hidden',
228
+ backgroundColor: brandColorDarkHex,
229
+ boxShadow: `0 25px 50px -12px rgba(0, 0, 0, 0.25), 0 0 0 1px ${brandColorLightHex}40`,
230
+ }}
231
+ >
232
+ {imageUrl ? (
233
+ // eslint-disable-next-line @next/next/no-img-element
234
+ <img src={imageUrl} alt={fullname} className="w-full h-full object-cover" />
235
+ ) : (
236
+ <div
237
+ className="w-full h-full flex items-center justify-center text-8xl font-bold text-white/80"
238
+ style={{ backgroundColor: brandColorDarkHex }}
239
+ >
240
+ {fullname.charAt(0).toUpperCase()}
241
+ </div>
242
+ )}
243
+
244
+ {/* Flip hint icon */}
245
+ <div className="absolute bottom-4 right-4 bg-black/30 p-2 rounded-full text-white/80 backdrop-blur-md opacity-0 group-hover:opacity-100 transition-opacity">
246
+ <RepeatIcon className="w-5 h-5" />
247
+ </div>
248
+ </div>
249
+
250
+ {/* Back of Card (QR Code) */}
251
+ <div
252
+ className="absolute inset-0 w-full h-full backface-hidden rounded-3xl shadow-2xl overflow-hidden backdrop-blur-sm flex flex-col items-center justify-center p-6"
253
+ style={{
254
+ backfaceVisibility: 'hidden',
255
+ transform: 'rotateY(180deg)',
256
+ background: `linear-gradient(135deg, ${brandColorLightHex} 0%, #ffffff 100%)`,
257
+ boxShadow: `0 25px 50px -12px rgba(0, 0, 0, 0.25), 0 0 0 1px ${brandColorLightHex}40`,
258
+ }}
259
+ >
260
+ <div className="transform scale-90 md:scale-100">
261
+ <AgentQrCode
262
+ agentName={agentName}
263
+ agentUrl={agentUrl}
264
+ agentEmail={agentEmail}
265
+ personaDescription={personaDescription}
266
+ meta={meta}
267
+ isJustVcardShown
268
+ />
269
+ </div>
270
+
271
+ {/* Flip hint icon */}
272
+ <div className="absolute bottom-4 right-4 bg-black/10 p-2 rounded-full text-black/50 backdrop-blur-md">
273
+ <RepeatIcon className="w-5 h-5" />
274
+ </div>
275
+ </div>
276
+ </div>
277
+ </div>
278
+
279
+ {/* Agent info */}
280
+ <div className="flex flex-col items-center md:items-start text-center md:text-left gap-6">
281
+ {/* Agent name with custom font */}
282
+ <h1
283
+ className="text-4xl md:text-5xl lg:text-6xl font-bold text-gray-900 tracking-tight"
284
+ style={{
285
+ textShadow: '0 2px 20px rgba(255, 255, 255, 0.5)',
286
+ }}
287
+ >
288
+ {fullname}
289
+ </h1>
290
+
291
+ {/* Short description */}
292
+ <p className="text-lg md:text-xl text-gray-700 max-w-lg leading-relaxed font-medium">
293
+ {personaDescription}
294
+ </p>
295
+
296
+ {/* Chat */}
297
+ <div className="w-full">{children}</div>
298
+
299
+ {/* Secondary Actions */}
300
+ {!isHeadless && (
301
+ <div className="flex flex-wrap justify-center md:justify-start items-center gap-4 md:gap-6 mt-2">
302
+ {actions}
303
+ </div>
304
+ )}
305
+ </div>
306
+ </div>
307
+
308
+ {/* Subtle gradient overlay at bottom */}
309
+ <div
310
+ className="absolute bottom-0 left-0 right-0 h-32 pointer-events-none"
311
+ style={{
312
+ background: `linear-gradient(to top, ${brandColorDarkHex}40, transparent)`,
313
+ }}
314
+ />
315
+ </div>
316
+
317
+ {/* QR Code Modal */}
318
+ <QrCodeModal
319
+ isOpen={isQrModalOpen}
320
+ onClose={() => setIsQrModalOpen(false)}
321
+ agentName={agentName}
322
+ meta={meta}
323
+ personaDescription={personaDescription}
324
+ agentUrl={agentUrl}
325
+ agentEmail={agentEmail}
326
+ brandColorHex={brandColorHex}
327
+ />
328
+ </>
329
+ );
330
+ }
331
+
332
+ /**
333
+ * TODO: !!!! Use 3D badge @see https://vercel.com/blog/building-an-interactive-3d-event-badge-with-react-three-fiber
334
+ */
@@ -0,0 +1,23 @@
1
+ 'use client';
2
+
3
+ import { string_book } from '@promptbook-local/types';
4
+ import { useMemo } from 'react';
5
+ import { parseAgentSource } from '../../../../../src/book-2.0/agent-source/parseAgentSource';
6
+ import { AgentProfile } from './AgentProfile';
7
+
8
+ type AgentProfileFromSourceProps = Omit<React.ComponentProps<typeof AgentProfile>, 'agent'> & {
9
+ /**
10
+ * Source code of the agent (book)
11
+ */
12
+ readonly source: string_book;
13
+ };
14
+
15
+ export function AgentProfileFromSource(props: AgentProfileFromSourceProps) {
16
+ const { source, ...rest } = props;
17
+
18
+ const agent = useMemo(() => {
19
+ return parseAgentSource(source);
20
+ }, [source]);
21
+
22
+ return <AgentProfile agent={agent} {...rest} />;
23
+ }
@@ -8,9 +8,12 @@ import spaceTrim from 'spacetrim';
8
8
  type AgentQrCodeProps = Pick<AgentBasicInformation, 'agentName' | 'personaDescription' | 'meta'> & {
9
9
  agentUrl: string;
10
10
  agentEmail: string;
11
+
12
+ isJustVcardShown?: boolean;
11
13
  };
12
14
 
13
- export function AgentQrCode({ agentName, agentUrl, agentEmail, personaDescription, meta }: AgentQrCodeProps) {
15
+ export function AgentQrCode(props: AgentQrCodeProps) {
16
+ const { agentName, agentUrl, agentEmail, personaDescription, meta, isJustVcardShown } = props;
14
17
  const [mode, setMode] = useState<'contact' | 'link'>('contact');
15
18
 
16
19
  // TODO: [🧠] Should we include more info in VCARD?
@@ -27,6 +30,10 @@ export function AgentQrCode({ agentName, agentUrl, agentEmail, personaDescriptio
27
30
  const qrValue = mode === 'contact' ? vcard : agentUrl;
28
31
  const label = mode === 'contact' ? 'Scan to add contact' : 'Scan to open agent';
29
32
 
33
+ if (isJustVcardShown) {
34
+ return <PromptbookQrCode value={vcard} className="" size={250} />;
35
+ }
36
+
30
37
  return (
31
38
  <div className="flex flex-col items-center">
32
39
  <div className="flex bg-gray-100 p-1 rounded-lg mb-4">
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { usePathname } from 'next/navigation';
3
+ import { usePathname, useSearchParams } from 'next/navigation';
4
4
  import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
5
5
  import type { UserInfo } from '../../utils/getCurrentUser';
6
6
  import { Footer, type FooterLink } from '../Footer/Footer';
@@ -28,12 +28,13 @@ export function LayoutWrapper({
28
28
  footerLinks,
29
29
  }: LayoutWrapperProps) {
30
30
  const pathname = usePathname();
31
- const isAdminChatPage =
32
- pathname?.startsWith('/admin/chat-history') || pathname?.startsWith('/admin/chat-feedback');
33
- const isHeaderHidden = pathname?.includes('/chat') && !isAdminChatPage;
34
- const isFooterHiddenOnPage = pathname ? /^\/agents\/[^/]+\/book(\+chat)?$/.test(pathname) : false;
31
+ const searchParams = useSearchParams();
32
+ const isHeadless = searchParams.has('headless');
33
+ // const isAdminChatPage = pathname?.startsWith('/admin/chat-history') || pathname?.startsWith('/admin/chat-feedback');
34
+ const isHeaderHidden = false; // pathname?.includes('/chat') && !isAdminChatPage;
35
+ const isFooterHiddenOnPage = pathname ? /^\/agents\/[^/]+\/(book|chat|book\+chat)$/.test(pathname) : false;
35
36
 
36
- if (isHeaderHidden) {
37
+ if (isHeaderHidden || isHeadless) {
37
38
  return <main className={`pt-0`}>{children}</main>;
38
39
  }
39
40
 
@@ -61,6 +61,12 @@ export const metadataDefaults = [
61
61
  note: 'Maximum size of file that can be uploaded in MB.',
62
62
  type: 'NUMBER',
63
63
  },
64
+ {
65
+ key: 'NAME_POOL',
66
+ value: 'ENGLISH',
67
+ note: 'Language for generating new agent names. Possible values: ENGLISH, CZECH.',
68
+ type: 'TEXT_SINGLE_LINE',
69
+ },
64
70
  ] as const satisfies ReadonlyArray<{
65
71
  key: string;
66
72
  value: string;
package/esm/index.es.js CHANGED
@@ -47,7 +47,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
47
47
  * @generated
48
48
  * @see https://github.com/webgptorg/promptbook
49
49
  */
50
- const PROMPTBOOK_ENGINE_VERSION = '0.103.0-56';
50
+ const PROMPTBOOK_ENGINE_VERSION = '0.103.0-66';
51
51
  /**
52
52
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
53
53
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -374,15 +374,33 @@ class Color {
374
374
  * @param color
375
375
  * @returns Color object
376
376
  */
377
- static from(color) {
378
- if (color instanceof Color) {
377
+ static from(color, _isSingleValue = false) {
378
+ if (color === '') {
379
+ throw new Error(`Can not create color from empty string`);
380
+ }
381
+ else if (color instanceof Color) {
379
382
  return take(color);
380
383
  }
381
384
  else if (Color.isColor(color)) {
382
385
  return take(color);
383
386
  }
384
387
  else if (typeof color === 'string') {
385
- return Color.fromString(color);
388
+ try {
389
+ return Color.fromString(color);
390
+ }
391
+ catch (error) {
392
+ // <- Note: Can not use `assertsError(error)` here because it causes circular dependency
393
+ if (_isSingleValue) {
394
+ throw error;
395
+ }
396
+ const parts = color.split(/[\s+,;|]/);
397
+ if (parts.length > 0) {
398
+ return Color.from(parts[0].trim(), true);
399
+ }
400
+ else {
401
+ throw new Error(`Can not create color from given string "${color}"`);
402
+ }
403
+ }
386
404
  }
387
405
  else {
388
406
  console.error({ color });
@@ -22454,15 +22472,19 @@ const _FormattedBookInMarkdownTranspilerRegistration = $bookTranspilersRegister.
22454
22472
  *
22455
22473
  * @private - TODO: [🧠] Maybe should be public?
22456
22474
  */
22457
- function createCommitmentRegex(commitment, aliases = []) {
22475
+ function createCommitmentRegex(commitment, aliases = [], requiresContent = true) {
22458
22476
  const allCommitments = [commitment, ...aliases];
22459
22477
  const patterns = allCommitments.map((c) => {
22460
22478
  const escapedCommitment = c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
22461
22479
  return escapedCommitment.split(/\s+/).join('\\s+');
22462
22480
  });
22463
22481
  const keywordPattern = patterns.join('|');
22464
- const regex = new RegExp(`^\\s*(?<type>${keywordPattern})\\b\\s+(?<contents>.+)$`, 'gim');
22465
- return regex;
22482
+ if (requiresContent) {
22483
+ return new RegExp(`^\\s*(?<type>${keywordPattern})\\b\\s+(?<contents>.+)$`, 'gim');
22484
+ }
22485
+ else {
22486
+ return new RegExp(`^\\s*(?<type>${keywordPattern})\\b(?:\\s+(?<contents>.+))?$`, 'gim');
22487
+ }
22466
22488
  }
22467
22489
  /**
22468
22490
  * Generates a regex pattern to match a specific commitment type
@@ -22495,12 +22517,20 @@ class BaseCommitmentDefinition {
22495
22517
  this.type = type;
22496
22518
  this.aliases = aliases;
22497
22519
  }
22520
+ /**
22521
+ * Whether this commitment requires content.
22522
+ * If true, regex will match only if there is content after the commitment keyword.
22523
+ * If false, regex will match even if there is no content.
22524
+ */
22525
+ get requiresContent() {
22526
+ return true;
22527
+ }
22498
22528
  /**
22499
22529
  * Creates a regex pattern to match this commitment in agent source
22500
22530
  * Uses the existing createCommitmentRegex function as internal helper
22501
22531
  */
22502
22532
  createRegex() {
22503
- return createCommitmentRegex(this.type, this.aliases);
22533
+ return createCommitmentRegex(this.type, this.aliases, this.requiresContent);
22504
22534
  }
22505
22535
  /**
22506
22536
  * Creates a regex pattern to match just the commitment type
@@ -22652,6 +22682,12 @@ class ClosedCommitmentDefinition extends BaseCommitmentDefinition {
22652
22682
  constructor() {
22653
22683
  super('CLOSED');
22654
22684
  }
22685
+ /**
22686
+ * The `CLOSED` commitment is standalone.
22687
+ */
22688
+ get requiresContent() {
22689
+ return false;
22690
+ }
22655
22691
  /**
22656
22692
  * Short one-line description of CLOSED.
22657
22693
  */
@@ -25331,6 +25367,12 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
25331
25367
  constructor() {
25332
25368
  super('USE BROWSER', ['BROWSER']);
25333
25369
  }
25370
+ /**
25371
+ * The `USE BROWSER` commitment is standalone.
25372
+ */
25373
+ get requiresContent() {
25374
+ return false;
25375
+ }
25334
25376
  /**
25335
25377
  * Short one-line description of USE BROWSER.
25336
25378
  */
@@ -26171,11 +26213,11 @@ function parseAgentSource(agentSource) {
26171
26213
  continue;
26172
26214
  }
26173
26215
  if (commitment.type === 'META COLOR') {
26174
- meta.color = spaceTrim$2(commitment.content);
26216
+ meta.color = normalizeSeparator(commitment.content);
26175
26217
  continue;
26176
26218
  }
26177
26219
  if (commitment.type === 'META FONT') {
26178
- meta.font = spaceTrim$2(commitment.content);
26220
+ meta.font = normalizeSeparator(commitment.content);
26179
26221
  continue;
26180
26222
  }
26181
26223
  if (commitment.type !== 'META') {
@@ -26211,6 +26253,19 @@ function parseAgentSource(agentSource) {
26211
26253
  parameters,
26212
26254
  };
26213
26255
  }
26256
+ /**
26257
+ * Normalizes the separator in the content
26258
+ *
26259
+ * @param content - The content to normalize
26260
+ * @returns The content with normalized separators
26261
+ */
26262
+ function normalizeSeparator(content) {
26263
+ const trimmed = spaceTrim$2(content);
26264
+ if (trimmed.includes(',')) {
26265
+ return trimmed;
26266
+ }
26267
+ return trimmed.split(/\s+/).join(', ');
26268
+ }
26214
26269
  /**
26215
26270
  * TODO: [🕛] Unite `AgentBasicInformation`, `ChatParticipant`, `LlmExecutionTools` + `LlmToolsMetadata`
26216
26271
  */