@promptbook/components 0.112.0-58 → 0.112.0-60

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.
package/esm/index.es.js CHANGED
@@ -40,7 +40,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
40
40
  * @generated
41
41
  * @see https://github.com/webgptorg/promptbook
42
42
  */
43
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-58';
43
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-60';
44
44
  /**
45
45
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
46
46
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -9530,6 +9530,49 @@ class BaseCommitmentDefinition {
9530
9530
  return this.appendToSystemMessage(requirements, commentSection);
9531
9531
  }
9532
9532
  }
9533
+ /**
9534
+ * Helper method to append a bullet point to an existing `## SectionTitle` section in the system
9535
+ * message, or to create a new section when it does not yet exist.
9536
+ *
9537
+ * Handles the case where the same commitment type appears multiple times in the book source and
9538
+ * all entries should be grouped under one shared heading rather than emitting a duplicate block.
9539
+ *
9540
+ * @param requirements - Current model requirements.
9541
+ * @param sectionTitle - Section title without the `##` prefix.
9542
+ * @param bulletContent - Bullet content without the leading `- ` prefix.
9543
+ * @returns Requirements with the bullet appended to the section.
9544
+ */
9545
+ appendBulletPointToSection(requirements, sectionTitle, bulletContent) {
9546
+ const sectionHeader = `## ${sectionTitle}`;
9547
+ const bullet = `- ${bulletContent}`;
9548
+ if (requirements.systemMessage.includes(sectionHeader)) {
9549
+ // Append bullet to end of existing section, before the next h2 heading or end of message
9550
+ const newSystemMessage = requirements.systemMessage.replace(new RegExp(`(## ${sectionTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\n\\n)([\\s\\S]*?)(?=\\n\\n##|$)`), `$1$2\n${bullet}`);
9551
+ return { ...requirements, systemMessage: newSystemMessage };
9552
+ }
9553
+ return this.appendToSystemMessage(requirements, `${sectionHeader}\n\n${bullet}`, '\n\n');
9554
+ }
9555
+ /**
9556
+ * Helper method to replace an existing `## SectionTitle` section in the system message, or to
9557
+ * append a new one when the section does not yet exist.
9558
+ *
9559
+ * Use this when a commitment type can appear multiple times and each subsequent occurrence should
9560
+ * update the single shared section rather than appending a duplicate block.
9561
+ *
9562
+ * @param requirements - Current model requirements.
9563
+ * @param sectionTitle - Section title without the `##` prefix.
9564
+ * @param sectionContent - Full section content including the `## Title` header line.
9565
+ * @returns Requirements with the section replaced or appended.
9566
+ */
9567
+ replaceOrCreateSection(requirements, sectionTitle, sectionContent) {
9568
+ const sectionHeader = `## ${sectionTitle}`;
9569
+ if (requirements.systemMessage.includes(sectionHeader)) {
9570
+ // Replace all text from the heading until the next h2 heading or end of message
9571
+ const newSystemMessage = requirements.systemMessage.replace(new RegExp(`## ${sectionTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?(?=\\n\\n##|$)`), sectionContent);
9572
+ return { ...requirements, systemMessage: newSystemMessage };
9573
+ }
9574
+ return this.appendToSystemMessage(requirements, sectionContent, '\n\n');
9575
+ }
9533
9576
  /**
9534
9577
  * Gets tool function implementations provided by this commitment
9535
9578
  *
@@ -10009,20 +10052,16 @@ class DictionaryCommitmentDefinition extends BaseCommitmentDefinition {
10009
10052
  if (!trimmedContent) {
10010
10053
  return requirements;
10011
10054
  }
10012
- // Get existing dictionary entries from metadata
10055
+ // Store the entry in metadata for debugging and inspection
10013
10056
  const existingDictionary = ((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.DICTIONARY) || '';
10014
- // Merge the new dictionary entry with existing entries
10015
10057
  const mergedDictionary = existingDictionary ? `${existingDictionary}\n${trimmedContent}` : trimmedContent;
10016
- // Store the merged dictionary in metadata for debugging and inspection
10017
10058
  const updatedMetadata = {
10018
10059
  ...requirements._metadata,
10019
10060
  DICTIONARY: mergedDictionary,
10020
10061
  };
10021
- // Create the dictionary section for the system message
10022
- // Format: "# DICTIONARY\nTerm: definition\nTerm: definition..."
10023
- const dictionarySection = `# DICTIONARY\n${mergedDictionary}`;
10062
+ // Append each dictionary entry as a bullet point under ## Dictionary
10024
10063
  return {
10025
- ...this.appendToSystemMessage(requirements, dictionarySection),
10064
+ ...this.appendBulletPointToSection(requirements, 'Dictionary', trimmedContent),
10026
10065
  _metadata: updatedMetadata,
10027
10066
  };
10028
10067
  }
@@ -10536,10 +10575,10 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
10536
10575
  if (!trimmedContent) {
10537
10576
  return requirements;
10538
10577
  }
10539
- // Add goal to the system message
10540
- const goalSection = `Goal: ${trimmedContent}`;
10578
+ // Add goal as a proper h2 section to the system message
10579
+ const goalSection = `## Goal\n\n${trimmedContent}`;
10541
10580
  const requirementsWithGoal = this.appendToSystemMessage(requirements, goalSection, '\n\n');
10542
- return this.appendToPromptSuffix(requirementsWithGoal, goalSection);
10581
+ return this.appendToPromptSuffix(requirementsWithGoal, trimmedContent);
10543
10582
  }
10544
10583
  }
10545
10584
  // Note: [💞] Ignore a discrepancy between file name and entity name
@@ -11059,11 +11098,8 @@ class LanguageCommitmentDefinition extends BaseCommitmentDefinition {
11059
11098
  if (!trimmedContent) {
11060
11099
  return requirements;
11061
11100
  }
11062
- // Add language rule to the system message
11063
- const languageSection = this.createSystemMessageSection('Language:', spaceTrim$1((block) => `
11064
- ${block(trimmedContent)}
11065
- <- You are speaking these languages in your responses to the user.
11066
- `));
11101
+ // Add language as a bullet under a ## Language section
11102
+ const languageSection = `## Language\n\n- Your language is ${trimmedContent}`;
11067
11103
  return this.appendToSystemMessage(requirements, languageSection, '\n\n');
11068
11104
  }
11069
11105
  }
@@ -11107,15 +11143,16 @@ const MemoryToolNames = {
11107
11143
  */
11108
11144
  function createMemorySystemMessage(extraInstructions) {
11109
11145
  return spaceTrim$1((block) => `
11110
- Memory:
11111
- - Prefer storing agent-scoped memories; only make them global when the fact should apply across all your agents.
11112
- - You can use persistent user memory tools.
11113
- - Use "${MemoryToolNames.retrieve}" to load relevant memory before answering.
11114
- - Use "${MemoryToolNames.store}" to save stable user-specific facts that improve future help.
11115
- - Use "${MemoryToolNames.update}" to refresh an existing memory when the content changes.
11116
- - Use "${MemoryToolNames.delete}" to delete memories that are no longer accurate (deletions are soft and hidden from future queries).
11117
- - Store concise memory items and avoid duplicates.
11118
- - Never claim memory was saved or loaded unless the tool confirms it.
11146
+ ## Memory
11147
+
11148
+ - Prefer storing agent-scoped memories; only make them global when the fact should apply across all your agents.
11149
+ - You can use persistent user memory tools.
11150
+ - Use \`${MemoryToolNames.retrieve}\` to load relevant memory before answering.
11151
+ - Use \`${MemoryToolNames.store}\` to save stable user-specific facts that improve future help.
11152
+ - Use \`${MemoryToolNames.update}\` to refresh an existing memory when the content changes.
11153
+ - Use \`${MemoryToolNames.delete}\` to delete memories that are no longer accurate (deletions are soft and hidden from future queries).
11154
+ - Store concise memory items and avoid duplicates.
11155
+ - Never claim memory was saved or loaded unless the tool confirms it.
11119
11156
  ${block(extraInstructions)}
11120
11157
  `);
11121
11158
  }
@@ -12037,10 +12074,8 @@ class MessageCommitmentDefinition extends BaseCommitmentDefinition {
12037
12074
  if (!trimmedContent) {
12038
12075
  return requirements;
12039
12076
  }
12040
- // Create message section for system message
12041
- const messageSection = `Previous Message: ${trimmedContent}`;
12042
12077
  // Messages represent conversation history and should be included for context
12043
- return this.appendToSystemMessage(requirements, messageSection, '\n\n');
12078
+ return this.appendBulletPointToSection(requirements, 'Previous messages', trimmedContent);
12044
12079
  }
12045
12080
  }
12046
12081
  // Note: [💞] Ignore a discrepancy between file name and entity name
@@ -13677,10 +13712,9 @@ class RuleCommitmentDefinition extends BaseCommitmentDefinition {
13677
13712
  if (!trimmedContent) {
13678
13713
  return requirements;
13679
13714
  }
13680
- // Add rule to the system message
13681
- const ruleSection = `Rule: ${trimmedContent}`;
13682
- const requirementsWithRule = this.appendToSystemMessage(requirements, ruleSection, '\n\n');
13683
- return this.appendToPromptSuffix(requirementsWithRule, ruleSection);
13715
+ // Group all rules under a single ## Rules section as bullet points
13716
+ const requirementsWithRule = this.appendBulletPointToSection(requirements, 'Rules', trimmedContent);
13717
+ return this.appendToPromptSuffix(requirementsWithRule, `- ${trimmedContent}`);
13684
13718
  }
13685
13719
  }
13686
13720
  // Note: [💞] Ignore a discrepancy between file name and entity name
@@ -13916,10 +13950,8 @@ class ScenarioCommitmentDefinition extends BaseCommitmentDefinition {
13916
13950
  if (!trimmedContent) {
13917
13951
  return requirements;
13918
13952
  }
13919
- // Create scenario section for system message
13920
- const scenarioSection = `Scenario: ${trimmedContent}`;
13921
13953
  // Scenarios provide important contextual information that affects behavior
13922
- return this.appendToSystemMessage(requirements, scenarioSection, '\n\n');
13954
+ return this.appendBulletPointToSection(requirements, 'Scenarios', trimmedContent);
13923
13955
  }
13924
13956
  }
13925
13957
  // Note: [💞] Ignore a discrepancy between file name and entity name
@@ -14302,8 +14334,8 @@ const teamToolTitles = {};
14302
14334
  * @private
14303
14335
  */
14304
14336
  const TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES = [
14305
- '- If a teammate is relevant to the request, consult that teammate using the matching tool.',
14306
- '- Do not ask the user for information that a listed teammate can provide directly.',
14337
+ '- If a teammate is relevant to the request, consult that teammate using the matching tool.',
14338
+ '- Do not ask the user for information that a listed teammate can provide directly.',
14307
14339
  ];
14308
14340
  /**
14309
14341
  * Constant for remote agents by Url.
@@ -14431,7 +14463,7 @@ class TeamCommitmentDefinition extends BaseCommitmentDefinition {
14431
14463
  toolName: entry.toolName,
14432
14464
  });
14433
14465
  }
14434
- const teamSystemMessage = this.createSystemMessageSection('Teammates:', buildTeamSystemMessageBody(teamEntries));
14466
+ const teamSystemMessage = this.createSystemMessageSection('Teammates', buildTeamSystemMessageBody(teamEntries));
14435
14467
  return this.appendToSystemMessage({
14436
14468
  ...requirements,
14437
14469
  tools: updatedTools,
@@ -14921,36 +14953,38 @@ function createAggregatedUseCommitmentSystemMessage(type, additionalInstructions
14921
14953
  switch (type) {
14922
14954
  case 'USE TIME':
14923
14955
  return spaceTrim$1((block) => `
14924
- Time and date context:
14925
- - It is ${moment().format('MMMM YYYY')} now.
14926
- - If you need more precise current time information, use the tool "get_current_time".
14956
+ ## Time and date context
14957
+
14958
+ - It is ${moment().format('MMMM YYYY')} now.
14959
+ - If you need more precise current time information, use the tool \`get_current_time\`.
14927
14960
  ${block(formatOptionalInstructionBlock('Time instructions', combinedAdditionalInstructions))}
14928
14961
  `);
14929
14962
  case 'USE BROWSER':
14930
14963
  return spaceTrim$1((block) => `
14931
- You have access to browser tools to fetch and access content from the internet.
14932
- - Use "fetch_url_content" to retrieve content from specific URLs (webpages or documents) using scrapers.
14933
- - Use "run_browser" for real interactive browser automation (navigation, clicks, typing, waiting, scrolling).
14934
- When you need to know information from a specific website or document, use the fetch_url_content tool.
14964
+ ## Browser
14965
+
14966
+ - Use \`fetch_url_content\` to retrieve content from specific URLs (webpages or documents) using scrapers.
14967
+ - Use \`run_browser\` for real interactive browser automation (navigation, clicks, typing, waiting, scrolling).
14968
+ - When you need to know information from a specific website or document, use the tools provided.
14935
14969
  ${block(formatOptionalInstructionBlock('Browser instructions', combinedAdditionalInstructions))}
14936
14970
  `);
14937
14971
  case 'USE SEARCH ENGINE':
14938
14972
  return spaceTrim$1((block) => `
14939
- Tool:
14940
- - You have access to the web search engine via the tool "web_search".
14941
- - Use it to find up-to-date information or facts that you don't know.
14942
- - When you need to know some information from the internet, use the tool provided to you.
14943
- - Do not make up information when you can search for it.
14944
- - Do not tell the user you cannot search for information, YOU CAN.
14973
+ ## Web Search
14974
+
14975
+ - Use \`web_search\` to find up-to-date information or facts.
14976
+ - When you need to know some information from the internet, use the search tool provided.
14977
+ - Do not make up information when you can search for it.
14978
+ - Do not tell the user you cannot search for information, YOU CAN.
14945
14979
  ${block(formatOptionalInstructionBlock('Search instructions', combinedAdditionalInstructions))}
14946
14980
  `);
14947
14981
  case 'USE DEEPSEARCH':
14948
14982
  return spaceTrim$1((block) => `
14949
- Tool:
14950
- - You have access to DeepSearch via the tool "deep_search".
14951
- - Use it for broader research tasks that need multi-step investigation, comparison, or synthesis across multiple sources.
14952
- - Prefer it over quick search when the user asks for a well-grounded brief, report, or deeper investigation.
14953
- - Do not pretend you cannot research current information when this tool is available.
14983
+ ## Deep Research
14984
+
14985
+ - Use \`deep_search\` for broader research tasks that need multi-step investigation, comparison, or synthesis across multiple sources.
14986
+ - Prefer it over quick search when the user asks for a well-grounded brief, report, or deeper investigation.
14987
+ - Do not pretend you cannot research current information when this tool is available.
14954
14988
  ${block(formatOptionalInstructionBlock('DeepSearch instructions', combinedAdditionalInstructions))}
14955
14989
  `);
14956
14990
  }
@@ -15280,96 +15314,6 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
15280
15314
  }
15281
15315
  // Note: [💞] Ignore a discrepancy between file name and entity name
15282
15316
 
15283
- /**
15284
- * Base Google Calendar API URL.
15285
- *
15286
- * @private constant of callGoogleCalendarApi
15287
- */
15288
- const GOOGLE_CALENDAR_API_BASE_URL = 'https://www.googleapis.com/calendar/v3';
15289
- /**
15290
- * Runs one Google Calendar API request and parses JSON response payload.
15291
- *
15292
- * @private function of UseCalendarCommitmentDefinition
15293
- */
15294
- async function callGoogleCalendarApi(accessToken, options) {
15295
- const url = new URL(options.path, GOOGLE_CALENDAR_API_BASE_URL);
15296
- if (options.query) {
15297
- for (const [key, value] of Object.entries(options.query)) {
15298
- if (value && value.trim()) {
15299
- url.searchParams.set(key, value);
15300
- }
15301
- }
15302
- }
15303
- const response = await fetch(url.toString(), {
15304
- method: options.method,
15305
- headers: {
15306
- Authorization: `Bearer ${accessToken}`,
15307
- Accept: 'application/json',
15308
- 'Content-Type': 'application/json',
15309
- },
15310
- body: options.body ? JSON.stringify(options.body) : undefined,
15311
- });
15312
- const textPayload = await response.text();
15313
- const parsedPayload = tryParseJson$2(textPayload);
15314
- if (options.allowNotFound && response.status === 404) {
15315
- return null;
15316
- }
15317
- if (!response.ok) {
15318
- throw new Error(spaceTrim$1(`
15319
- Google Calendar API request failed (${response.status} ${response.statusText}):
15320
- ${extractGoogleCalendarApiErrorMessage(parsedPayload, textPayload)}
15321
- `));
15322
- }
15323
- return parsedPayload;
15324
- }
15325
- /**
15326
- * Parses raw text into JSON when possible.
15327
- *
15328
- * @private function of callGoogleCalendarApi
15329
- */
15330
- function tryParseJson$2(rawText) {
15331
- if (!rawText.trim()) {
15332
- return {};
15333
- }
15334
- try {
15335
- return JSON.parse(rawText);
15336
- }
15337
- catch (_a) {
15338
- return rawText;
15339
- }
15340
- }
15341
- /**
15342
- * Extracts a user-friendly Google Calendar API error message.
15343
- *
15344
- * @private function of callGoogleCalendarApi
15345
- */
15346
- function extractGoogleCalendarApiErrorMessage(parsedPayload, fallbackText) {
15347
- if (parsedPayload && typeof parsedPayload === 'object') {
15348
- const payload = parsedPayload;
15349
- const errorPayload = payload.error;
15350
- if (errorPayload && typeof errorPayload === 'object') {
15351
- const normalizedErrorPayload = errorPayload;
15352
- const message = typeof normalizedErrorPayload.message === 'string' ? normalizedErrorPayload.message : '';
15353
- const errors = Array.isArray(normalizedErrorPayload.errors) ? normalizedErrorPayload.errors : [];
15354
- const flattenedErrors = errors
15355
- .map((errorEntry) => {
15356
- if (!errorEntry || typeof errorEntry !== 'object') {
15357
- return '';
15358
- }
15359
- const normalizedErrorEntry = errorEntry;
15360
- const detailMessage = typeof normalizedErrorEntry.message === 'string' ? normalizedErrorEntry.message : '';
15361
- const reason = typeof normalizedErrorEntry.reason === 'string' ? normalizedErrorEntry.reason : '';
15362
- return [detailMessage, reason].filter(Boolean).join(' | ');
15363
- })
15364
- .filter(Boolean);
15365
- if (message || flattenedErrors.length > 0) {
15366
- return [message, ...flattenedErrors].filter(Boolean).join(' | ');
15367
- }
15368
- }
15369
- }
15370
- return fallbackText || 'Unknown Google Calendar API error';
15371
- }
15372
-
15373
15317
  /**
15374
15318
  * Hostnames accepted for Google Calendar references.
15375
15319
  *
@@ -15551,6 +15495,96 @@ function removeTokenFromLine(line, token) {
15551
15495
  }
15552
15496
  // Note: [💞] Ignore a discrepancy between file name and entity name
15553
15497
 
15498
+ /**
15499
+ * Base Google Calendar API URL.
15500
+ *
15501
+ * @private constant of callGoogleCalendarApi
15502
+ */
15503
+ const GOOGLE_CALENDAR_API_BASE_URL = 'https://www.googleapis.com/calendar/v3';
15504
+ /**
15505
+ * Runs one Google Calendar API request and parses JSON response payload.
15506
+ *
15507
+ * @private function of UseCalendarCommitmentDefinition
15508
+ */
15509
+ async function callGoogleCalendarApi(accessToken, options) {
15510
+ const url = new URL(options.path, GOOGLE_CALENDAR_API_BASE_URL);
15511
+ if (options.query) {
15512
+ for (const [key, value] of Object.entries(options.query)) {
15513
+ if (value && value.trim()) {
15514
+ url.searchParams.set(key, value);
15515
+ }
15516
+ }
15517
+ }
15518
+ const response = await fetch(url.toString(), {
15519
+ method: options.method,
15520
+ headers: {
15521
+ Authorization: `Bearer ${accessToken}`,
15522
+ Accept: 'application/json',
15523
+ 'Content-Type': 'application/json',
15524
+ },
15525
+ body: options.body ? JSON.stringify(options.body) : undefined,
15526
+ });
15527
+ const textPayload = await response.text();
15528
+ const parsedPayload = tryParseJson$2(textPayload);
15529
+ if (options.allowNotFound && response.status === 404) {
15530
+ return null;
15531
+ }
15532
+ if (!response.ok) {
15533
+ throw new Error(spaceTrim$1(`
15534
+ Google Calendar API request failed (${response.status} ${response.statusText}):
15535
+ ${extractGoogleCalendarApiErrorMessage(parsedPayload, textPayload)}
15536
+ `));
15537
+ }
15538
+ return parsedPayload;
15539
+ }
15540
+ /**
15541
+ * Parses raw text into JSON when possible.
15542
+ *
15543
+ * @private function of callGoogleCalendarApi
15544
+ */
15545
+ function tryParseJson$2(rawText) {
15546
+ if (!rawText.trim()) {
15547
+ return {};
15548
+ }
15549
+ try {
15550
+ return JSON.parse(rawText);
15551
+ }
15552
+ catch (_a) {
15553
+ return rawText;
15554
+ }
15555
+ }
15556
+ /**
15557
+ * Extracts a user-friendly Google Calendar API error message.
15558
+ *
15559
+ * @private function of callGoogleCalendarApi
15560
+ */
15561
+ function extractGoogleCalendarApiErrorMessage(parsedPayload, fallbackText) {
15562
+ if (parsedPayload && typeof parsedPayload === 'object') {
15563
+ const payload = parsedPayload;
15564
+ const errorPayload = payload.error;
15565
+ if (errorPayload && typeof errorPayload === 'object') {
15566
+ const normalizedErrorPayload = errorPayload;
15567
+ const message = typeof normalizedErrorPayload.message === 'string' ? normalizedErrorPayload.message : '';
15568
+ const errors = Array.isArray(normalizedErrorPayload.errors) ? normalizedErrorPayload.errors : [];
15569
+ const flattenedErrors = errors
15570
+ .map((errorEntry) => {
15571
+ if (!errorEntry || typeof errorEntry !== 'object') {
15572
+ return '';
15573
+ }
15574
+ const normalizedErrorEntry = errorEntry;
15575
+ const detailMessage = typeof normalizedErrorEntry.message === 'string' ? normalizedErrorEntry.message : '';
15576
+ const reason = typeof normalizedErrorEntry.reason === 'string' ? normalizedErrorEntry.reason : '';
15577
+ return [detailMessage, reason].filter(Boolean).join(' | ');
15578
+ })
15579
+ .filter(Boolean);
15580
+ if (message || flattenedErrors.length > 0) {
15581
+ return [message, ...flattenedErrors].filter(Boolean).join(' | ');
15582
+ }
15583
+ }
15584
+ }
15585
+ return fallbackText || 'Unknown Google Calendar API error';
15586
+ }
15587
+
15554
15588
  /**
15555
15589
  * Wallet metadata used by USE CALENDAR when resolving Google Calendar credentials.
15556
15590
  *
@@ -16451,18 +16485,20 @@ class UseCalendarCommitmentDefinition extends BaseCommitmentDefinition {
16451
16485
  if (parsedCommitment.calendar) {
16452
16486
  addConfiguredCalendarIfMissing(existingConfiguredCalendars, parsedCommitment.calendar);
16453
16487
  }
16454
- const calendarsList = existingConfiguredCalendars.length > 0
16455
- ? existingConfiguredCalendars
16456
- .map((calendar) => [
16457
- `- ${calendar.provider}: ${calendar.url}`,
16458
- calendar.scopes.length > 0 ? ` scopes: ${calendar.scopes.join(', ')}` : '',
16459
- ]
16460
- .filter(Boolean)
16461
- .join('\n'))
16462
- .join('\n')
16463
- : '- Calendar is resolved from runtime context';
16488
+ const calendarBullets = existingConfiguredCalendars.length > 0
16489
+ ? existingConfiguredCalendars.map((calendar) => `- ${calendar.provider}: ${calendar.url}`).join('\n')
16490
+ : '- Calendar is resolved from runtime context';
16464
16491
  const extraInstructions = formatOptionalInstructionBlock('Calendar instructions', parsedCommitment.instructions);
16465
- return this.appendToSystemMessage({
16492
+ const calendarSectionContent = spaceTrim$1((block) => `
16493
+ ## Calendar
16494
+
16495
+ - Use \`calendar_list_events\`, \`calendar_get_event\`, \`calendar_create_event\`, \`calendar_update_event\`, \`calendar_delete_event\`, and \`calendar_invite_guests\` to manage events in configured calendars.
16496
+ - Supported operations include read, create, update, delete, invite guests, and reminders.
16497
+ - Configured calendars:
16498
+ ${block(calendarBullets)}
16499
+ ${block(extraInstructions)}
16500
+ `);
16501
+ return this.replaceOrCreateSection({
16466
16502
  ...requirements,
16467
16503
  tools: createUseCalendarTools(requirements.tools || []),
16468
16504
  _metadata: {
@@ -16470,16 +16506,7 @@ class UseCalendarCommitmentDefinition extends BaseCommitmentDefinition {
16470
16506
  useCalendar: true,
16471
16507
  useCalendars: existingConfiguredCalendars,
16472
16508
  },
16473
- }, spaceTrim$1((block) => `
16474
- Calendar tools:
16475
- - You can inspect and manage events in configured calendars.
16476
- - Supported operations include read, create, update, delete, invite guests, and reminders.
16477
- - Configured calendars:
16478
- ${block(calendarsList)}
16479
- - USE CALENDAR credentials are read from wallet records (ACCESS_TOKEN, service "${UseCalendarWallet.service}", key "${UseCalendarWallet.key}").
16480
- - If credentials are missing, ask user to connect calendar credentials in host UI and/or add them to wallet.
16481
- ${block(extraInstructions)}
16482
- `));
16509
+ }, 'Calendar', calendarSectionContent);
16483
16510
  }
16484
16511
  /**
16485
16512
  * Gets human-readable titles for tool functions provided by this commitment.
@@ -16816,18 +16843,6 @@ async function sendEmailViaBrowser(args, agentsServerUrl) {
16816
16843
  * @private internal USE EMAIL constant
16817
16844
  */
16818
16845
  const SEND_EMAIL_TOOL_NAME = 'send_email';
16819
- /**
16820
- * Wallet service used for SMTP credentials required by USE EMAIL.
16821
- *
16822
- * @private internal USE EMAIL constant
16823
- */
16824
- const USE_EMAIL_SMTP_WALLET_SERVICE = 'smtp';
16825
- /**
16826
- * Wallet key used for SMTP credentials required by USE EMAIL.
16827
- *
16828
- * @private internal USE EMAIL constant
16829
- */
16830
- const USE_EMAIL_SMTP_WALLET_KEY = 'use-email-smtp-credentials';
16831
16846
  /**
16832
16847
  * USE EMAIL commitment definition.
16833
16848
  *
@@ -16886,31 +16901,41 @@ class UseEmailCommitmentDefinition extends BaseCommitmentDefinition {
16886
16901
  `);
16887
16902
  }
16888
16903
  applyToAgentModelRequirements(requirements, content) {
16904
+ var _a;
16889
16905
  const parsedCommitment = parseUseEmailCommitmentContent(content);
16890
- const extraInstructions = formatOptionalInstructionBlock('Email instructions', parsedCommitment.instructions);
16891
- const senderInstruction = parsedCommitment.senderEmail
16892
- ? `- Default sender address from commitment: "${parsedCommitment.senderEmail}".`
16893
- : '';
16894
16906
  const updatedTools = addUseEmailTools(requirements.tools || []);
16895
- return this.appendToSystemMessage({
16907
+ // Collect all configured sender emails across multiple USE EMAIL commitments
16908
+ const existingSenders = Array.isArray((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.useEmailSenders)
16909
+ ? [...requirements._metadata.useEmailSenders]
16910
+ : [];
16911
+ if (parsedCommitment.senderEmail && !existingSenders.includes(parsedCommitment.senderEmail)) {
16912
+ existingSenders.push(parsedCommitment.senderEmail);
16913
+ }
16914
+ const senderBullets = existingSenders.length > 0
16915
+ ? existingSenders
16916
+ .map((email, index) => index === 0
16917
+ ? `- Default sender address: "${email}".`
16918
+ : `- Additional sender address: "${email}".`)
16919
+ .join('\n')
16920
+ : '';
16921
+ const extraInstructions = formatOptionalInstructionBlock('Email instructions', parsedCommitment.instructions);
16922
+ const emailSectionContent = spaceTrim$1((block) => `
16923
+ ## Emails
16924
+
16925
+ - Use \`${SEND_EMAIL_TOOL_NAME}\` to send outbound emails.
16926
+ ${block(senderBullets)}
16927
+ ${block(extraInstructions)}
16928
+ `);
16929
+ return this.replaceOrCreateSection({
16896
16930
  ...requirements,
16897
16931
  tools: updatedTools,
16898
16932
  _metadata: {
16899
16933
  ...requirements._metadata,
16900
16934
  useEmail: true,
16901
16935
  ...(parsedCommitment.senderEmail ? { useEmailSender: parsedCommitment.senderEmail } : {}),
16936
+ useEmailSenders: existingSenders,
16902
16937
  },
16903
- }, spaceTrim$1((block) => `
16904
- Email tool:
16905
- - Use "${SEND_EMAIL_TOOL_NAME}" to send outbound emails.
16906
- - Prefer \`message\` argument compatible with Promptbook \`Message\` type.
16907
- - Include subject in \`message.metadata.subject\` (or use legacy \`subject\` argument).
16908
- - USE EMAIL credentials are read from wallet records (ACCESS_TOKEN, service "${USE_EMAIL_SMTP_WALLET_SERVICE}", key "${USE_EMAIL_SMTP_WALLET_KEY}").
16909
- - Wallet secret must contain SMTP credentials in JSON format with fields \`host\`, \`port\`, \`secure\`, \`username\`, \`password\`.
16910
- - If credentials are missing, ask user to add wallet credentials.
16911
- ${block(senderInstruction)}
16912
- ${block(extraInstructions)}
16913
- `));
16938
+ }, 'Emails', emailSectionContent);
16914
16939
  }
16915
16940
  /**
16916
16941
  * Gets human-readable titles for tool functions provided by this commitment.
@@ -16946,13 +16971,13 @@ function addUseEmailTools(existingTools) {
16946
16971
  ...existingTools,
16947
16972
  {
16948
16973
  name: SEND_EMAIL_TOOL_NAME,
16949
- description: 'Send an outbound email through configured SMTP credentials. Prefer providing Message-like payload in `message`.',
16974
+ description: 'Send an outbound email.',
16950
16975
  parameters: {
16951
16976
  type: 'object',
16952
16977
  properties: {
16953
16978
  message: {
16954
16979
  type: 'object',
16955
- description: 'Preferred input payload compatible with Promptbook Message type. Use metadata.subject for subject line.',
16980
+ description: 'Email payload. Use metadata.subject for the subject line.',
16956
16981
  },
16957
16982
  to: {
16958
16983
  type: 'string',
@@ -17056,13 +17081,14 @@ class UseImageGeneratorCommitmentDefinition extends BaseCommitmentDefinition {
17056
17081
  useImageGenerator: content || true,
17057
17082
  },
17058
17083
  }, spaceTrim$1((block) => `
17059
- Image generation:
17060
- - You do not generate images directly and you do not call any image tool.
17061
- - When the user asks for an image, include markdown notation in your message:
17062
- \`![<alt text>](?image-prompt=<prompt>)\`
17063
- - Keep \`<alt text>\` short and descriptive.
17064
- - Keep \`<prompt>\` detailed so the generated image matches the request.
17065
- - You can include normal explanatory text before and after the notation.
17084
+ ## Image generation
17085
+
17086
+ - You do not generate images directly and you do not call any image tool.
17087
+ - When the user asks for an image, include markdown notation in your message:
17088
+ \`![<alt text>](?image-prompt=<prompt>)\`
17089
+ - Keep \`<alt text>\` short and descriptive.
17090
+ - Keep \`<prompt>\` detailed so the generated image matches the request.
17091
+ - You can include normal explanatory text before and after the notation.
17066
17092
  ${block(extraInstructions)}
17067
17093
  `));
17068
17094
  }
@@ -17242,11 +17268,12 @@ class UsePopupCommitmentDefinition extends BaseCommitmentDefinition {
17242
17268
  usePopup: content || true,
17243
17269
  },
17244
17270
  }, spaceTrim$1((block) => `
17245
- Tool:
17246
- - You can open a popup window with a specific URL using the tool "open_popup".
17247
- - Use this when you want the user to see or interact with a specific website.
17271
+ ## Popup
17272
+
17273
+ - You can open a popup window with a specific URL using the tool \`open_popup\`.
17274
+ - Use this when you want the user to see or interact with a specific website.
17248
17275
  ${block(extraInstructions)}
17249
- `));
17276
+ `));
17250
17277
  }
17251
17278
  /**
17252
17279
  * Gets human-readable titles for tool functions provided by this commitment.
@@ -17420,11 +17447,12 @@ class UsePrivacyCommitmentDefinition extends BaseCommitmentDefinition {
17420
17447
  usePrivacy: content || true,
17421
17448
  },
17422
17449
  }, spaceTrim$1((block) => `
17423
- Privacy mode:
17424
- - Use "${TURN_PRIVACY_ON_TOOL_NAME}" when the user asks for a private/sensitive conversation.
17425
- - This tool requests a UI confirmation dialog. Private mode is enabled only after user confirms.
17426
- - Current implementation uses the existing chat private mode (no chat persistence, memory persistence, or self-learning while active).
17427
- - Do not claim that end-to-end encryption is implemented yet.
17450
+ ## Privacy
17451
+
17452
+ - Use \`${TURN_PRIVACY_ON_TOOL_NAME}\` when the user asks for a private/sensitive conversation.
17453
+ - This tool requests a UI confirmation dialog. Private mode is enabled only after user confirms.
17454
+ - Current implementation uses the existing chat private mode (no chat persistence, memory persistence, or self-learning while active).
17455
+ - Do not claim that end-to-end encryption is implemented yet.
17428
17456
  ${block(extraInstructions)}
17429
17457
  `));
17430
17458
  }
@@ -19067,9 +19095,16 @@ class UseProjectCommitmentDefinition extends BaseCommitmentDefinition {
19067
19095
  }
19068
19096
  const existingConfiguredProjects = normalizeConfiguredProjects((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.useProjects);
19069
19097
  addConfiguredProjectIfMissing(existingConfiguredProjects, parsedCommitment.repository);
19070
- const repositoriesList = existingConfiguredProjects.map((project) => `- ${project.url}`).join('\n');
19071
19098
  const extraInstructions = formatOptionalInstructionBlock('Project instructions', parsedCommitment.instructions);
19072
- return this.appendToSystemMessage({
19099
+ const sectionContent = spaceTrim$1((block) => `
19100
+ - You can inspect and edit configured GitHub repositories using project tools.
19101
+ - Configured repositories:
19102
+ ${block(existingConfiguredProjects.map((project) => `- ${project.url}`).join('\n'))}
19103
+ - When a repository is not obvious from context, pass \`repository\` in tool arguments explicitly.
19104
+ - If credentials are missing, ask the user to connect their GitHub account in the host UI.
19105
+ ${block(extraInstructions)}
19106
+ `);
19107
+ return this.replaceOrCreateSection({
19073
19108
  ...requirements,
19074
19109
  tools: createUseProjectTools(requirements.tools || []),
19075
19110
  _metadata: {
@@ -19077,16 +19112,7 @@ class UseProjectCommitmentDefinition extends BaseCommitmentDefinition {
19077
19112
  useProject: true,
19078
19113
  useProjects: existingConfiguredProjects,
19079
19114
  },
19080
- }, spaceTrim$1((block) => `
19081
- Project tools:
19082
- - You can inspect and edit configured GitHub repositories using project tools.
19083
- - Configured repositories:
19084
- ${block(repositoriesList)}
19085
- - When a repository is not obvious from context, pass "repository" in tool arguments explicitly.
19086
- - USE PROJECT credentials are read from wallet records (ACCESS_TOKEN, service "${UseProjectWallet.service}", key "${UseProjectWallet.key}").
19087
- - If credentials are missing, ask the user to connect credentials in host UI and/or add them to wallet.
19088
- ${block(extraInstructions)}
19089
- `));
19115
+ }, 'GitHub repositories', sectionContent);
19090
19116
  }
19091
19117
  /**
19092
19118
  * Gets human-readable titles for tool functions provided by this commitment.
@@ -19433,11 +19459,12 @@ class UseSpawnCommitmentDefinition extends BaseCommitmentDefinition {
19433
19459
  useSpawn: content || true,
19434
19460
  },
19435
19461
  }, spaceTrim$1((block) => `
19436
- Spawning agents:
19437
- - Use "${SPAWN_AGENT_TOOL_NAME}" only when user asks to create a persistent new agent.
19438
- - Pass full agent source in \`source\`.
19439
- - Keep \`source\` concise; the maximum accepted length is ${CREATE_AGENT_INPUT_SOURCE_MAX_LENGTH} characters.
19440
- - Do not add unknown fields in tool arguments.
19462
+ ## Spawning agents
19463
+
19464
+ - Use \`${SPAWN_AGENT_TOOL_NAME}\` only when user asks to create a persistent new agent.
19465
+ - Pass full agent source in \`source\`.
19466
+ - Keep \`source\` concise; the maximum accepted length is ${CREATE_AGENT_INPUT_SOURCE_MAX_LENGTH} characters.
19467
+ - Do not add unknown fields in tool arguments.
19441
19468
  ${block(extraInstructions)}
19442
19469
  `));
19443
19470
  }
@@ -19471,13 +19498,14 @@ class UseSpawnCommitmentDefinition extends BaseCommitmentDefinition {
19471
19498
  */
19472
19499
  function createTimeoutSystemMessage(extraInstructions) {
19473
19500
  return spaceTrim$1((block) => `
19474
- Timeout scheduling:
19475
- - Use "set_timeout" to wake this same chat thread in the future.
19476
- - Use "list_timeouts" to review timeout ids/details across all chats for the same user+agent scope.
19477
- - "cancel_timeout" accepts either one timeout id or \`allActive: true\` to cancel all active timeouts in this same user+agent scope.
19478
- - Use "update_timeout" to pause/resume, edit next run, edit recurrence, or update timeout payload details.
19479
- - When one timeout elapses, you will receive a new user-like message that explicitly says it is a timeout wake-up and includes the \`timeoutId\`.
19480
- - Do not claim a timer was set or cancelled unless the tool confirms it.
19501
+ ## Timeout scheduling
19502
+
19503
+ - Use \`set_timeout\` to wake this same chat thread in the future.
19504
+ - Use \`list_timeouts\` to review timeout ids/details across all chats for the same user+agent scope.
19505
+ - \`cancel_timeout\` accepts either one timeout id or \`allActive: true\` to cancel all active timeouts in this same user+agent scope.
19506
+ - Use \`update_timeout\` to pause/resume, edit next run, edit recurrence, or update timeout payload details.
19507
+ - When one timeout elapses, you will receive a new user-like message that explicitly says it is a timeout wake-up and includes the \`timeoutId\`.
19508
+ - Do not claim a timer was set or cancelled unless the tool confirms it.
19481
19509
  ${block(extraInstructions)}
19482
19510
  `);
19483
19511
  }
@@ -20577,10 +20605,11 @@ class UseUserLocationCommitmentDefinition extends BaseCommitmentDefinition {
20577
20605
  useUserLocation: content || true,
20578
20606
  },
20579
20607
  }, spaceTrim$1((block) => `
20580
- User location:
20581
- - Use "${GET_USER_LOCATION_TOOL_NAME}" only when location is needed for a better answer.
20582
- - If the tool returns "unavailable" or "permission-denied", ask user to share location or provide city manually.
20583
- - Do not invent coordinates or local facts when location is unavailable.
20608
+ ## User location
20609
+
20610
+ - Use \`${GET_USER_LOCATION_TOOL_NAME}\` only when location is needed for a better answer.
20611
+ - If the tool returns "unavailable" or "permission-denied", ask user to share location or provide city manually.
20612
+ - Do not invent coordinates or local facts when location is unavailable.
20584
20613
  ${block(extraInstructions)}
20585
20614
  `));
20586
20615
  }
@@ -23532,6 +23561,241 @@ function useBookEditorMonacoDiagnostics({ editor, monaco, diagnostics }) {
23532
23561
  }, [diagnostics, editor, monaco]);
23533
23562
  }
23534
23563
 
23564
+ /**
23565
+ * Clipboard MIME types treated as rich text documents for upload.
23566
+ *
23567
+ * @private function of BookEditorMonaco
23568
+ */
23569
+ const RICH_CLIPBOARD_TEXT_MIME_TYPES = new Set(['text/html', 'text/rtf']);
23570
+ /**
23571
+ * Uploaded filename mapping for known rich clipboard MIME types.
23572
+ *
23573
+ * @private function of BookEditorMonaco
23574
+ */
23575
+ const CLIPBOARD_RICH_CONTENT_FILENAMES = {
23576
+ 'text/html': 'clipboard-content.html',
23577
+ 'text/rtf': 'clipboard-content.rtf',
23578
+ 'application/rtf': 'clipboard-content.rtf',
23579
+ };
23580
+ /**
23581
+ * Fallback filename used when clipboard MIME type is unknown.
23582
+ *
23583
+ * @private function of BookEditorMonaco
23584
+ */
23585
+ const DEFAULT_CLIPBOARD_RICH_CONTENT_FILENAME = 'clipboard-content.txt';
23586
+ /**
23587
+ * Maximum pointer travel still treated as a tap on the touch focus overlay.
23588
+ *
23589
+ * @private function of BookEditorMonaco
23590
+ */
23591
+ const TOUCH_TAP_THRESHOLD = 10;
23592
+ /**
23593
+ * Lists transferable items from a browser `DataTransfer` object.
23594
+ *
23595
+ * @private function of BookEditorMonaco
23596
+ */
23597
+ function listDataTransferItems(dataTransfer) {
23598
+ const items = [];
23599
+ for (let index = 0; index < dataTransfer.items.length; index++) {
23600
+ const item = dataTransfer.items[index];
23601
+ if (item) {
23602
+ items.push(item);
23603
+ }
23604
+ }
23605
+ return items;
23606
+ }
23607
+ /**
23608
+ * Removes duplicate files by using stable file metadata signature.
23609
+ *
23610
+ * @private function of BookEditorMonaco
23611
+ */
23612
+ function deduplicateFiles(files) {
23613
+ const uniqueFiles = new Map();
23614
+ for (const file of files) {
23615
+ uniqueFiles.set(`${file.name}:${file.type}:${file.size}`, file);
23616
+ }
23617
+ return Array.from(uniqueFiles.values());
23618
+ }
23619
+ /**
23620
+ * Extracts all file-like clipboard/drop payloads from a `DataTransfer`.
23621
+ *
23622
+ * @private function of BookEditorMonaco
23623
+ */
23624
+ function getDataTransferFiles(dataTransfer) {
23625
+ const directFiles = Array.from(dataTransfer.files || []);
23626
+ const itemFiles = listDataTransferItems(dataTransfer)
23627
+ .filter((item) => item.kind === 'file')
23628
+ .map((item) => item.getAsFile())
23629
+ .filter((file) => file !== null);
23630
+ return deduplicateFiles([...directFiles, ...itemFiles]);
23631
+ }
23632
+ /**
23633
+ * Picks the richest textual clipboard item that should be uploaded as a document.
23634
+ *
23635
+ * @private function of BookEditorMonaco
23636
+ */
23637
+ function getRichClipboardTextItem(dataTransfer) {
23638
+ const items = listDataTransferItems(dataTransfer);
23639
+ const hasPlainTextItem = items.some((item) => item.kind === 'string' && item.type.toLowerCase() === 'text/plain');
23640
+ const applicationItem = items.find((item) => item.kind === 'string' && item.type.toLowerCase().startsWith('application/'));
23641
+ if (applicationItem) {
23642
+ return applicationItem;
23643
+ }
23644
+ if (hasPlainTextItem) {
23645
+ return null;
23646
+ }
23647
+ const richTextItem = items.find((item) => {
23648
+ if (item.kind !== 'string') {
23649
+ return false;
23650
+ }
23651
+ return RICH_CLIPBOARD_TEXT_MIME_TYPES.has(item.type.toLowerCase());
23652
+ });
23653
+ return richTextItem || null;
23654
+ }
23655
+ /**
23656
+ * Resolves whether paste should route into upload workflow instead of text insert.
23657
+ *
23658
+ * @private function of BookEditorMonaco
23659
+ */
23660
+ function hasUploadableClipboardContent(dataTransfer) {
23661
+ if (getDataTransferFiles(dataTransfer).length > 0) {
23662
+ return true;
23663
+ }
23664
+ return getRichClipboardTextItem(dataTransfer) !== null;
23665
+ }
23666
+ /**
23667
+ * Reads string payload from clipboard item.
23668
+ *
23669
+ * @private function of BookEditorMonaco
23670
+ */
23671
+ function getClipboardItemString(item) {
23672
+ return new Promise((resolve) => {
23673
+ try {
23674
+ item.getAsString((value) => resolve(value || ''));
23675
+ }
23676
+ catch (_a) {
23677
+ resolve('');
23678
+ }
23679
+ });
23680
+ }
23681
+ /**
23682
+ * Determines filename for generated clipboard rich-content uploads.
23683
+ *
23684
+ * @private function of BookEditorMonaco
23685
+ */
23686
+ function getClipboardRichContentFilename(mimeType) {
23687
+ return CLIPBOARD_RICH_CONTENT_FILENAMES[mimeType.toLowerCase()] || DEFAULT_CLIPBOARD_RICH_CONTENT_FILENAME;
23688
+ }
23689
+ /**
23690
+ * Converts clipboard payload into upload-ready files.
23691
+ *
23692
+ * @private function of BookEditorMonaco
23693
+ */
23694
+ async function resolveClipboardUploadFiles(dataTransfer) {
23695
+ const files = getDataTransferFiles(dataTransfer);
23696
+ if (files.length > 0) {
23697
+ return files;
23698
+ }
23699
+ const richTextItem = getRichClipboardTextItem(dataTransfer);
23700
+ if (!richTextItem) {
23701
+ return [];
23702
+ }
23703
+ const content = await getClipboardItemString(richTextItem);
23704
+ const mimeType = richTextItem.type || 'text/plain';
23705
+ if (!content.trim()) {
23706
+ return [];
23707
+ }
23708
+ return [new File([content], getClipboardRichContentFilename(mimeType), { type: mimeType })];
23709
+ }
23710
+ /**
23711
+ * Manages drag, paste and file input interactions for `BookEditorMonaco`.
23712
+ *
23713
+ * @private function of BookEditorMonaco
23714
+ */
23715
+ function useBookEditorMonacoInteractions({ editor, handleFiles, }) {
23716
+ const [isDragOver, setIsDragOver] = useState(false);
23717
+ const touchStartRef = useRef(null);
23718
+ const fileUploadInputRef = useRef(null);
23719
+ const cameraInputRef = useRef(null);
23720
+ const handleDrop = useCallback(async (event) => {
23721
+ event.preventDefault();
23722
+ setIsDragOver(false);
23723
+ const files = getDataTransferFiles(event.dataTransfer);
23724
+ await handleFiles(files);
23725
+ }, [handleFiles]);
23726
+ const handlePaste = useCallback(async (event) => {
23727
+ const clipboardData = event.clipboardData;
23728
+ if (!hasUploadableClipboardContent(clipboardData)) {
23729
+ return;
23730
+ }
23731
+ event.preventDefault();
23732
+ event.stopPropagation();
23733
+ const files = await resolveClipboardUploadFiles(clipboardData);
23734
+ await handleFiles(files);
23735
+ }, [handleFiles]);
23736
+ const handleUploadDocument = useCallback(() => {
23737
+ var _a;
23738
+ (_a = fileUploadInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
23739
+ }, []);
23740
+ const handleTakePhoto = useCallback(() => {
23741
+ var _a;
23742
+ (_a = cameraInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
23743
+ }, []);
23744
+ const handleFileInputChange = useCallback((event) => {
23745
+ const files = Array.from(event.target.files || []);
23746
+ void handleFiles(files);
23747
+ event.target.value = '';
23748
+ }, [handleFiles]);
23749
+ const handleDragOver = useCallback((event) => {
23750
+ event.preventDefault();
23751
+ setIsDragOver(true);
23752
+ }, []);
23753
+ const handleDragEnter = useCallback((event) => {
23754
+ event.preventDefault();
23755
+ setIsDragOver(true);
23756
+ }, []);
23757
+ const handleDragLeave = useCallback((event) => {
23758
+ event.preventDefault();
23759
+ setIsDragOver(false);
23760
+ }, []);
23761
+ const handleFocusOverlayTouchStart = useCallback((event) => {
23762
+ const touch = event.touches[0];
23763
+ if (touch) {
23764
+ touchStartRef.current = { x: touch.clientX, y: touch.clientY };
23765
+ }
23766
+ }, []);
23767
+ const handleFocusOverlayTouchEnd = useCallback((event) => {
23768
+ event.preventDefault();
23769
+ const touch = event.changedTouches[0];
23770
+ if (touch && touchStartRef.current) {
23771
+ const deltaX = Math.abs(touch.clientX - touchStartRef.current.x);
23772
+ const deltaY = Math.abs(touch.clientY - touchStartRef.current.y);
23773
+ if (deltaX < TOUCH_TAP_THRESHOLD && deltaY < TOUCH_TAP_THRESHOLD) {
23774
+ editor === null || editor === void 0 ? void 0 : editor.focus();
23775
+ }
23776
+ }
23777
+ touchStartRef.current = null;
23778
+ }, [editor]);
23779
+ const focusOverlayTouchHandlers = useMemo(() => ({
23780
+ onTouchStart: handleFocusOverlayTouchStart,
23781
+ onTouchEnd: handleFocusOverlayTouchEnd,
23782
+ }), [handleFocusOverlayTouchEnd, handleFocusOverlayTouchStart]);
23783
+ return {
23784
+ isDragOver,
23785
+ fileUploadInputRef,
23786
+ cameraInputRef,
23787
+ handleDrop,
23788
+ handlePaste,
23789
+ handleUploadDocument,
23790
+ handleTakePhoto,
23791
+ handleFileInputChange,
23792
+ handleDragOver,
23793
+ handleDragEnter,
23794
+ handleDragLeave,
23795
+ focusOverlayTouchHandlers,
23796
+ };
23797
+ }
23798
+
23535
23799
  /**
23536
23800
  * Priority order for the important commitments shown first in catalogues and intellisense.
23537
23801
  *
@@ -24194,6 +24458,181 @@ function useBookEditorMonacoLanguage({ monaco, theme }) {
24194
24458
  }, [monaco, theme]);
24195
24459
  }
24196
24460
 
24461
+ /**
24462
+ * Local storage key that enables BookEditor Monaco lifecycle debug logs in development.
24463
+ *
24464
+ * @private function of BookEditorMonaco
24465
+ */
24466
+ const BOOK_EDITOR_MONACO_DEBUG_STORAGE_KEY = 'promptbook-debug-book-editor-monaco';
24467
+ /**
24468
+ * Duration of the save notification.
24469
+ *
24470
+ * @private function of BookEditorMonaco
24471
+ */
24472
+ const SAVE_NOTIFICATION_HIDE_DELAY_MS = 2000;
24473
+ /**
24474
+ * Resolves whether verbose BookEditor Monaco lifecycle logs are enabled.
24475
+ *
24476
+ * @private function of BookEditorMonaco
24477
+ */
24478
+ function isBookEditorMonacoDebugEnabled() {
24479
+ if (process.env.NODE_ENV === 'production' || typeof window === 'undefined') {
24480
+ return false;
24481
+ }
24482
+ try {
24483
+ return window.localStorage.getItem(BOOK_EDITOR_MONACO_DEBUG_STORAGE_KEY) === '1';
24484
+ }
24485
+ catch (_a) {
24486
+ return false;
24487
+ }
24488
+ }
24489
+ /**
24490
+ * Prints one BookEditor Monaco debug line when the dev debug flag is enabled.
24491
+ *
24492
+ * @param message - Human-readable lifecycle message.
24493
+ *
24494
+ * @private function of BookEditorMonaco
24495
+ */
24496
+ function logBookEditorMonacoDebug(message) {
24497
+ if (!isBookEditorMonacoDebugEnabled()) {
24498
+ return;
24499
+ }
24500
+ console.info(`[BookEditorMonaco] ${message}`);
24501
+ }
24502
+ /**
24503
+ * Detects whether the current device primarily uses a coarse pointer.
24504
+ *
24505
+ * @private function of BookEditorMonaco
24506
+ */
24507
+ function detectIsTouchDevice() {
24508
+ if (typeof window === 'undefined') {
24509
+ return false;
24510
+ }
24511
+ return window.matchMedia('(pointer: coarse)').matches;
24512
+ }
24513
+ /**
24514
+ * Manages Monaco lifecycle wiring for `BookEditorMonaco`.
24515
+ *
24516
+ * @private function of BookEditorMonaco
24517
+ */
24518
+ function useBookEditorMonacoLifecycle({ monaco, theme, }) {
24519
+ const [editor, setEditor] = useState(null);
24520
+ const [isFocused, setIsFocused] = useState(false);
24521
+ const [isTouchDevice, setIsTouchDevice] = useState(false);
24522
+ const [isSavedShown, setIsSavedShown] = useState(false);
24523
+ /**
24524
+ * Re-applies Book language + theme to the currently mounted Monaco model.
24525
+ */
24526
+ const reapplyBookLanguageAndTheme = useCallback((reason) => {
24527
+ if (!editor || !monaco) {
24528
+ return;
24529
+ }
24530
+ ensureBookEditorMonacoLanguageForEditor({ monaco, monacoEditor: editor, theme });
24531
+ logBookEditorMonacoDebug(`Re-applied Book Monaco language/theme (${reason}).`);
24532
+ }, [editor, monaco, theme]);
24533
+ /**
24534
+ * Re-triggers the transient save toast without preventing the browser save dialog.
24535
+ */
24536
+ const showSavedNotification = useCallback(() => {
24537
+ setIsSavedShown(false);
24538
+ setTimeout(() => setIsSavedShown(true), 0);
24539
+ }, []);
24540
+ useEffect(() => {
24541
+ setIsTouchDevice(detectIsTouchDevice());
24542
+ }, []);
24543
+ useEffect(() => {
24544
+ if (!editor || !monaco) {
24545
+ return;
24546
+ }
24547
+ const focusListener = editor.onDidFocusEditorWidget(() => {
24548
+ setIsFocused(true);
24549
+ reapplyBookLanguageAndTheme('focus');
24550
+ });
24551
+ const blurListener = editor.onDidBlurEditorWidget(() => {
24552
+ setIsFocused(false);
24553
+ });
24554
+ const saveAction = editor.addAction({
24555
+ id: 'save-book',
24556
+ label: 'Save',
24557
+ keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS],
24558
+ run: () => {
24559
+ showSavedNotification();
24560
+ // Note: We don't prevent default, so browser's save dialog still opens
24561
+ },
24562
+ });
24563
+ return () => {
24564
+ focusListener.dispose();
24565
+ blurListener.dispose();
24566
+ saveAction.dispose();
24567
+ };
24568
+ }, [editor, monaco, reapplyBookLanguageAndTheme, showSavedNotification]);
24569
+ useEffect(() => {
24570
+ reapplyBookLanguageAndTheme('editor-ready');
24571
+ }, [reapplyBookLanguageAndTheme]);
24572
+ useEffect(() => {
24573
+ if (!editor || !monaco) {
24574
+ return;
24575
+ }
24576
+ const handlePopState = () => {
24577
+ reapplyBookLanguageAndTheme('history-popstate');
24578
+ };
24579
+ const handlePageShow = () => {
24580
+ reapplyBookLanguageAndTheme('pageshow');
24581
+ };
24582
+ const handleWindowFocus = () => {
24583
+ reapplyBookLanguageAndTheme('window-focus');
24584
+ };
24585
+ const handleVisibilityChange = () => {
24586
+ if (document.visibilityState === 'visible') {
24587
+ reapplyBookLanguageAndTheme('visibility-visible');
24588
+ }
24589
+ };
24590
+ window.addEventListener('popstate', handlePopState);
24591
+ window.addEventListener('pageshow', handlePageShow);
24592
+ window.addEventListener('focus', handleWindowFocus);
24593
+ document.addEventListener('visibilitychange', handleVisibilityChange);
24594
+ return () => {
24595
+ window.removeEventListener('popstate', handlePopState);
24596
+ window.removeEventListener('pageshow', handlePageShow);
24597
+ window.removeEventListener('focus', handleWindowFocus);
24598
+ document.removeEventListener('visibilitychange', handleVisibilityChange);
24599
+ };
24600
+ }, [editor, monaco, reapplyBookLanguageAndTheme]);
24601
+ useEffect(() => {
24602
+ if (!isSavedShown) {
24603
+ return;
24604
+ }
24605
+ const timer = setTimeout(() => {
24606
+ setIsSavedShown(false);
24607
+ }, SAVE_NOTIFICATION_HIDE_DELAY_MS);
24608
+ return () => {
24609
+ clearTimeout(timer);
24610
+ };
24611
+ }, [isSavedShown]);
24612
+ /**
24613
+ * Ensures Book language/tokenizer is ready before Monaco creates the editor model.
24614
+ */
24615
+ const handleBeforeMonacoMount = useCallback((beforeMountMonaco) => {
24616
+ ensureBookEditorMonacoLanguage(beforeMountMonaco, theme);
24617
+ }, [theme]);
24618
+ /**
24619
+ * Re-applies Book language/theme once Monaco editor is mounted.
24620
+ */
24621
+ const handleMonacoMount = useCallback((mountedEditor, mountedMonaco) => {
24622
+ setEditor(mountedEditor);
24623
+ ensureBookEditorMonacoLanguageForEditor({ monaco: mountedMonaco, monacoEditor: mountedEditor, theme });
24624
+ logBookEditorMonacoDebug('Mounted Monaco editor and re-applied Book language/theme.');
24625
+ }, [theme]);
24626
+ return {
24627
+ editor,
24628
+ isFocused,
24629
+ isTouchDevice,
24630
+ isSavedShown,
24631
+ handleBeforeMonacoMount,
24632
+ handleMonacoMount,
24633
+ };
24634
+ }
24635
+
24197
24636
  /**
24198
24637
  * Relative Y offset multiplier for aligning line background with Monaco rendering.
24199
24638
  *
@@ -25003,190 +25442,101 @@ function BookEditorMonacoUploadPanel({ activeUploadItems, uploadStats, pauseUplo
25003
25442
  */
25004
25443
  const INVALID_CSS_IDENTIFIER_CHARACTER_PATTERN = /[^a-zA-Z0-9_-]/g;
25005
25444
  /**
25006
- * Clipboard MIME types treated as rich text documents for upload.
25445
+ * Base Book editor font size before zoom scaling.
25007
25446
  *
25008
25447
  * @private Internal utility of `BookEditorMonaco`.
25009
25448
  */
25010
- const RICH_CLIPBOARD_TEXT_MIME_TYPES = new Set(['text/html', 'text/rtf']);
25449
+ const BASE_FONT_SIZE = 20;
25011
25450
  /**
25012
- * Uploaded filename mapping for known rich clipboard MIME types.
25451
+ * Minimum supported Book editor font size after zoom scaling.
25013
25452
  *
25014
25453
  * @private Internal utility of `BookEditorMonaco`.
25015
25454
  */
25016
- const CLIPBOARD_RICH_CONTENT_FILENAMES = {
25017
- 'text/html': 'clipboard-content.html',
25018
- 'text/rtf': 'clipboard-content.rtf',
25019
- 'application/rtf': 'clipboard-content.rtf',
25020
- };
25455
+ const MIN_FONT_SIZE = 8;
25021
25456
  /**
25022
- * Fallback filename used when clipboard MIME type is unknown.
25023
- *
25024
- * @private Internal utility of `BookEditorMonaco`.
25025
- */
25026
- const DEFAULT_CLIPBOARD_RICH_CONTENT_FILENAME = 'clipboard-content.txt';
25027
- /**
25028
- * Local storage key that enables BookEditor Monaco lifecycle debug logs in development.
25029
- *
25030
- * @private Internal utility of `BookEditorMonaco`.
25031
- */
25032
- const BOOK_EDITOR_MONACO_DEBUG_STORAGE_KEY = 'promptbook-debug-book-editor-monaco';
25033
- /**
25034
- * Resolves whether verbose BookEditor Monaco lifecycle logs are enabled.
25457
+ * Default Monaco scrollbar size before zoom scaling.
25035
25458
  *
25036
25459
  * @private Internal utility of `BookEditorMonaco`.
25037
25460
  */
25038
- function isBookEditorMonacoDebugEnabled() {
25039
- if (process.env.NODE_ENV === 'production' || typeof window === 'undefined') {
25040
- return false;
25041
- }
25042
- try {
25043
- return window.localStorage.getItem(BOOK_EDITOR_MONACO_DEBUG_STORAGE_KEY) === '1';
25044
- }
25045
- catch (_a) {
25046
- return false;
25047
- }
25048
- }
25049
- /**
25050
- * Prints one BookEditor Monaco debug line when the dev debug flag is enabled.
25051
- *
25052
- * @param message - Human-readable lifecycle message.
25053
- *
25054
- * @private Internal utility of `BookEditorMonaco`.
25055
- */
25056
- function logBookEditorMonacoDebug(message) {
25057
- if (!isBookEditorMonacoDebugEnabled()) {
25058
- return;
25059
- }
25060
- console.info(`[BookEditorMonaco] ${message}`);
25061
- }
25062
- /**
25063
- * Lists transferable items from a browser `DataTransfer` object.
25064
- *
25065
- * @private Internal utility of `BookEditorMonaco`.
25066
- */
25067
- function listDataTransferItems(dataTransfer) {
25068
- const items = [];
25069
- for (let index = 0; index < dataTransfer.items.length; index++) {
25070
- const item = dataTransfer.items[index];
25071
- if (item) {
25072
- items.push(item);
25073
- }
25074
- }
25075
- return items;
25076
- }
25077
- /**
25078
- * Removes duplicate files by using stable file metadata signature.
25079
- *
25080
- * @private Internal utility of `BookEditorMonaco`.
25081
- */
25082
- function deduplicateFiles(files) {
25083
- const uniqueFiles = new Map();
25084
- for (const file of files) {
25085
- uniqueFiles.set(`${file.name}:${file.type}:${file.size}`, file);
25086
- }
25087
- return Array.from(uniqueFiles.values());
25088
- }
25089
- /**
25090
- * Extracts all file-like clipboard/drop payloads from a `DataTransfer`.
25091
- *
25092
- * @private Internal utility of `BookEditorMonaco`.
25093
- */
25094
- function getDataTransferFiles(dataTransfer) {
25095
- const directFiles = Array.from(dataTransfer.files || []);
25096
- const itemFiles = listDataTransferItems(dataTransfer)
25097
- .filter((item) => item.kind === 'file')
25098
- .map((item) => item.getAsFile())
25099
- .filter((file) => file !== null);
25100
- return deduplicateFiles([...directFiles, ...itemFiles]);
25101
- }
25461
+ const BASE_SCROLLBAR_SIZE = 5;
25102
25462
  /**
25103
- * Picks the richest textual clipboard item that should be uploaded as a document.
25463
+ * Minimum Monaco scrollbar size after zoom scaling.
25104
25464
  *
25105
25465
  * @private Internal utility of `BookEditorMonaco`.
25106
25466
  */
25107
- function getRichClipboardTextItem(dataTransfer) {
25108
- const items = listDataTransferItems(dataTransfer);
25109
- const hasPlainTextItem = items.some((item) => item.kind === 'string' && item.type.toLowerCase() === 'text/plain');
25110
- const applicationItem = items.find((item) => item.kind === 'string' && item.type.toLowerCase().startsWith('application/'));
25111
- if (applicationItem) {
25112
- return applicationItem;
25113
- }
25114
- if (hasPlainTextItem) {
25115
- return null;
25116
- }
25117
- const richTextItem = items.find((item) => {
25118
- if (item.kind !== 'string') {
25119
- return false;
25120
- }
25121
- return RICH_CLIPBOARD_TEXT_MIME_TYPES.has(item.type.toLowerCase());
25122
- });
25123
- if (richTextItem) {
25124
- return richTextItem;
25125
- }
25126
- return null;
25127
- }
25467
+ const MIN_SCROLLBAR_SIZE = 2;
25128
25468
  /**
25129
- * Resolves whether paste should route into upload workflow instead of text insert.
25469
+ * Minimum left padding Monaco reserves for custom line decorations.
25130
25470
  *
25131
25471
  * @private Internal utility of `BookEditorMonaco`.
25132
25472
  */
25133
- function hasUploadableClipboardContent(dataTransfer) {
25134
- if (getDataTransferFiles(dataTransfer).length > 0) {
25135
- return true;
25136
- }
25137
- return getRichClipboardTextItem(dataTransfer) !== null;
25138
- }
25473
+ const MIN_CONTENT_PADDING_LEFT = 8;
25139
25474
  /**
25140
- * Reads string payload from clipboard item.
25475
+ * Creates a hydration-stable CSS class name from React's `useId()` value.
25141
25476
  *
25142
25477
  * @private Internal utility of `BookEditorMonaco`.
25143
25478
  */
25144
- function getClipboardItemString(item) {
25145
- return new Promise((resolve) => {
25146
- try {
25147
- item.getAsString((value) => resolve(value || ''));
25148
- }
25149
- catch (_a) {
25150
- resolve('');
25151
- }
25152
- });
25479
+ function createStableBookEditorInstanceClassName(reactId) {
25480
+ return `book-editor-instance-${reactId.replace(INVALID_CSS_IDENTIFIER_CHARACTER_PATTERN, '-')}`;
25153
25481
  }
25154
25482
  /**
25155
- * Determines filename for generated clipboard rich-content uploads.
25483
+ * Resolves Monaco layout values from the active Book editor zoom level.
25156
25484
  *
25157
25485
  * @private Internal utility of `BookEditorMonaco`.
25158
25486
  */
25159
- function getClipboardRichContentFilename(mimeType) {
25160
- return CLIPBOARD_RICH_CONTENT_FILENAMES[mimeType.toLowerCase()] || DEFAULT_CLIPBOARD_RICH_CONTENT_FILENAME;
25487
+ function createBookEditorMonacoScale(zoomLevel) {
25488
+ return {
25489
+ scaledLineHeight: Math.round(BookEditorMonacoConstants.LINE_HEIGHT * zoomLevel),
25490
+ scaledContentPaddingLeft: Math.max(MIN_CONTENT_PADDING_LEFT, Math.round(BookEditorMonacoConstants.CONTENT_PADDING_LEFT * zoomLevel)),
25491
+ scaledVerticalLineLeft: Math.max(0, Math.round(BookEditorMonacoConstants.VERTICAL_LINE_LEFT * zoomLevel)),
25492
+ scaledFontSize: Math.max(MIN_FONT_SIZE, Math.round(BASE_FONT_SIZE * zoomLevel)),
25493
+ scaledScrollbarSize: Math.max(MIN_SCROLLBAR_SIZE, Math.round(BASE_SCROLLBAR_SIZE * zoomLevel)),
25494
+ };
25161
25495
  }
25162
25496
  /**
25163
- * Creates a hydration-stable CSS class name from React's `useId()` value.
25497
+ * Determines whether the Book editor action bar should be shown.
25164
25498
  *
25165
25499
  * @private Internal utility of `BookEditorMonaco`.
25166
25500
  */
25167
- function createStableBookEditorInstanceClassName(reactId) {
25168
- return `book-editor-instance-${reactId.replace(INVALID_CSS_IDENTIFIER_CHARACTER_PATTERN, '-')}`;
25501
+ function isBookEditorMonacoActionbarVisible({ hoistedMenuItems, isUploadButtonShown, isCameraButtonShown, isDownloadButtonShown, isAboutButtonShown, isFullscreenButtonShown, }) {
25502
+ return (Boolean(hoistedMenuItems && hoistedMenuItems.length > 0) ||
25503
+ Boolean(isUploadButtonShown) ||
25504
+ Boolean(isCameraButtonShown) ||
25505
+ Boolean(isDownloadButtonShown) ||
25506
+ Boolean(isAboutButtonShown) ||
25507
+ Boolean(isFullscreenButtonShown));
25169
25508
  }
25170
25509
  /**
25171
- * Converts clipboard payload into upload-ready files.
25510
+ * Builds the Monaco editor options consumed by `MonacoEditorWithShadowDom`.
25172
25511
  *
25173
25512
  * @private Internal utility of `BookEditorMonaco`.
25174
25513
  */
25175
- async function resolveClipboardUploadFiles(dataTransfer) {
25176
- const files = getDataTransferFiles(dataTransfer);
25177
- if (files.length > 0) {
25178
- return files;
25179
- }
25180
- const richTextItem = getRichClipboardTextItem(dataTransfer);
25181
- if (!richTextItem) {
25182
- return [];
25183
- }
25184
- const content = await getClipboardItemString(richTextItem);
25185
- const mimeType = richTextItem.type || 'text/plain';
25186
- if (!content.trim()) {
25187
- return [];
25188
- }
25189
- return [new File([content], getClipboardRichContentFilename(mimeType), { type: mimeType })];
25514
+ function createBookEditorMonacoOptions({ isReadonly, translations, scaledFontSize, scaledLineHeight, scaledContentPaddingLeft, scaledScrollbarSize, }) {
25515
+ return {
25516
+ readOnly: isReadonly,
25517
+ readOnlyMessage: {
25518
+ value: (translations === null || translations === void 0 ? void 0 : translations.readonlyMessage) || 'You cannot edit this book',
25519
+ },
25520
+ wordWrap: 'on',
25521
+ minimap: { enabled: false },
25522
+ lineNumbers: 'off',
25523
+ fontSize: scaledFontSize,
25524
+ fontFamily: `"Playfair Display", serif`,
25525
+ lineHeight: scaledLineHeight,
25526
+ renderLineHighlight: 'none',
25527
+ lineDecorationsWidth: scaledContentPaddingLeft,
25528
+ glyphMargin: false,
25529
+ folding: false,
25530
+ lineNumbersMinChars: 0,
25531
+ links: true,
25532
+ scrollbar: {
25533
+ vertical: 'auto',
25534
+ horizontal: 'hidden',
25535
+ verticalScrollbarSize: scaledScrollbarSize,
25536
+ arrowSize: 0,
25537
+ useShadows: false,
25538
+ },
25539
+ };
25190
25540
  }
25191
25541
  /**
25192
25542
  * Handles book editor monaco.
@@ -25196,29 +25546,40 @@ async function resolveClipboardUploadFiles(dataTransfer) {
25196
25546
  function BookEditorMonaco(props) {
25197
25547
  const { value, onChange, diagnostics, isReadonly, theme = 'LIGHT', translations, onFileUpload, isUploadButtonShown, isCameraButtonShown, isDownloadButtonShown, isAboutButtonShown = true, isFullscreenButtonShown = true, onFullscreenClick, isFullscreen, zoom = 1, monacoModelPath, hoistedMenuItems, } = props;
25198
25548
  const zoomLevel = zoom;
25199
- const scaledLineHeight = Math.round(BookEditorMonacoConstants.LINE_HEIGHT * zoomLevel);
25200
- const scaledContentPaddingLeft = Math.max(8, Math.round(BookEditorMonacoConstants.CONTENT_PADDING_LEFT * zoomLevel));
25201
- const scaledVerticalLineLeft = Math.max(0, Math.round(BookEditorMonacoConstants.VERTICAL_LINE_LEFT * zoomLevel));
25202
- const baseFontSize = 20;
25203
- const scaledFontSize = Math.max(8, Math.round(baseFontSize * zoomLevel));
25204
- const scaledScrollbarSize = Math.max(2, Math.round(5 * zoomLevel));
25205
- const [isDragOver, setIsDragOver] = useState(false);
25206
- const [editor, setEditor] = useState(null);
25207
- const [isFocused, setIsFocused] = useState(false);
25208
- const [isTouchDevice, setIsTouchDevice] = useState(false);
25209
- const [isSavedShown, setIsSavedShown] = useState(false);
25549
+ const { scaledLineHeight, scaledContentPaddingLeft, scaledVerticalLineLeft, scaledFontSize, scaledScrollbarSize } = createBookEditorMonacoScale(zoomLevel);
25210
25550
  const monaco = useMonaco();
25211
25551
  const reactId = useId();
25212
25552
  const instanceClass = createStableBookEditorInstanceClassName(reactId);
25213
- const touchStartRef = useRef(null);
25214
- const fileUploadInputRef = useRef(null);
25215
- const cameraInputRef = useRef(null);
25553
+ const { editor, isFocused, isTouchDevice, isSavedShown, handleBeforeMonacoMount, handleMonacoMount } = useBookEditorMonacoLifecycle({
25554
+ monaco,
25555
+ theme,
25556
+ });
25216
25557
  const { activeUploadItems, uploadStats, pauseUpload, resumeUpload, handleFiles } = useBookEditorMonacoUploads({
25217
25558
  editor,
25218
25559
  monaco,
25219
25560
  onFileUpload,
25220
25561
  });
25562
+ const { isDragOver, fileUploadInputRef, cameraInputRef, handleDrop, handlePaste, handleUploadDocument, handleTakePhoto, handleFileInputChange, handleDragOver, handleDragEnter, handleDragLeave, focusOverlayTouchHandlers, } = useBookEditorMonacoInteractions({
25563
+ editor,
25564
+ handleFiles,
25565
+ });
25221
25566
  const combinedDiagnostics = [...(diagnostics || []), ...createDeprecatedCommitmentDiagnostics(value)];
25567
+ const isActionBarVisible = isBookEditorMonacoActionbarVisible({
25568
+ hoistedMenuItems,
25569
+ isUploadButtonShown,
25570
+ isCameraButtonShown,
25571
+ isDownloadButtonShown,
25572
+ isAboutButtonShown,
25573
+ isFullscreenButtonShown,
25574
+ });
25575
+ const monacoOptions = createBookEditorMonacoOptions({
25576
+ isReadonly,
25577
+ translations,
25578
+ scaledFontSize,
25579
+ scaledLineHeight,
25580
+ scaledContentPaddingLeft,
25581
+ scaledScrollbarSize,
25582
+ });
25222
25583
  useBookEditorMonacoLanguage({ monaco, theme });
25223
25584
  useBookEditorMonacoDiagnostics({ monaco, editor, diagnostics: combinedDiagnostics });
25224
25585
  useBookEditorMonacoDecorations({ editor, monaco });
@@ -25230,171 +25591,6 @@ function BookEditorMonaco(props) {
25230
25591
  zoomLevel,
25231
25592
  theme,
25232
25593
  });
25233
- /**
25234
- * Re-applies Book language + theme to the currently mounted Monaco model.
25235
- */
25236
- const reapplyBookLanguageAndTheme = useCallback((reason) => {
25237
- if (!editor || !monaco) {
25238
- return;
25239
- }
25240
- ensureBookEditorMonacoLanguageForEditor({ monaco, monacoEditor: editor, theme });
25241
- logBookEditorMonacoDebug(`Re-applied Book Monaco language/theme (${reason}).`);
25242
- }, [editor, monaco, theme]);
25243
- useEffect(() => {
25244
- setIsTouchDevice(typeof window !== 'undefined' && window.matchMedia('(pointer: coarse)').matches);
25245
- }, []);
25246
- useEffect(() => {
25247
- if (!editor || !monaco) {
25248
- return;
25249
- }
25250
- const focusListener = editor.onDidFocusEditorWidget(() => {
25251
- setIsFocused(true);
25252
- reapplyBookLanguageAndTheme('focus');
25253
- });
25254
- const blurListener = editor.onDidBlurEditorWidget(() => {
25255
- setIsFocused(false);
25256
- });
25257
- const saveAction = editor.addAction({
25258
- id: 'save-book',
25259
- label: 'Save',
25260
- keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS],
25261
- run: () => {
25262
- setIsSavedShown(false);
25263
- setTimeout(() => setIsSavedShown(true), 0);
25264
- // Note: We don't prevent default, so browser's save dialog still opens
25265
- },
25266
- });
25267
- return () => {
25268
- focusListener.dispose();
25269
- blurListener.dispose();
25270
- saveAction.dispose();
25271
- };
25272
- }, [editor, monaco, reapplyBookLanguageAndTheme]);
25273
- useEffect(() => {
25274
- reapplyBookLanguageAndTheme('editor-ready');
25275
- }, [reapplyBookLanguageAndTheme]);
25276
- useEffect(() => {
25277
- if (!editor || !monaco) {
25278
- return;
25279
- }
25280
- const handlePopState = () => {
25281
- reapplyBookLanguageAndTheme('history-popstate');
25282
- };
25283
- const handlePageShow = () => {
25284
- reapplyBookLanguageAndTheme('pageshow');
25285
- };
25286
- const handleWindowFocus = () => {
25287
- reapplyBookLanguageAndTheme('window-focus');
25288
- };
25289
- const handleVisibilityChange = () => {
25290
- if (document.visibilityState === 'visible') {
25291
- reapplyBookLanguageAndTheme('visibility-visible');
25292
- }
25293
- };
25294
- window.addEventListener('popstate', handlePopState);
25295
- window.addEventListener('pageshow', handlePageShow);
25296
- window.addEventListener('focus', handleWindowFocus);
25297
- document.addEventListener('visibilitychange', handleVisibilityChange);
25298
- return () => {
25299
- window.removeEventListener('popstate', handlePopState);
25300
- window.removeEventListener('pageshow', handlePageShow);
25301
- window.removeEventListener('focus', handleWindowFocus);
25302
- document.removeEventListener('visibilitychange', handleVisibilityChange);
25303
- };
25304
- }, [editor, monaco, reapplyBookLanguageAndTheme]);
25305
- useEffect(() => {
25306
- if (!isSavedShown) {
25307
- return;
25308
- }
25309
- const timer = setTimeout(() => {
25310
- setIsSavedShown(false);
25311
- }, 2000);
25312
- return () => {
25313
- clearTimeout(timer);
25314
- };
25315
- }, [isSavedShown]);
25316
- const handleDrop = useCallback(async (event) => {
25317
- event.preventDefault();
25318
- setIsDragOver(false);
25319
- const files = getDataTransferFiles(event.dataTransfer);
25320
- await handleFiles(files);
25321
- }, [handleFiles]);
25322
- const handlePaste = useCallback(async (event) => {
25323
- const clipboardData = event.clipboardData;
25324
- if (!hasUploadableClipboardContent(clipboardData)) {
25325
- return;
25326
- }
25327
- event.preventDefault();
25328
- event.stopPropagation();
25329
- const files = await resolveClipboardUploadFiles(clipboardData);
25330
- await handleFiles(files);
25331
- }, [handleFiles]);
25332
- const handleUploadDocument = useCallback(() => {
25333
- var _a;
25334
- (_a = fileUploadInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
25335
- }, []);
25336
- const handleTakePhoto = useCallback(() => {
25337
- var _a;
25338
- (_a = cameraInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
25339
- }, []);
25340
- const handleFileInputChange = useCallback((event) => {
25341
- const files = Array.from(event.target.files || []);
25342
- void handleFiles(files);
25343
- event.target.value = '';
25344
- }, [handleFiles]);
25345
- /**
25346
- * Ensures Book language/tokenizer is ready before Monaco creates the editor model.
25347
- */
25348
- const handleBeforeMonacoMount = useCallback((beforeMountMonaco) => {
25349
- ensureBookEditorMonacoLanguage(beforeMountMonaco, theme);
25350
- }, [theme]);
25351
- /**
25352
- * Re-applies Book language/theme once Monaco editor is mounted.
25353
- */
25354
- const handleMonacoMount = useCallback((mountedEditor, mountedMonaco) => {
25355
- setEditor(mountedEditor);
25356
- ensureBookEditorMonacoLanguageForEditor({ monaco: mountedMonaco, monacoEditor: mountedEditor, theme });
25357
- logBookEditorMonacoDebug('Mounted Monaco editor and re-applied Book language/theme.');
25358
- }, [theme]);
25359
- const handleDragOver = useCallback((event) => {
25360
- event.preventDefault();
25361
- setIsDragOver(true);
25362
- }, []);
25363
- const handleDragEnter = useCallback((event) => {
25364
- event.preventDefault();
25365
- setIsDragOver(true);
25366
- }, []);
25367
- const handleDragLeave = useCallback((event) => {
25368
- event.preventDefault();
25369
- setIsDragOver(false);
25370
- }, []);
25371
- const focusOverlayTouchHandlers = {
25372
- onTouchStart: (event) => {
25373
- const touch = event.touches[0];
25374
- if (touch) {
25375
- touchStartRef.current = { x: touch.clientX, y: touch.clientY };
25376
- }
25377
- },
25378
- onTouchEnd: (event) => {
25379
- event.preventDefault();
25380
- const touch = event.changedTouches[0];
25381
- if (touch && touchStartRef.current) {
25382
- const deltaX = Math.abs(touch.clientX - touchStartRef.current.x);
25383
- const deltaY = Math.abs(touch.clientY - touchStartRef.current.y);
25384
- const threshold = 10;
25385
- if (deltaX < threshold && deltaY < threshold) {
25386
- editor === null || editor === void 0 ? void 0 : editor.focus();
25387
- }
25388
- }
25389
- touchStartRef.current = null;
25390
- },
25391
- };
25392
- const isActionBarVisible = Boolean(hoistedMenuItems && hoistedMenuItems.length > 0) ||
25393
- isUploadButtonShown ||
25394
- isCameraButtonShown ||
25395
- isDownloadButtonShown ||
25396
- isAboutButtonShown ||
25397
- isFullscreenButtonShown;
25398
25594
  return (jsxs("div", { className: classNames(styles$d.bookEditorContainer, instanceClass), onDrop: handleDrop, onPaste: handlePaste, onDragOver: handleDragOver, onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, children: [isActionBarVisible && (jsx(BookEditorActionbar, { value,
25399
25595
  isUploadButtonShown,
25400
25596
  isCameraButtonShown: isCameraButtonShown !== null && isCameraButtonShown !== void 0 ? isCameraButtonShown : isTouchDevice,
@@ -25420,31 +25616,7 @@ function BookEditorMonaco(props) {
25420
25616
  height: '100%',
25421
25617
  width: '100%',
25422
25618
  backgroundColor: 'transparent',
25423
- }, ...focusOverlayTouchHandlers })), jsx(MonacoEditorWithShadowDom, { language: BookEditorMonacoConstants.BOOK_LANGUAGE_ID, theme: BookEditorMonacoConstants.BOOK_THEME_ID, path: monacoModelPath, saveViewState: Boolean(monacoModelPath), value: value, beforeMount: handleBeforeMonacoMount, onMount: handleMonacoMount, onChange: (newValue) => onChange === null || onChange === void 0 ? void 0 : onChange(newValue), options: {
25424
- readOnly: isReadonly,
25425
- readOnlyMessage: {
25426
- value: (translations === null || translations === void 0 ? void 0 : translations.readonlyMessage) || 'You cannot edit this book',
25427
- },
25428
- wordWrap: 'on',
25429
- minimap: { enabled: false },
25430
- lineNumbers: 'off',
25431
- fontSize: scaledFontSize,
25432
- fontFamily: `"Playfair Display", serif`,
25433
- lineHeight: scaledLineHeight,
25434
- renderLineHighlight: 'none',
25435
- lineDecorationsWidth: scaledContentPaddingLeft,
25436
- glyphMargin: false,
25437
- folding: false,
25438
- lineNumbersMinChars: 0,
25439
- links: true,
25440
- scrollbar: {
25441
- vertical: 'auto',
25442
- horizontal: 'hidden',
25443
- verticalScrollbarSize: scaledScrollbarSize,
25444
- arrowSize: 0,
25445
- useShadows: false,
25446
- },
25447
- }, loading: jsx("div", { className: styles$d.loading, children: "\uD83D\uDCD6" }) })] })] }));
25619
+ }, ...focusOverlayTouchHandlers })), jsx(MonacoEditorWithShadowDom, { language: BookEditorMonacoConstants.BOOK_LANGUAGE_ID, theme: BookEditorMonacoConstants.BOOK_THEME_ID, path: monacoModelPath, saveViewState: Boolean(monacoModelPath), value: value, beforeMount: handleBeforeMonacoMount, onMount: handleMonacoMount, onChange: (newValue) => onChange === null || onChange === void 0 ? void 0 : onChange(newValue), options: monacoOptions, loading: jsx("div", { className: styles$d.loading, children: "\uD83D\uDCD6" }) })] })] }));
25448
25620
  }
25449
25621
 
25450
25622
  /**
@@ -36204,7 +36376,7 @@ function createExampleInteractionsContent(parseResult, samples) {
36204
36376
  if (examples.length === 0) {
36205
36377
  return null;
36206
36378
  }
36207
- return `Example interaction:\n\n${examples.join('\n\n')}`;
36379
+ return `## Sample of communication with the agent:\n\n${examples.join('\n\n')}`;
36208
36380
  }
36209
36381
  /**
36210
36382
  * Collects the individual lines used in the example interaction section.
@@ -36220,11 +36392,11 @@ function collectExampleInteractionLines(parseResult, samples) {
36220
36392
  const examples = [];
36221
36393
  const initialMessage = (_a = parseResult.commitments.find((commitment) => commitment.type === 'INITIAL MESSAGE')) === null || _a === void 0 ? void 0 : _a.content;
36222
36394
  if (initialMessage) {
36223
- examples.push(`Agent: ${initialMessage}`);
36395
+ examples.push(`**Agent:**\n${initialMessage}`);
36224
36396
  }
36225
36397
  if (samples && samples.length > 0) {
36226
36398
  for (const sample of samples) {
36227
- examples.push(`User: ${sample.question}\nAgent: ${sample.answer}`);
36399
+ examples.push(`**User:** ${sample.question}\n\n**Agent:**\n${sample.answer}`);
36228
36400
  }
36229
36401
  }
36230
36402
  return examples;
@@ -41061,8 +41233,8 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
41061
41233
  * Prepares an AgentKit agent with optional knowledge sources and tool definitions.
41062
41234
  */
41063
41235
  async prepareAgentKitAgent(options) {
41064
- var _a, _b;
41065
- const { name, instructions, knowledgeSources, tools, vectorStoreId: cachedVectorStoreId, storeAsPrepared, } = options;
41236
+ var _a, _b, _c;
41237
+ const { name, instructions, knowledgeSources, tools, nativeAgentKitTools, vectorStoreId: cachedVectorStoreId, storeAsPrepared, } = options;
41066
41238
  await this.ensureAgentKitDefaults();
41067
41239
  if (this.options.isVerbose) {
41068
41240
  console.info('[🤰]', 'Preparing OpenAI AgentKit agent', {
@@ -41070,6 +41242,7 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
41070
41242
  instructionsLength: instructions.length,
41071
41243
  knowledgeSourcesCount: (_a = knowledgeSources === null || knowledgeSources === void 0 ? void 0 : knowledgeSources.length) !== null && _a !== void 0 ? _a : 0,
41072
41244
  toolsCount: (_b = tools === null || tools === void 0 ? void 0 : tools.length) !== null && _b !== void 0 ? _b : 0,
41245
+ nativeAgentKitToolsCount: (_c = nativeAgentKitTools === null || nativeAgentKitTools === void 0 ? void 0 : nativeAgentKitTools.length) !== null && _c !== void 0 ? _c : 0,
41073
41246
  });
41074
41247
  }
41075
41248
  let vectorStoreId = cachedVectorStoreId;
@@ -41088,7 +41261,7 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
41088
41261
  vectorStoreId,
41089
41262
  });
41090
41263
  }
41091
- const agentKitTools = this.buildAgentKitTools({ tools, vectorStoreId });
41264
+ const agentKitTools = this.buildAgentKitTools({ tools, nativeAgentKitTools, vectorStoreId });
41092
41265
  const openAiAgentKitAgent = new Agent$1({
41093
41266
  name,
41094
41267
  model: this.agentKitModelName,
@@ -41127,11 +41300,14 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
41127
41300
  * Builds the tool list for AgentKit, including hosted file search when applicable.
41128
41301
  */
41129
41302
  buildAgentKitTools(options) {
41130
- const { tools, vectorStoreId } = options;
41303
+ const { tools, nativeAgentKitTools, vectorStoreId } = options;
41131
41304
  const agentKitTools = [];
41132
41305
  if (vectorStoreId) {
41133
41306
  agentKitTools.push(fileSearchTool(vectorStoreId));
41134
41307
  }
41308
+ if (nativeAgentKitTools && nativeAgentKitTools.length > 0) {
41309
+ agentKitTools.push(...nativeAgentKitTools);
41310
+ }
41135
41311
  if (tools && tools.length > 0) {
41136
41312
  let scriptTools = null;
41137
41313
  for (const toolDefinition of tools) {