@promptbook/wizard 0.112.0-11 → 0.112.0-12

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 (75) hide show
  1. package/esm/index.es.js +1407 -159
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/src/book-2.0/agent-source/AgentBasicInformation.d.ts +1 -1
  4. package/esm/src/book-components/Chat/Chat/ChatToolCallModal.test.d.ts +2 -0
  5. package/esm/src/book-components/Chat/Chat/renderToolCallDetails.d.ts +4 -0
  6. package/esm/src/book-components/Chat/utils/getToolCallChipletInfo.timeout.test.d.ts +1 -0
  7. package/esm/src/book-components/Chat/utils/timeoutToolCallPresentation.d.ts +123 -0
  8. package/esm/src/book-components/Chat/utils/timeoutToolCallPresentation.test.d.ts +1 -0
  9. package/esm/src/commitments/USE_CALENDAR/USE_CALENDAR.d.ts +42 -0
  10. package/esm/src/commitments/USE_CALENDAR/USE_CALENDAR.test.d.ts +1 -0
  11. package/esm/src/commitments/USE_CALENDAR/UseCalendarToolNames.d.ts +13 -0
  12. package/esm/src/commitments/USE_CALENDAR/UseCalendarWallet.d.ts +9 -0
  13. package/esm/src/commitments/USE_CALENDAR/calendarReference.d.ts +68 -0
  14. package/esm/src/commitments/USE_CALENDAR/callGoogleCalendarApi.d.ts +19 -0
  15. package/esm/src/commitments/USE_CALENDAR/createUseCalendarToolFunctions.d.ts +8 -0
  16. package/esm/src/commitments/USE_CALENDAR/createUseCalendarTools.d.ts +7 -0
  17. package/esm/src/commitments/USE_CALENDAR/getUseCalendarToolTitles.d.ts +7 -0
  18. package/esm/src/commitments/USE_CALENDAR/normalizeConfiguredCalendars.d.ts +19 -0
  19. package/esm/src/commitments/USE_CALENDAR/resolveUseCalendarToolRuntimeOrWalletCredentialResult.d.ts +34 -0
  20. package/esm/src/commitments/_common/toolRuntimeContext.d.ts +9 -0
  21. package/esm/src/commitments/index.d.ts +2 -1
  22. package/esm/src/executables/apps/locateVscode.d.ts +7 -0
  23. package/esm/src/executables/apps/locateVscode.test.d.ts +1 -0
  24. package/esm/src/executables/browsers/locateBrowser.d.ts +10 -0
  25. package/esm/src/executables/browsers/locateBrowser.test.d.ts +1 -0
  26. package/esm/src/executables/browsers/locateChrome.d.ts +7 -0
  27. package/esm/src/executables/browsers/locateChrome.test.d.ts +1 -0
  28. package/esm/src/executables/browsers/locateDefaultSystemBrowser.d.ts +10 -0
  29. package/esm/src/executables/browsers/locateDefaultSystemBrowser.test.d.ts +1 -0
  30. package/esm/src/executables/browsers/locateEdge.d.ts +7 -0
  31. package/esm/src/executables/browsers/locateEdge.test.d.ts +1 -0
  32. package/esm/src/executables/browsers/locateFirefox.d.ts +7 -0
  33. package/esm/src/executables/browsers/locateFirefox.test.d.ts +1 -0
  34. package/esm/src/executables/browsers/locateInternetExplorer.d.ts +7 -0
  35. package/esm/src/executables/browsers/locateInternetExplorer.test.d.ts +1 -0
  36. package/esm/src/executables/browsers/locateSafari.d.ts +7 -0
  37. package/esm/src/version.d.ts +1 -1
  38. package/package.json +2 -2
  39. package/umd/index.umd.js +1412 -163
  40. package/umd/index.umd.js.map +1 -1
  41. package/umd/src/book-2.0/agent-source/AgentBasicInformation.d.ts +1 -1
  42. package/umd/src/book-components/Chat/Chat/ChatToolCallModal.test.d.ts +2 -0
  43. package/umd/src/book-components/Chat/Chat/renderToolCallDetails.d.ts +4 -0
  44. package/umd/src/book-components/Chat/utils/getToolCallChipletInfo.timeout.test.d.ts +1 -0
  45. package/umd/src/book-components/Chat/utils/timeoutToolCallPresentation.d.ts +123 -0
  46. package/umd/src/book-components/Chat/utils/timeoutToolCallPresentation.test.d.ts +1 -0
  47. package/umd/src/commitments/USE_CALENDAR/USE_CALENDAR.d.ts +42 -0
  48. package/umd/src/commitments/USE_CALENDAR/USE_CALENDAR.test.d.ts +1 -0
  49. package/umd/src/commitments/USE_CALENDAR/UseCalendarToolNames.d.ts +13 -0
  50. package/umd/src/commitments/USE_CALENDAR/UseCalendarWallet.d.ts +9 -0
  51. package/umd/src/commitments/USE_CALENDAR/calendarReference.d.ts +68 -0
  52. package/umd/src/commitments/USE_CALENDAR/callGoogleCalendarApi.d.ts +19 -0
  53. package/umd/src/commitments/USE_CALENDAR/createUseCalendarToolFunctions.d.ts +8 -0
  54. package/umd/src/commitments/USE_CALENDAR/createUseCalendarTools.d.ts +7 -0
  55. package/umd/src/commitments/USE_CALENDAR/getUseCalendarToolTitles.d.ts +7 -0
  56. package/umd/src/commitments/USE_CALENDAR/normalizeConfiguredCalendars.d.ts +19 -0
  57. package/umd/src/commitments/USE_CALENDAR/resolveUseCalendarToolRuntimeOrWalletCredentialResult.d.ts +34 -0
  58. package/umd/src/commitments/_common/toolRuntimeContext.d.ts +9 -0
  59. package/umd/src/commitments/index.d.ts +2 -1
  60. package/umd/src/executables/apps/locateVscode.d.ts +7 -0
  61. package/umd/src/executables/apps/locateVscode.test.d.ts +1 -0
  62. package/umd/src/executables/browsers/locateBrowser.d.ts +10 -0
  63. package/umd/src/executables/browsers/locateBrowser.test.d.ts +1 -0
  64. package/umd/src/executables/browsers/locateChrome.d.ts +7 -0
  65. package/umd/src/executables/browsers/locateChrome.test.d.ts +1 -0
  66. package/umd/src/executables/browsers/locateDefaultSystemBrowser.d.ts +10 -0
  67. package/umd/src/executables/browsers/locateDefaultSystemBrowser.test.d.ts +1 -0
  68. package/umd/src/executables/browsers/locateEdge.d.ts +7 -0
  69. package/umd/src/executables/browsers/locateEdge.test.d.ts +1 -0
  70. package/umd/src/executables/browsers/locateFirefox.d.ts +7 -0
  71. package/umd/src/executables/browsers/locateFirefox.test.d.ts +1 -0
  72. package/umd/src/executables/browsers/locateInternetExplorer.d.ts +7 -0
  73. package/umd/src/executables/browsers/locateInternetExplorer.test.d.ts +1 -0
  74. package/umd/src/executables/browsers/locateSafari.d.ts +7 -0
  75. package/umd/src/version.d.ts +1 -1
package/esm/index.es.js CHANGED
@@ -16,7 +16,6 @@ import { Converter } from 'showdown';
16
16
  import { randomBytes, randomUUID } from 'crypto';
17
17
  import { tmpdir } from 'os';
18
18
  import { ConfigChecker } from 'configchecker';
19
- import { locateChrome } from 'locate-app';
20
19
  import { chromium } from 'playwright';
21
20
  import * as dotenv from 'dotenv';
22
21
  import sha256 from 'crypto-js/sha256';
@@ -42,7 +41,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
42
41
  * @generated
43
42
  * @see https://github.com/webgptorg/promptbook
44
43
  */
45
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-11';
44
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-12';
46
45
  /**
47
46
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
48
47
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -23428,6 +23427,1232 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
23428
23427
  * Note: [💞] Ignore a discrepancy between file name and entity name
23429
23428
  */
23430
23429
 
23430
+ /**
23431
+ * Base Google Calendar API URL.
23432
+ *
23433
+ * @private constant of callGoogleCalendarApi
23434
+ */
23435
+ const GOOGLE_CALENDAR_API_BASE_URL = 'https://www.googleapis.com/calendar/v3';
23436
+ /**
23437
+ * Runs one Google Calendar API request and parses JSON response payload.
23438
+ *
23439
+ * @private function of UseCalendarCommitmentDefinition
23440
+ */
23441
+ async function callGoogleCalendarApi(accessToken, options) {
23442
+ const url = new URL(options.path, GOOGLE_CALENDAR_API_BASE_URL);
23443
+ if (options.query) {
23444
+ for (const [key, value] of Object.entries(options.query)) {
23445
+ if (value && value.trim()) {
23446
+ url.searchParams.set(key, value);
23447
+ }
23448
+ }
23449
+ }
23450
+ const response = await fetch(url.toString(), {
23451
+ method: options.method,
23452
+ headers: {
23453
+ Authorization: `Bearer ${accessToken}`,
23454
+ Accept: 'application/json',
23455
+ 'Content-Type': 'application/json',
23456
+ },
23457
+ body: options.body ? JSON.stringify(options.body) : undefined,
23458
+ });
23459
+ const textPayload = await response.text();
23460
+ const parsedPayload = tryParseJson$2(textPayload);
23461
+ if (options.allowNotFound && response.status === 404) {
23462
+ return null;
23463
+ }
23464
+ if (!response.ok) {
23465
+ throw new Error(spaceTrim$1(`
23466
+ Google Calendar API request failed (${response.status} ${response.statusText}):
23467
+ ${extractGoogleCalendarApiErrorMessage(parsedPayload, textPayload)}
23468
+ `));
23469
+ }
23470
+ return parsedPayload;
23471
+ }
23472
+ /**
23473
+ * Parses raw text into JSON when possible.
23474
+ *
23475
+ * @private function of callGoogleCalendarApi
23476
+ */
23477
+ function tryParseJson$2(rawText) {
23478
+ if (!rawText.trim()) {
23479
+ return {};
23480
+ }
23481
+ try {
23482
+ return JSON.parse(rawText);
23483
+ }
23484
+ catch (_a) {
23485
+ return rawText;
23486
+ }
23487
+ }
23488
+ /**
23489
+ * Extracts a user-friendly Google Calendar API error message.
23490
+ *
23491
+ * @private function of callGoogleCalendarApi
23492
+ */
23493
+ function extractGoogleCalendarApiErrorMessage(parsedPayload, fallbackText) {
23494
+ if (parsedPayload && typeof parsedPayload === 'object') {
23495
+ const payload = parsedPayload;
23496
+ const errorPayload = payload.error;
23497
+ if (errorPayload && typeof errorPayload === 'object') {
23498
+ const normalizedErrorPayload = errorPayload;
23499
+ const message = typeof normalizedErrorPayload.message === 'string' ? normalizedErrorPayload.message : '';
23500
+ const errors = Array.isArray(normalizedErrorPayload.errors) ? normalizedErrorPayload.errors : [];
23501
+ const flattenedErrors = errors
23502
+ .map((errorEntry) => {
23503
+ if (!errorEntry || typeof errorEntry !== 'object') {
23504
+ return '';
23505
+ }
23506
+ const normalizedErrorEntry = errorEntry;
23507
+ const detailMessage = typeof normalizedErrorEntry.message === 'string' ? normalizedErrorEntry.message : '';
23508
+ const reason = typeof normalizedErrorEntry.reason === 'string' ? normalizedErrorEntry.reason : '';
23509
+ return [detailMessage, reason].filter(Boolean).join(' | ');
23510
+ })
23511
+ .filter(Boolean);
23512
+ if (message || flattenedErrors.length > 0) {
23513
+ return [message, ...flattenedErrors].filter(Boolean).join(' | ');
23514
+ }
23515
+ }
23516
+ }
23517
+ return fallbackText || 'Unknown Google Calendar API error';
23518
+ }
23519
+
23520
+ /**
23521
+ * Hostnames accepted for Google Calendar references.
23522
+ *
23523
+ * @private internal USE CALENDAR constant
23524
+ */
23525
+ const GOOGLE_CALENDAR_HOSTNAMES = new Set(['calendar.google.com', 'www.calendar.google.com']);
23526
+ /**
23527
+ * Default Google Calendar OAuth scopes when commitment content does not list any.
23528
+ *
23529
+ * @private internal USE CALENDAR constant
23530
+ */
23531
+ const DEFAULT_GOOGLE_CALENDAR_SCOPES = ['https://www.googleapis.com/auth/calendar'];
23532
+ /**
23533
+ * Parses one Google Calendar URL/reference into canonical details.
23534
+ *
23535
+ * Supported input forms:
23536
+ * - `https://calendar.google.com/...`
23537
+ * - `calendar.google.com/...`
23538
+ *
23539
+ * @private internal utility of USE CALENDAR commitment
23540
+ */
23541
+ function parseGoogleCalendarReference(rawReference) {
23542
+ const trimmedReference = rawReference.trim();
23543
+ if (!trimmedReference) {
23544
+ return null;
23545
+ }
23546
+ const normalizedReference = trimmedReference.startsWith('calendar.google.com/')
23547
+ ? `https://${trimmedReference}`
23548
+ : trimmedReference;
23549
+ let parsedUrl;
23550
+ try {
23551
+ parsedUrl = new URL(normalizedReference);
23552
+ }
23553
+ catch (_a) {
23554
+ return null;
23555
+ }
23556
+ if (!GOOGLE_CALENDAR_HOSTNAMES.has(parsedUrl.hostname.toLowerCase())) {
23557
+ return null;
23558
+ }
23559
+ parsedUrl.protocol = 'https:';
23560
+ parsedUrl.hash = '';
23561
+ return {
23562
+ provider: 'google',
23563
+ url: parsedUrl.toString(),
23564
+ calendarId: parseGoogleCalendarIdFromUrl(parsedUrl) || 'primary',
23565
+ scopes: [...DEFAULT_GOOGLE_CALENDAR_SCOPES],
23566
+ };
23567
+ }
23568
+ /**
23569
+ * Parses `USE CALENDAR` commitment content into calendar reference + optional instructions.
23570
+ *
23571
+ * @private internal utility of USE CALENDAR commitment
23572
+ */
23573
+ function parseUseCalendarCommitmentContent(content) {
23574
+ const trimmedContent = spaceTrim$1(content);
23575
+ if (!trimmedContent) {
23576
+ return {
23577
+ calendar: null,
23578
+ calendarUrlRaw: null,
23579
+ instructions: '',
23580
+ };
23581
+ }
23582
+ const lines = trimmedContent
23583
+ .split(/\r?\n/)
23584
+ .map((line) => line.trim())
23585
+ .filter(Boolean);
23586
+ if (lines.length === 0) {
23587
+ return {
23588
+ calendar: null,
23589
+ calendarUrlRaw: null,
23590
+ instructions: '',
23591
+ };
23592
+ }
23593
+ let calendarReferenceRaw = null;
23594
+ let calendarReference = null;
23595
+ const firstLine = lines[0] || '';
23596
+ const firstLineTokens = firstLine.split(/\s+/).filter(Boolean);
23597
+ for (const token of firstLineTokens) {
23598
+ const cleanedToken = token.replace(/[),.;:!?]+$/g, '');
23599
+ const parsedReference = parseGoogleCalendarReference(cleanedToken);
23600
+ if (!parsedReference) {
23601
+ continue;
23602
+ }
23603
+ calendarReferenceRaw = cleanedToken;
23604
+ calendarReference = parsedReference;
23605
+ break;
23606
+ }
23607
+ if (!calendarReference) {
23608
+ const firstUrl = findFirstUrlToken(trimmedContent);
23609
+ if (firstUrl) {
23610
+ calendarReferenceRaw = firstUrl;
23611
+ calendarReference = parseGoogleCalendarReference(firstUrl);
23612
+ }
23613
+ }
23614
+ const scopes = extractGoogleCalendarScopes(trimmedContent);
23615
+ if (calendarReference) {
23616
+ calendarReference = {
23617
+ ...calendarReference,
23618
+ scopes: scopes.length > 0 ? scopes : [...DEFAULT_GOOGLE_CALENDAR_SCOPES],
23619
+ tokenRef: extractTokenRef(trimmedContent),
23620
+ };
23621
+ }
23622
+ const instructionLines = [...lines];
23623
+ if (instructionLines.length > 0 && calendarReferenceRaw) {
23624
+ instructionLines[0] = removeTokenFromLine(instructionLines[0] || '', calendarReferenceRaw);
23625
+ }
23626
+ const filteredInstructionLines = instructionLines.filter((line) => !/^\s*SCOPES?\s+/i.test(line) && !line.trim().startsWith('https://www.googleapis.com/auth/'));
23627
+ return {
23628
+ calendar: calendarReference,
23629
+ calendarUrlRaw: calendarReferenceRaw,
23630
+ instructions: filteredInstructionLines.join('\n').trim(),
23631
+ };
23632
+ }
23633
+ /**
23634
+ * Attempts to resolve one concrete Google Calendar id from URL.
23635
+ *
23636
+ * @private internal utility of USE CALENDAR commitment
23637
+ */
23638
+ function parseGoogleCalendarIdFromUrl(url) {
23639
+ const rawCalendarId = url.searchParams.get('cid') || url.searchParams.get('src') || url.searchParams.get('calendarId');
23640
+ if (!rawCalendarId) {
23641
+ return null;
23642
+ }
23643
+ const decodedCalendarId = decodeURIComponent(rawCalendarId).trim();
23644
+ return decodedCalendarId || null;
23645
+ }
23646
+ /**
23647
+ * Finds the first URL-looking token in text.
23648
+ *
23649
+ * @private utility of USE CALENDAR commitment
23650
+ */
23651
+ function findFirstUrlToken(text) {
23652
+ const match = text.match(/https?:\/\/[^\s)]+/i);
23653
+ return match ? match[0] || null : null;
23654
+ }
23655
+ /**
23656
+ * Extracts Google Calendar OAuth scopes declared in commitment text.
23657
+ *
23658
+ * @private utility of USE CALENDAR commitment
23659
+ */
23660
+ function extractGoogleCalendarScopes(content) {
23661
+ const scopesFromUrls = content.match(/https:\/\/www\.googleapis\.com\/auth\/[A-Za-z0-9._/-]+/gim) || [];
23662
+ const scopesFromKeywordLines = content
23663
+ .split(/\r?\n/)
23664
+ .map((line) => line.trim())
23665
+ .filter((line) => /^\s*SCOPES?\s+/i.test(line))
23666
+ .flatMap((line) => line
23667
+ .replace(/^\s*SCOPES?\s+/i, '')
23668
+ .split(/[,\s]+/)
23669
+ .map((scope) => scope.trim())
23670
+ .filter(Boolean))
23671
+ .filter((scope) => scope.startsWith('https://www.googleapis.com/auth/'));
23672
+ const uniqueScopes = new Set();
23673
+ for (const scope of [...scopesFromUrls, ...scopesFromKeywordLines]) {
23674
+ uniqueScopes.add(scope);
23675
+ }
23676
+ return [...uniqueScopes];
23677
+ }
23678
+ /**
23679
+ * Extracts optional token reference marker from commitment text.
23680
+ *
23681
+ * @private utility of USE CALENDAR commitment
23682
+ */
23683
+ function extractTokenRef(content) {
23684
+ var _a;
23685
+ const tokenRefMatch = content.match(/@[\w.-]+/);
23686
+ const tokenRef = (_a = tokenRefMatch === null || tokenRefMatch === void 0 ? void 0 : tokenRefMatch[0]) === null || _a === void 0 ? void 0 : _a.trim();
23687
+ return tokenRef || undefined;
23688
+ }
23689
+ /**
23690
+ * Removes one specific token from one instruction line.
23691
+ *
23692
+ * @private utility of USE CALENDAR commitment
23693
+ */
23694
+ function removeTokenFromLine(line, token) {
23695
+ const tokens = line.split(/\s+/).filter(Boolean);
23696
+ const filteredTokens = tokens.filter((lineToken) => lineToken.replace(/[),.;:!?]+$/g, '') !== token);
23697
+ return filteredTokens.join(' ').trim();
23698
+ }
23699
+ /**
23700
+ * Note: [💞] Ignore a discrepancy between file name and entity name
23701
+ */
23702
+
23703
+ /**
23704
+ * Wallet metadata used by USE CALENDAR when resolving Google Calendar credentials.
23705
+ *
23706
+ * @private constant of UseCalendarCommitmentDefinition
23707
+ */
23708
+ const UseCalendarWallet = {
23709
+ service: 'google_calendar',
23710
+ key: 'use-calendar-google-token',
23711
+ };
23712
+
23713
+ /**
23714
+ * Internal error used to signal missing wallet credentials.
23715
+ *
23716
+ * @private class of resolveUseCalendarToolRuntimeOrWalletCredentialResult
23717
+ */
23718
+ class CalendarWalletCredentialRequiredError extends Error {
23719
+ constructor(options) {
23720
+ super('Google Calendar token is missing in wallet. Request it from user and store as ACCESS_TOKEN (service google_calendar, key use-calendar-google-token).');
23721
+ this.name = 'CalendarWalletCredentialRequiredError';
23722
+ this.service = UseCalendarWallet.service;
23723
+ this.key = UseCalendarWallet.key;
23724
+ this.calendarReference = options.calendarReference;
23725
+ }
23726
+ }
23727
+ /**
23728
+ * Resolves calendar runtime or returns a wallet-credential-required payload when missing.
23729
+ *
23730
+ * @private function of UseCalendarCommitmentDefinition
23731
+ */
23732
+ function resolveUseCalendarToolRuntimeOrWalletCredentialResult(args) {
23733
+ try {
23734
+ return resolveUseCalendarToolRuntime(args);
23735
+ }
23736
+ catch (error) {
23737
+ if (error instanceof CalendarWalletCredentialRequiredError) {
23738
+ return {
23739
+ walletResult: JSON.stringify(createCalendarWalletCredentialRequiredResult(error)),
23740
+ };
23741
+ }
23742
+ throw error;
23743
+ }
23744
+ }
23745
+ /**
23746
+ * Resolves runtime calendar + token for a USE CALENDAR tool call.
23747
+ *
23748
+ * @private function of resolveUseCalendarToolRuntimeOrWalletCredentialResult
23749
+ */
23750
+ function resolveUseCalendarToolRuntime(args) {
23751
+ var _a, _b;
23752
+ const runtimeContext = (readToolRuntimeContextFromToolArgs(args) ||
23753
+ {});
23754
+ const configuredCalendars = normalizeConfiguredCalendars$1((_a = runtimeContext.calendars) === null || _a === void 0 ? void 0 : _a.connections);
23755
+ const calendarArgument = normalizeOptionalText$1(args.calendarUrl);
23756
+ let calendarReference = null;
23757
+ if (calendarArgument) {
23758
+ calendarReference = parseGoogleCalendarReference(calendarArgument);
23759
+ if (!calendarReference) {
23760
+ throw new Error(`Calendar URL "${calendarArgument}" is invalid.`);
23761
+ }
23762
+ }
23763
+ else if (configuredCalendars.length === 1) {
23764
+ calendarReference = configuredCalendars[0] || null;
23765
+ }
23766
+ else if (configuredCalendars.length > 1) {
23767
+ throw new Error('Calendar is ambiguous. Provide "calendarUrl" argument with one calendar from USE CALENDAR commitments.');
23768
+ }
23769
+ else {
23770
+ throw new Error('Calendar is required. Provide "calendarUrl" argument in the tool call.');
23771
+ }
23772
+ if (!calendarReference) {
23773
+ throw new Error('Calendar is required but was not resolved.');
23774
+ }
23775
+ const accessToken = normalizeOptionalText$1((_b = runtimeContext.calendars) === null || _b === void 0 ? void 0 : _b.googleAccessToken) || '';
23776
+ if (!accessToken) {
23777
+ throw new CalendarWalletCredentialRequiredError({
23778
+ calendarReference,
23779
+ });
23780
+ }
23781
+ if (configuredCalendars.length > 0) {
23782
+ const allowedCalendarUrls = new Set(configuredCalendars.map((configuredCalendar) => configuredCalendar.url));
23783
+ if (!allowedCalendarUrls.has(calendarReference.url)) {
23784
+ throw new Error(`Calendar "${calendarReference.url}" is not configured by USE CALENDAR for this agent.`);
23785
+ }
23786
+ }
23787
+ return {
23788
+ calendarReference,
23789
+ accessToken,
23790
+ };
23791
+ }
23792
+ /**
23793
+ * Normalizes optional calendar list from runtime context.
23794
+ *
23795
+ * @private function of resolveUseCalendarToolRuntimeOrWalletCredentialResult
23796
+ */
23797
+ function normalizeConfiguredCalendars$1(rawCalendars) {
23798
+ if (!Array.isArray(rawCalendars)) {
23799
+ return [];
23800
+ }
23801
+ const calendars = [];
23802
+ const knownCalendars = new Set();
23803
+ for (const rawCalendar of rawCalendars) {
23804
+ if (!rawCalendar || typeof rawCalendar !== 'object') {
23805
+ continue;
23806
+ }
23807
+ const calendar = rawCalendar;
23808
+ const rawUrl = normalizeOptionalText$1(calendar.url);
23809
+ if (!rawUrl) {
23810
+ continue;
23811
+ }
23812
+ const parsedReference = parseGoogleCalendarReference(rawUrl);
23813
+ if (!parsedReference) {
23814
+ continue;
23815
+ }
23816
+ const key = `${parsedReference.provider}|${parsedReference.url}`;
23817
+ if (knownCalendars.has(key)) {
23818
+ continue;
23819
+ }
23820
+ knownCalendars.add(key);
23821
+ const scopes = Array.isArray(calendar.scopes)
23822
+ ? calendar.scopes
23823
+ .filter((scope) => typeof scope === 'string')
23824
+ .map((scope) => scope.trim())
23825
+ .filter(Boolean)
23826
+ : [];
23827
+ calendars.push({
23828
+ ...parsedReference,
23829
+ scopes: scopes.length > 0 ? scopes : parsedReference.scopes,
23830
+ });
23831
+ }
23832
+ return calendars;
23833
+ }
23834
+ /**
23835
+ * Converts missing-wallet errors into structured tool result payloads.
23836
+ *
23837
+ * @private function of resolveUseCalendarToolRuntimeOrWalletCredentialResult
23838
+ */
23839
+ function createCalendarWalletCredentialRequiredResult(error) {
23840
+ return {
23841
+ action: 'calendar-auth',
23842
+ status: 'wallet-credential-required',
23843
+ recordType: 'ACCESS_TOKEN',
23844
+ service: error.service,
23845
+ key: error.key,
23846
+ isUserScoped: false,
23847
+ isGlobal: false,
23848
+ provider: 'google',
23849
+ calendarUrl: error.calendarReference.url,
23850
+ scopes: error.calendarReference.scopes,
23851
+ message: error.message,
23852
+ };
23853
+ }
23854
+ /**
23855
+ * Normalizes unknown text input to trimmed non-empty string.
23856
+ *
23857
+ * @private function of resolveUseCalendarToolRuntimeOrWalletCredentialResult
23858
+ */
23859
+ function normalizeOptionalText$1(value) {
23860
+ if (typeof value !== 'string') {
23861
+ return undefined;
23862
+ }
23863
+ const trimmedValue = value.trim();
23864
+ return trimmedValue || undefined;
23865
+ }
23866
+
23867
+ /**
23868
+ * Names of tools used by the USE CALENDAR commitment.
23869
+ *
23870
+ * @private constant of UseCalendarCommitmentDefinition
23871
+ */
23872
+ const UseCalendarToolNames = {
23873
+ listEvents: 'calendar_list_events',
23874
+ getEvent: 'calendar_get_event',
23875
+ createEvent: 'calendar_create_event',
23876
+ updateEvent: 'calendar_update_event',
23877
+ deleteEvent: 'calendar_delete_event',
23878
+ inviteGuests: 'calendar_invite_guests',
23879
+ };
23880
+
23881
+ /**
23882
+ * Gets Google Calendar tool function implementations.
23883
+ *
23884
+ * @private function of UseCalendarCommitmentDefinition
23885
+ */
23886
+ function createUseCalendarToolFunctions() {
23887
+ return {
23888
+ async [UseCalendarToolNames.listEvents](args) {
23889
+ return withUseCalendarRuntime(args, async ({ calendarReference, accessToken }) => {
23890
+ const query = {};
23891
+ if (normalizeOptionalText(args.timeMin)) {
23892
+ query.timeMin = args.timeMin.trim();
23893
+ }
23894
+ if (normalizeOptionalText(args.timeMax)) {
23895
+ query.timeMax = args.timeMax.trim();
23896
+ }
23897
+ if (normalizeOptionalText(args.query)) {
23898
+ query.q = args.query.trim();
23899
+ }
23900
+ if (typeof args.maxResults === 'number' && Number.isFinite(args.maxResults) && args.maxResults > 0) {
23901
+ query.maxResults = String(Math.floor(args.maxResults));
23902
+ }
23903
+ if (args.singleEvents !== undefined) {
23904
+ query.singleEvents = args.singleEvents ? 'true' : 'false';
23905
+ }
23906
+ if (args.orderBy === 'startTime' || args.orderBy === 'updated') {
23907
+ query.orderBy = args.orderBy;
23908
+ }
23909
+ if (normalizeOptionalText(args.timeZone)) {
23910
+ query.timeZone = args.timeZone.trim();
23911
+ }
23912
+ const payload = await callGoogleCalendarApi(accessToken, {
23913
+ method: 'GET',
23914
+ path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events`,
23915
+ query,
23916
+ });
23917
+ const events = ((payload === null || payload === void 0 ? void 0 : payload.items) || []).map((event) => mapGoogleCalendarEvent(event));
23918
+ return JSON.stringify({
23919
+ provider: calendarReference.provider,
23920
+ calendarUrl: calendarReference.url,
23921
+ calendarId: calendarReference.calendarId,
23922
+ events,
23923
+ nextPageToken: (payload === null || payload === void 0 ? void 0 : payload.nextPageToken) || null,
23924
+ nextSyncToken: (payload === null || payload === void 0 ? void 0 : payload.nextSyncToken) || null,
23925
+ });
23926
+ });
23927
+ },
23928
+ async [UseCalendarToolNames.getEvent](args) {
23929
+ return withUseCalendarRuntime(args, async ({ calendarReference, accessToken }) => {
23930
+ const eventId = normalizeRequiredText(args.eventId, 'eventId');
23931
+ const payload = await callGoogleCalendarApi(accessToken, {
23932
+ method: 'GET',
23933
+ path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events/${encodeURIComponent(eventId)}`,
23934
+ });
23935
+ return JSON.stringify({
23936
+ provider: calendarReference.provider,
23937
+ calendarUrl: calendarReference.url,
23938
+ calendarId: calendarReference.calendarId,
23939
+ event: mapGoogleCalendarEvent(payload || {}),
23940
+ });
23941
+ });
23942
+ },
23943
+ async [UseCalendarToolNames.createEvent](args) {
23944
+ return withUseCalendarRuntime(args, async ({ calendarReference, accessToken }) => {
23945
+ const requestBody = createGoogleCalendarEventPayload({
23946
+ summary: normalizeRequiredText(args.summary, 'summary'),
23947
+ description: normalizeOptionalText(args.description),
23948
+ location: normalizeOptionalText(args.location),
23949
+ start: normalizeRequiredText(args.start, 'start'),
23950
+ end: normalizeRequiredText(args.end, 'end'),
23951
+ timeZone: normalizeOptionalText(args.timeZone),
23952
+ attendees: normalizeAttendees(args.attendees),
23953
+ reminderMinutes: normalizeReminderMinutes(args.reminderMinutes),
23954
+ });
23955
+ const payload = await callGoogleCalendarApi(accessToken, {
23956
+ method: 'POST',
23957
+ path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events`,
23958
+ query: createSendUpdatesQuery(args.sendUpdates),
23959
+ body: requestBody,
23960
+ });
23961
+ return JSON.stringify({
23962
+ provider: calendarReference.provider,
23963
+ calendarUrl: calendarReference.url,
23964
+ calendarId: calendarReference.calendarId,
23965
+ event: mapGoogleCalendarEvent(payload || {}),
23966
+ });
23967
+ });
23968
+ },
23969
+ async [UseCalendarToolNames.updateEvent](args) {
23970
+ return withUseCalendarRuntime(args, async ({ calendarReference, accessToken }) => {
23971
+ const eventId = normalizeRequiredText(args.eventId, 'eventId');
23972
+ const requestBody = createGoogleCalendarEventPayload({
23973
+ summary: normalizeOptionalText(args.summary),
23974
+ description: normalizeOptionalText(args.description),
23975
+ location: normalizeOptionalText(args.location),
23976
+ start: normalizeOptionalText(args.start),
23977
+ end: normalizeOptionalText(args.end),
23978
+ timeZone: normalizeOptionalText(args.timeZone),
23979
+ attendees: normalizeAttendees(args.attendees),
23980
+ reminderMinutes: normalizeReminderMinutes(args.reminderMinutes),
23981
+ });
23982
+ const payload = await callGoogleCalendarApi(accessToken, {
23983
+ method: 'PATCH',
23984
+ path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events/${encodeURIComponent(eventId)}`,
23985
+ query: createSendUpdatesQuery(args.sendUpdates),
23986
+ body: requestBody,
23987
+ });
23988
+ return JSON.stringify({
23989
+ provider: calendarReference.provider,
23990
+ calendarUrl: calendarReference.url,
23991
+ calendarId: calendarReference.calendarId,
23992
+ event: mapGoogleCalendarEvent(payload || {}),
23993
+ });
23994
+ });
23995
+ },
23996
+ async [UseCalendarToolNames.deleteEvent](args) {
23997
+ return withUseCalendarRuntime(args, async ({ calendarReference, accessToken }) => {
23998
+ const eventId = normalizeRequiredText(args.eventId, 'eventId');
23999
+ await callGoogleCalendarApi(accessToken, {
24000
+ method: 'DELETE',
24001
+ path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events/${encodeURIComponent(eventId)}`,
24002
+ query: createSendUpdatesQuery(args.sendUpdates),
24003
+ });
24004
+ return JSON.stringify({
24005
+ provider: calendarReference.provider,
24006
+ calendarUrl: calendarReference.url,
24007
+ calendarId: calendarReference.calendarId,
24008
+ eventId,
24009
+ status: 'deleted',
24010
+ });
24011
+ });
24012
+ },
24013
+ async [UseCalendarToolNames.inviteGuests](args) {
24014
+ return withUseCalendarRuntime(args, async ({ calendarReference, accessToken }) => {
24015
+ const eventId = normalizeRequiredText(args.eventId, 'eventId');
24016
+ const guests = normalizeAttendees(args.guests);
24017
+ if (guests.length === 0) {
24018
+ throw new Error('Tool "calendar_invite_guests" requires non-empty "guests".');
24019
+ }
24020
+ const existingEvent = await callGoogleCalendarApi(accessToken, {
24021
+ method: 'GET',
24022
+ path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events/${encodeURIComponent(eventId)}`,
24023
+ });
24024
+ const existingAttendees = ((existingEvent === null || existingEvent === void 0 ? void 0 : existingEvent.attendees) || [])
24025
+ .map((attendee) => normalizeOptionalText(attendee.email))
24026
+ .filter((email) => Boolean(email));
24027
+ const mergedAttendees = [...new Set([...existingAttendees, ...guests])];
24028
+ const payload = await callGoogleCalendarApi(accessToken, {
24029
+ method: 'PATCH',
24030
+ path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events/${encodeURIComponent(eventId)}`,
24031
+ query: createSendUpdatesQuery(args.sendUpdates),
24032
+ body: {
24033
+ attendees: mergedAttendees.map((email) => ({ email })),
24034
+ },
24035
+ });
24036
+ return JSON.stringify({
24037
+ provider: calendarReference.provider,
24038
+ calendarUrl: calendarReference.url,
24039
+ calendarId: calendarReference.calendarId,
24040
+ event: mapGoogleCalendarEvent(payload || {}),
24041
+ invitedGuests: guests,
24042
+ });
24043
+ });
24044
+ },
24045
+ };
24046
+ }
24047
+ /**
24048
+ * Executes one tool operation with resolved USE CALENDAR runtime.
24049
+ *
24050
+ * @private function of createUseCalendarToolFunctions
24051
+ */
24052
+ async function withUseCalendarRuntime(args, operation) {
24053
+ const runtime = resolveUseCalendarToolRuntimeOrWalletCredentialResult(args);
24054
+ if ('walletResult' in runtime) {
24055
+ return runtime.walletResult;
24056
+ }
24057
+ return operation(runtime);
24058
+ }
24059
+ /**
24060
+ * Encodes one Google calendar id for URL path usage.
24061
+ *
24062
+ * @private function of createUseCalendarToolFunctions
24063
+ */
24064
+ function encodeGoogleCalendarId(calendarId) {
24065
+ return encodeURIComponent(calendarId);
24066
+ }
24067
+ /**
24068
+ * Normalizes one required textual input.
24069
+ *
24070
+ * @private function of createUseCalendarToolFunctions
24071
+ */
24072
+ function normalizeRequiredText(value, fieldName) {
24073
+ const normalizedValue = normalizeOptionalText(value);
24074
+ if (!normalizedValue) {
24075
+ throw new Error(`Tool "${fieldName}" requires non-empty value.`);
24076
+ }
24077
+ return normalizedValue;
24078
+ }
24079
+ /**
24080
+ * Normalizes unknown text input to trimmed non-empty string.
24081
+ *
24082
+ * @private function of createUseCalendarToolFunctions
24083
+ */
24084
+ function normalizeOptionalText(value) {
24085
+ if (typeof value !== 'string') {
24086
+ return undefined;
24087
+ }
24088
+ const trimmedValue = value.trim();
24089
+ return trimmedValue || undefined;
24090
+ }
24091
+ /**
24092
+ * Normalizes optional attendee list from tool input.
24093
+ *
24094
+ * @private function of createUseCalendarToolFunctions
24095
+ */
24096
+ function normalizeAttendees(value) {
24097
+ if (!Array.isArray(value)) {
24098
+ return [];
24099
+ }
24100
+ const normalizedAttendees = value
24101
+ .filter((attendee) => typeof attendee === 'string')
24102
+ .map((attendee) => attendee.trim())
24103
+ .filter(Boolean);
24104
+ return [...new Set(normalizedAttendees)];
24105
+ }
24106
+ /**
24107
+ * Normalizes optional reminder-minute offsets from tool input.
24108
+ *
24109
+ * @private function of createUseCalendarToolFunctions
24110
+ */
24111
+ function normalizeReminderMinutes(value) {
24112
+ if (!Array.isArray(value)) {
24113
+ return [];
24114
+ }
24115
+ const reminderMinutes = value
24116
+ .filter((minute) => typeof minute === 'number' && Number.isFinite(minute))
24117
+ .map((minute) => Math.max(0, Math.floor(minute)));
24118
+ return [...new Set(reminderMinutes)];
24119
+ }
24120
+ /**
24121
+ * Builds optional `sendUpdates` query for mutating Google Calendar requests.
24122
+ *
24123
+ * @private function of createUseCalendarToolFunctions
24124
+ */
24125
+ function createSendUpdatesQuery(sendUpdates) {
24126
+ if (sendUpdates === 'all' || sendUpdates === 'externalOnly' || sendUpdates === 'none') {
24127
+ return { sendUpdates };
24128
+ }
24129
+ return {};
24130
+ }
24131
+ /**
24132
+ * Creates one Google Calendar event payload from normalized tool arguments.
24133
+ *
24134
+ * @private function of createUseCalendarToolFunctions
24135
+ */
24136
+ function createGoogleCalendarEventPayload(options) {
24137
+ const payload = {};
24138
+ if (options.summary) {
24139
+ payload.summary = options.summary;
24140
+ }
24141
+ if (options.description) {
24142
+ payload.description = options.description;
24143
+ }
24144
+ if (options.location) {
24145
+ payload.location = options.location;
24146
+ }
24147
+ if (options.start) {
24148
+ payload.start = createGoogleCalendarDateValue(options.start, options.timeZone);
24149
+ }
24150
+ if (options.end) {
24151
+ payload.end = createGoogleCalendarDateValue(options.end, options.timeZone);
24152
+ }
24153
+ if (options.attendees && options.attendees.length > 0) {
24154
+ payload.attendees = options.attendees.map((email) => ({ email }));
24155
+ }
24156
+ if (options.reminderMinutes && options.reminderMinutes.length > 0) {
24157
+ payload.reminders = {
24158
+ useDefault: false,
24159
+ overrides: options.reminderMinutes.map((minutes) => ({
24160
+ method: 'popup',
24161
+ minutes,
24162
+ })),
24163
+ };
24164
+ }
24165
+ return payload;
24166
+ }
24167
+ /**
24168
+ * Converts date/dateTime input into a Google Calendar-compatible date object.
24169
+ *
24170
+ * @private function of createUseCalendarToolFunctions
24171
+ */
24172
+ function createGoogleCalendarDateValue(value, timeZone) {
24173
+ const isDateOnly = /^\d{4}-\d{2}-\d{2}$/.test(value);
24174
+ if (isDateOnly) {
24175
+ return {
24176
+ date: value,
24177
+ };
24178
+ }
24179
+ return {
24180
+ dateTime: value,
24181
+ ...(timeZone ? { timeZone } : {}),
24182
+ };
24183
+ }
24184
+ /**
24185
+ * Maps raw Google Calendar event payload to a compact tool result object.
24186
+ *
24187
+ * @private function of createUseCalendarToolFunctions
24188
+ */
24189
+ function mapGoogleCalendarEvent(event) {
24190
+ return {
24191
+ id: event.id || null,
24192
+ summary: event.summary || null,
24193
+ description: event.description || null,
24194
+ location: event.location || null,
24195
+ status: event.status || null,
24196
+ htmlLink: event.htmlLink || null,
24197
+ start: event.start || null,
24198
+ end: event.end || null,
24199
+ organizer: event.organizer || null,
24200
+ attendees: (event.attendees || []).map((attendee) => ({
24201
+ email: attendee.email || null,
24202
+ responseStatus: attendee.responseStatus || null,
24203
+ })),
24204
+ };
24205
+ }
24206
+
24207
+ /**
24208
+ * Shared calendar URL argument description used in USE CALENDAR tool schemas.
24209
+ *
24210
+ * @private constant of createUseCalendarTools
24211
+ */
24212
+ const CALENDAR_URL_PARAMETER_DESCRIPTION = 'Google Calendar URL configured by USE CALENDAR (for example "https://calendar.google.com/...").';
24213
+ /**
24214
+ * Adds USE CALENDAR tool definitions while keeping already registered tools untouched.
24215
+ *
24216
+ * @private function of UseCalendarCommitmentDefinition
24217
+ */
24218
+ function createUseCalendarTools(existingTools) {
24219
+ const updatedTools = [...existingTools];
24220
+ const addToolIfMissing = (tool) => {
24221
+ if (!updatedTools.some((existingTool) => existingTool.name === tool.name)) {
24222
+ updatedTools.push(tool);
24223
+ }
24224
+ };
24225
+ addToolIfMissing({
24226
+ name: UseCalendarToolNames.listEvents,
24227
+ description: 'List events from a configured calendar for a time range.',
24228
+ parameters: {
24229
+ type: 'object',
24230
+ properties: {
24231
+ calendarUrl: {
24232
+ type: 'string',
24233
+ description: CALENDAR_URL_PARAMETER_DESCRIPTION,
24234
+ },
24235
+ timeMin: {
24236
+ type: 'string',
24237
+ description: 'Inclusive event start bound in ISO datetime.',
24238
+ },
24239
+ timeMax: {
24240
+ type: 'string',
24241
+ description: 'Exclusive event end bound in ISO datetime.',
24242
+ },
24243
+ query: {
24244
+ type: 'string',
24245
+ description: 'Optional free-text event search query.',
24246
+ },
24247
+ maxResults: {
24248
+ type: 'integer',
24249
+ description: 'Maximum number of events to return.',
24250
+ },
24251
+ singleEvents: {
24252
+ type: 'boolean',
24253
+ description: 'Expand recurring events into individual instances.',
24254
+ },
24255
+ orderBy: {
24256
+ type: 'string',
24257
+ description: 'Optional ordering ("startTime" or "updated").',
24258
+ },
24259
+ timeZone: {
24260
+ type: 'string',
24261
+ description: 'Optional IANA timezone for response rendering.',
24262
+ },
24263
+ },
24264
+ required: [],
24265
+ },
24266
+ });
24267
+ addToolIfMissing({
24268
+ name: UseCalendarToolNames.getEvent,
24269
+ description: 'Get one event by id from a configured calendar.',
24270
+ parameters: {
24271
+ type: 'object',
24272
+ properties: {
24273
+ calendarUrl: {
24274
+ type: 'string',
24275
+ description: CALENDAR_URL_PARAMETER_DESCRIPTION,
24276
+ },
24277
+ eventId: {
24278
+ type: 'string',
24279
+ description: 'Google Calendar event id.',
24280
+ },
24281
+ },
24282
+ required: ['eventId'],
24283
+ },
24284
+ });
24285
+ addToolIfMissing({
24286
+ name: UseCalendarToolNames.createEvent,
24287
+ description: 'Create one event in a configured calendar.',
24288
+ parameters: {
24289
+ type: 'object',
24290
+ properties: {
24291
+ calendarUrl: {
24292
+ type: 'string',
24293
+ description: CALENDAR_URL_PARAMETER_DESCRIPTION,
24294
+ },
24295
+ summary: {
24296
+ type: 'string',
24297
+ description: 'Event title/summary.',
24298
+ },
24299
+ description: {
24300
+ type: 'string',
24301
+ description: 'Optional event description.',
24302
+ },
24303
+ location: {
24304
+ type: 'string',
24305
+ description: 'Optional event location.',
24306
+ },
24307
+ start: {
24308
+ type: 'string',
24309
+ description: 'Event start as ISO datetime or date.',
24310
+ },
24311
+ end: {
24312
+ type: 'string',
24313
+ description: 'Event end as ISO datetime or date.',
24314
+ },
24315
+ timeZone: {
24316
+ type: 'string',
24317
+ description: 'Optional timezone for datetime values.',
24318
+ },
24319
+ attendees: {
24320
+ type: 'array',
24321
+ description: 'Optional guest email list.',
24322
+ },
24323
+ reminderMinutes: {
24324
+ type: 'array',
24325
+ description: 'Optional popup reminder minute offsets.',
24326
+ },
24327
+ sendUpdates: {
24328
+ type: 'string',
24329
+ description: 'Guest update policy ("all", "externalOnly", "none").',
24330
+ },
24331
+ },
24332
+ required: ['summary', 'start', 'end'],
24333
+ },
24334
+ });
24335
+ addToolIfMissing({
24336
+ name: UseCalendarToolNames.updateEvent,
24337
+ description: 'Update one existing event in a configured calendar.',
24338
+ parameters: {
24339
+ type: 'object',
24340
+ properties: {
24341
+ calendarUrl: {
24342
+ type: 'string',
24343
+ description: CALENDAR_URL_PARAMETER_DESCRIPTION,
24344
+ },
24345
+ eventId: {
24346
+ type: 'string',
24347
+ description: 'Google Calendar event id.',
24348
+ },
24349
+ summary: {
24350
+ type: 'string',
24351
+ description: 'Updated event summary.',
24352
+ },
24353
+ description: {
24354
+ type: 'string',
24355
+ description: 'Updated event description.',
24356
+ },
24357
+ location: {
24358
+ type: 'string',
24359
+ description: 'Updated event location.',
24360
+ },
24361
+ start: {
24362
+ type: 'string',
24363
+ description: 'Updated event start as ISO datetime or date.',
24364
+ },
24365
+ end: {
24366
+ type: 'string',
24367
+ description: 'Updated event end as ISO datetime or date.',
24368
+ },
24369
+ timeZone: {
24370
+ type: 'string',
24371
+ description: 'Optional timezone for datetime values.',
24372
+ },
24373
+ attendees: {
24374
+ type: 'array',
24375
+ description: 'Optional replacement guest email list.',
24376
+ },
24377
+ reminderMinutes: {
24378
+ type: 'array',
24379
+ description: 'Optional replacement popup reminder minute offsets.',
24380
+ },
24381
+ sendUpdates: {
24382
+ type: 'string',
24383
+ description: 'Guest update policy ("all", "externalOnly", "none").',
24384
+ },
24385
+ },
24386
+ required: ['eventId'],
24387
+ },
24388
+ });
24389
+ addToolIfMissing({
24390
+ name: UseCalendarToolNames.deleteEvent,
24391
+ description: 'Delete one event from a configured calendar.',
24392
+ parameters: {
24393
+ type: 'object',
24394
+ properties: {
24395
+ calendarUrl: {
24396
+ type: 'string',
24397
+ description: CALENDAR_URL_PARAMETER_DESCRIPTION,
24398
+ },
24399
+ eventId: {
24400
+ type: 'string',
24401
+ description: 'Google Calendar event id.',
24402
+ },
24403
+ sendUpdates: {
24404
+ type: 'string',
24405
+ description: 'Guest update policy ("all", "externalOnly", "none").',
24406
+ },
24407
+ },
24408
+ required: ['eventId'],
24409
+ },
24410
+ });
24411
+ addToolIfMissing({
24412
+ name: UseCalendarToolNames.inviteGuests,
24413
+ description: 'Add guests to an existing event in a configured calendar.',
24414
+ parameters: {
24415
+ type: 'object',
24416
+ properties: {
24417
+ calendarUrl: {
24418
+ type: 'string',
24419
+ description: CALENDAR_URL_PARAMETER_DESCRIPTION,
24420
+ },
24421
+ eventId: {
24422
+ type: 'string',
24423
+ description: 'Google Calendar event id.',
24424
+ },
24425
+ guests: {
24426
+ type: 'array',
24427
+ description: 'Guest email list to add to the event.',
24428
+ },
24429
+ sendUpdates: {
24430
+ type: 'string',
24431
+ description: 'Guest update policy ("all", "externalOnly", "none").',
24432
+ },
24433
+ },
24434
+ required: ['eventId', 'guests'],
24435
+ },
24436
+ });
24437
+ return updatedTools;
24438
+ }
24439
+
24440
+ /**
24441
+ * Gets human-readable tool labels for USE CALENDAR functions.
24442
+ *
24443
+ * @private function of UseCalendarCommitmentDefinition
24444
+ */
24445
+ function getUseCalendarToolTitles() {
24446
+ return {
24447
+ [UseCalendarToolNames.listEvents]: 'List calendar events',
24448
+ [UseCalendarToolNames.getEvent]: 'Get calendar event',
24449
+ [UseCalendarToolNames.createEvent]: 'Create calendar event',
24450
+ [UseCalendarToolNames.updateEvent]: 'Update calendar event',
24451
+ [UseCalendarToolNames.deleteEvent]: 'Delete calendar event',
24452
+ [UseCalendarToolNames.inviteGuests]: 'Invite calendar guests',
24453
+ };
24454
+ }
24455
+
24456
+ /**
24457
+ * Normalizes unknown metadata payload into a typed list of configured calendars.
24458
+ *
24459
+ * @private internal utility of USE CALENDAR commitment
24460
+ */
24461
+ function normalizeConfiguredCalendars(rawCalendars) {
24462
+ if (!Array.isArray(rawCalendars)) {
24463
+ return [];
24464
+ }
24465
+ const uniqueCalendars = new Set();
24466
+ const calendars = [];
24467
+ for (const rawCalendar of rawCalendars) {
24468
+ if (!rawCalendar || typeof rawCalendar !== 'object') {
24469
+ continue;
24470
+ }
24471
+ const calendar = rawCalendar;
24472
+ const provider = normalizeProvider(calendar.provider);
24473
+ const url = normalizeText(calendar.url);
24474
+ const calendarId = normalizeText(calendar.calendarId);
24475
+ if (!provider || !url || !calendarId) {
24476
+ continue;
24477
+ }
24478
+ const uniqueKey = `${provider}|${url}`;
24479
+ if (uniqueCalendars.has(uniqueKey)) {
24480
+ continue;
24481
+ }
24482
+ uniqueCalendars.add(uniqueKey);
24483
+ const scopes = Array.isArray(calendar.scopes)
24484
+ ? calendar.scopes
24485
+ .filter((scope) => typeof scope === 'string')
24486
+ .map((scope) => scope.trim())
24487
+ .filter(Boolean)
24488
+ : [];
24489
+ calendars.push({
24490
+ provider,
24491
+ url,
24492
+ calendarId,
24493
+ scopes,
24494
+ ...(normalizeText(calendar.tokenRef) ? { tokenRef: normalizeText(calendar.tokenRef) } : {}),
24495
+ });
24496
+ }
24497
+ return calendars;
24498
+ }
24499
+ /**
24500
+ * Normalizes optional provider text to one supported value.
24501
+ *
24502
+ * @private function of normalizeConfiguredCalendars
24503
+ */
24504
+ function normalizeProvider(value) {
24505
+ if (typeof value !== 'string') {
24506
+ return null;
24507
+ }
24508
+ const normalizedProvider = value.trim().toLowerCase();
24509
+ if (normalizedProvider === 'google') {
24510
+ return 'google';
24511
+ }
24512
+ return null;
24513
+ }
24514
+ /**
24515
+ * Normalizes unknown text input to trimmed non-empty string.
24516
+ *
24517
+ * @private function of normalizeConfiguredCalendars
24518
+ */
24519
+ function normalizeText(value) {
24520
+ return typeof value === 'string' ? value.trim() : '';
24521
+ }
24522
+
24523
+ /**
24524
+ * USE CALENDAR commitment definition.
24525
+ *
24526
+ * `USE CALENDAR` enables calendar tooling so the agent can read and manage events
24527
+ * in one configured Google Calendar.
24528
+ *
24529
+ * Authentication is expected through runtime context provided by the host app UI.
24530
+ * Hosts can provide manual wallet tokens or host-managed OAuth tokens.
24531
+ *
24532
+ * @private [🪔] Maybe export the commitments through some package
24533
+ */
24534
+ class UseCalendarCommitmentDefinition extends BaseCommitmentDefinition {
24535
+ constructor() {
24536
+ super('USE CALENDAR', ['CALENDAR']);
24537
+ }
24538
+ /**
24539
+ * Short one-line description of USE CALENDAR.
24540
+ */
24541
+ get description() {
24542
+ return 'Enable calendar tools for reading and managing events through Google Calendar.';
24543
+ }
24544
+ /**
24545
+ * Icon for this commitment.
24546
+ */
24547
+ get icon() {
24548
+ return '📅';
24549
+ }
24550
+ /**
24551
+ * Markdown documentation for USE CALENDAR commitment.
24552
+ */
24553
+ get documentation() {
24554
+ return spaceTrim$1(`
24555
+ # USE CALENDAR
24556
+
24557
+ Enables the agent to access and manage one Google Calendar.
24558
+
24559
+ ## Key aspects
24560
+
24561
+ - The first URL in the commitment should point to a Google Calendar URL.
24562
+ - Optional \`SCOPES\` lines can provide explicit OAuth scopes.
24563
+ - Optional extra instructions can follow calendar reference lines.
24564
+ - Runtime provides Google Calendar OAuth token (manual wallet token or host-managed OAuth token).
24565
+ - Tools support listing events, reading one event, creating events, updating events, deleting events, and inviting guests.
24566
+
24567
+ ## Examples
24568
+
24569
+ \`\`\`book
24570
+ Scheduling Assistant
24571
+
24572
+ PERSONA You coordinate meetings and schedules.
24573
+ USE CALENDAR https://calendar.google.com/calendar/u/0/r
24574
+ \`\`\`
24575
+
24576
+ \`\`\`book
24577
+ Executive Assistant
24578
+
24579
+ USE CALENDAR https://calendar.google.com/calendar/u/0/r
24580
+ SCOPES https://www.googleapis.com/auth/calendar.readonly
24581
+ RULE Ask for confirmation before deleting events.
24582
+ \`\`\`
24583
+ `);
24584
+ }
24585
+ applyToAgentModelRequirements(requirements, content) {
24586
+ var _a;
24587
+ const parsedCommitment = parseUseCalendarCommitmentContent(content);
24588
+ const existingConfiguredCalendars = normalizeConfiguredCalendars((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.useCalendars);
24589
+ if (parsedCommitment.calendar) {
24590
+ addConfiguredCalendarIfMissing(existingConfiguredCalendars, parsedCommitment.calendar);
24591
+ }
24592
+ const calendarsList = existingConfiguredCalendars.length > 0
24593
+ ? existingConfiguredCalendars
24594
+ .map((calendar) => [
24595
+ `- ${calendar.provider}: ${calendar.url}`,
24596
+ calendar.scopes.length > 0 ? ` scopes: ${calendar.scopes.join(', ')}` : '',
24597
+ ]
24598
+ .filter(Boolean)
24599
+ .join('\n'))
24600
+ .join('\n')
24601
+ : '- Calendar is resolved from runtime context';
24602
+ const extraInstructions = formatOptionalInstructionBlock('Calendar instructions', parsedCommitment.instructions);
24603
+ return this.appendToSystemMessage({
24604
+ ...requirements,
24605
+ tools: createUseCalendarTools(requirements.tools || []),
24606
+ _metadata: {
24607
+ ...requirements._metadata,
24608
+ useCalendar: true,
24609
+ useCalendars: existingConfiguredCalendars,
24610
+ },
24611
+ }, spaceTrim$1((block) => `
24612
+ Calendar tools:
24613
+ - You can inspect and manage events in configured calendars.
24614
+ - Supported operations include read, create, update, delete, invite guests, and reminders.
24615
+ - Configured calendars:
24616
+ ${block(calendarsList)}
24617
+ - USE CALENDAR credentials are read from wallet records (ACCESS_TOKEN, service "${UseCalendarWallet.service}", key "${UseCalendarWallet.key}").
24618
+ - If credentials are missing, ask user to connect calendar credentials in host UI and/or add them to wallet.
24619
+ ${block(extraInstructions)}
24620
+ `));
24621
+ }
24622
+ /**
24623
+ * Gets human-readable titles for tool functions provided by this commitment.
24624
+ */
24625
+ getToolTitles() {
24626
+ return getUseCalendarToolTitles();
24627
+ }
24628
+ /**
24629
+ * Gets calendar tool function implementations.
24630
+ */
24631
+ getToolFunctions() {
24632
+ return createUseCalendarToolFunctions();
24633
+ }
24634
+ }
24635
+ /**
24636
+ * Adds calendar into configured calendars list if it is not already present.
24637
+ *
24638
+ * @private function of UseCalendarCommitmentDefinition
24639
+ */
24640
+ function addConfiguredCalendarIfMissing(configuredCalendars, calendarReference) {
24641
+ if (configuredCalendars.some((calendar) => calendar.provider === calendarReference.provider && calendar.url === calendarReference.url)) {
24642
+ return;
24643
+ }
24644
+ configuredCalendars.push({
24645
+ provider: calendarReference.provider,
24646
+ url: calendarReference.url,
24647
+ calendarId: calendarReference.calendarId,
24648
+ scopes: [...calendarReference.scopes],
24649
+ ...(calendarReference.tokenRef ? { tokenRef: calendarReference.tokenRef } : {}),
24650
+ });
24651
+ }
24652
+ /**
24653
+ * Note: [💞] Ignore a discrepancy between file name and entity name
24654
+ */
24655
+
23431
24656
  /**
23432
24657
  * Lightweight email token matcher used for `USE EMAIL` first-line parsing.
23433
24658
  *
@@ -27246,6 +28471,7 @@ const COMMITMENT_REGISTRY = [
27246
28471
  new UseTimeoutCommitmentDefinition(),
27247
28472
  new UseTimeCommitmentDefinition(),
27248
28473
  new UseUserLocationCommitmentDefinition(),
28474
+ new UseCalendarCommitmentDefinition(),
27249
28475
  new UseEmailCommitmentDefinition(),
27250
28476
  new UsePopupCommitmentDefinition(),
27251
28477
  new UseImageGeneratorCommitmentDefinition('USE IMAGE GENERATOR'),
@@ -28146,6 +29372,14 @@ function parseAgentSource(agentSource) {
28146
29372
  });
28147
29373
  continue;
28148
29374
  }
29375
+ if (commitment.type === 'USE CALENDAR') {
29376
+ capabilities.push({
29377
+ type: 'calendar',
29378
+ label: 'Calendar',
29379
+ iconName: 'Calendar',
29380
+ });
29381
+ continue;
29382
+ }
28149
29383
  if (commitment.type === 'FROM') {
28150
29384
  const content = spaceTrim$2(commitment.content).split(/\r?\n/)[0] || '';
28151
29385
  if (content === 'Adam' || content === '' /* <- Note: Adam is implicit */) {
@@ -29788,16 +31022,179 @@ const runBrowserResultFormatting = {
29788
31022
  ${suggestedNextSteps.map((step) => `- ${step}`).join('\n')}
29789
31023
  `}
29790
31024
 
29791
- ## Playback payload
31025
+ ## Playback payload
31026
+
31027
+ \`\`\`json
31028
+ ${JSON.stringify(payload, null, 2)}
31029
+ \`\`\`
31030
+
31031
+ The browser tool could not complete the requested actions.
31032
+ `);
31033
+ },
31034
+ };
31035
+
31036
+ /**
31037
+ * Attempts to locate the specified application on a Linux system using the 'which' command.
31038
+ * Returns the path to the executable if found, or null otherwise.
31039
+ *
31040
+ * @private within the repository
31041
+ */
31042
+ async function locateAppOnLinux({ linuxWhich, }) {
31043
+ try {
31044
+ const result = await $execCommand({ crashOnError: true, command: `which ${linuxWhich}` });
31045
+ return result.trim();
31046
+ }
31047
+ catch (error) {
31048
+ assertsError(error);
31049
+ return null;
31050
+ }
31051
+ }
31052
+ /**
31053
+ * TODO: [🧠][♿] Maybe export through `@promptbook/node`
31054
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
31055
+ */
31056
+
31057
+ /**
31058
+ * Checks if the file is executable
31059
+ *
31060
+ * @private within the repository
31061
+ */
31062
+ async function isExecutable(path, fs) {
31063
+ try {
31064
+ await fs.access(path, fs.constants.X_OK);
31065
+ return true;
31066
+ }
31067
+ catch (error) {
31068
+ return false;
31069
+ }
31070
+ }
31071
+ /**
31072
+ * Note: Not [~🟢~] because it is not directly dependent on `fs
31073
+ * TODO: [🖇] What about symlinks?
31074
+ */
31075
+
31076
+ // Note: Module `userhome` has no types available, so it is imported using `require`
31077
+ // @see https://stackoverflow.com/questions/37000981/how-to-import-node-module-in-typescript-without-type-definitions
31078
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
31079
+ const userhome = require('userhome');
31080
+ /**
31081
+ * Attempts to locate the specified application on a macOS system by checking standard application paths and using mdfind.
31082
+ * Returns the path to the executable if found, or null otherwise.
31083
+ *
31084
+ * @private within the repository
31085
+ */
31086
+ async function locateAppOnMacOs({ macOsName, }) {
31087
+ try {
31088
+ const toExec = `/Contents/MacOS/${macOsName}`;
31089
+ const regPath = `/Applications/${macOsName}.app` + toExec;
31090
+ const altPath = userhome(regPath.slice(1));
31091
+ if (await isExecutable(regPath, $provideFilesystemForNode())) {
31092
+ return regPath;
31093
+ }
31094
+ else if (await isExecutable(altPath, $provideFilesystemForNode())) {
31095
+ return altPath;
31096
+ }
31097
+ const result = await $execCommand({
31098
+ crashOnError: true,
31099
+ command: `mdfind 'kMDItemDisplayName == "${macOsName}" && kMDItemKind == Application'`,
31100
+ });
31101
+ return result.trim() + toExec;
31102
+ }
31103
+ catch (error) {
31104
+ assertsError(error);
31105
+ return null;
31106
+ }
31107
+ }
31108
+ /**
31109
+ * TODO: [🧠][♿] Maybe export through `@promptbook/node`
31110
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
31111
+ */
31112
+
31113
+ /**
31114
+ * Attempts to locate the specified application on a Windows system by searching common installation directories.
31115
+ * Returns the path to the executable if found, or null otherwise.
31116
+ *
31117
+ * @private within the repository
31118
+ */
31119
+ async function locateAppOnWindows({ appName, windowsSuffix, }) {
31120
+ try {
31121
+ const prefixes = [
31122
+ process.env.LOCALAPPDATA,
31123
+ join(process.env.LOCALAPPDATA || '', 'Programs'),
31124
+ process.env.PROGRAMFILES,
31125
+ process.env['PROGRAMFILES(X86)'],
31126
+ ];
31127
+ for (const prefix of prefixes) {
31128
+ const path = prefix + windowsSuffix;
31129
+ if (await isExecutable(path, $provideFilesystemForNode())) {
31130
+ return path;
31131
+ }
31132
+ }
31133
+ throw new Error(`Can not locate app ${appName} on Windows.`);
31134
+ }
31135
+ catch (error) {
31136
+ assertsError(error);
31137
+ return null;
31138
+ }
31139
+ }
31140
+ /**
31141
+ * TODO: [🧠][♿] Maybe export through `@promptbook/node`
31142
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
31143
+ */
29792
31144
 
29793
- \`\`\`json
29794
- ${JSON.stringify(payload, null, 2)}
29795
- \`\`\`
31145
+ /**
31146
+ * Locates an application on the system
31147
+ *
31148
+ * @private within the repository
31149
+ */
31150
+ function locateApp(options) {
31151
+ if (!$isRunningInNode()) {
31152
+ throw new EnvironmentMismatchError('Locating apps works only in Node.js environment');
31153
+ }
31154
+ const { appName, linuxWhich, windowsSuffix, macOsName } = options;
31155
+ if (process.platform === 'win32') {
31156
+ if (windowsSuffix) {
31157
+ return locateAppOnWindows({ appName, windowsSuffix });
31158
+ }
31159
+ else {
31160
+ throw new Error(`${appName} is not available on Windows.`);
31161
+ }
31162
+ }
31163
+ else if (process.platform === 'darwin') {
31164
+ if (macOsName) {
31165
+ return locateAppOnMacOs({ macOsName });
31166
+ }
31167
+ else {
31168
+ throw new Error(`${appName} is not available on macOS.`);
31169
+ }
31170
+ }
31171
+ else {
31172
+ if (linuxWhich) {
31173
+ return locateAppOnLinux({ linuxWhich });
31174
+ }
31175
+ else {
31176
+ throw new Error(`${appName} is not available on Linux.`);
31177
+ }
31178
+ }
31179
+ }
31180
+ /**
31181
+ * TODO: [🧠][♿] Maybe export through `@promptbook/node`
31182
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
31183
+ */
29796
31184
 
29797
- The browser tool could not complete the requested actions.
29798
- `);
29799
- },
29800
- };
31185
+ /**
31186
+ * @@@
31187
+ *
31188
+ * @private within the repository
31189
+ */
31190
+ function locateChrome() {
31191
+ return locateApp({
31192
+ appName: 'Chrome',
31193
+ linuxWhich: 'google-chrome',
31194
+ windowsSuffix: '\\Google\\Chrome\\Application\\chrome.exe',
31195
+ macOsName: 'Google Chrome',
31196
+ });
31197
+ }
29801
31198
 
29802
31199
  /**
29803
31200
  * Creates one standard abort error for cancelled retry loops.
@@ -31062,155 +32459,6 @@ function getAllCommitmentsToolFunctionsForNode() {
31062
32459
  * TODO: [??] Unite `xxxForServer` and `xxxForNode` naming
31063
32460
  */
31064
32461
 
31065
- /**
31066
- * Attempts to locate the specified application on a Linux system using the 'which' command.
31067
- * Returns the path to the executable if found, or null otherwise.
31068
- *
31069
- * @private within the repository
31070
- */
31071
- async function locateAppOnLinux({ linuxWhich, }) {
31072
- try {
31073
- const result = await $execCommand({ crashOnError: true, command: `which ${linuxWhich}` });
31074
- return result.trim();
31075
- }
31076
- catch (error) {
31077
- assertsError(error);
31078
- return null;
31079
- }
31080
- }
31081
- /**
31082
- * TODO: [🧠][♿] Maybe export through `@promptbook/node`
31083
- * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
31084
- */
31085
-
31086
- /**
31087
- * Checks if the file is executable
31088
- *
31089
- * @private within the repository
31090
- */
31091
- async function isExecutable(path, fs) {
31092
- try {
31093
- await fs.access(path, fs.constants.X_OK);
31094
- return true;
31095
- }
31096
- catch (error) {
31097
- return false;
31098
- }
31099
- }
31100
- /**
31101
- * Note: Not [~🟢~] because it is not directly dependent on `fs
31102
- * TODO: [🖇] What about symlinks?
31103
- */
31104
-
31105
- // Note: Module `userhome` has no types available, so it is imported using `require`
31106
- // @see https://stackoverflow.com/questions/37000981/how-to-import-node-module-in-typescript-without-type-definitions
31107
- // eslint-disable-next-line @typescript-eslint/no-var-requires
31108
- const userhome = require('userhome');
31109
- /**
31110
- * Attempts to locate the specified application on a macOS system by checking standard application paths and using mdfind.
31111
- * Returns the path to the executable if found, or null otherwise.
31112
- *
31113
- * @private within the repository
31114
- */
31115
- async function locateAppOnMacOs({ macOsName, }) {
31116
- try {
31117
- const toExec = `/Contents/MacOS/${macOsName}`;
31118
- const regPath = `/Applications/${macOsName}.app` + toExec;
31119
- const altPath = userhome(regPath.slice(1));
31120
- if (await isExecutable(regPath, $provideFilesystemForNode())) {
31121
- return regPath;
31122
- }
31123
- else if (await isExecutable(altPath, $provideFilesystemForNode())) {
31124
- return altPath;
31125
- }
31126
- const result = await $execCommand({
31127
- crashOnError: true,
31128
- command: `mdfind 'kMDItemDisplayName == "${macOsName}" && kMDItemKind == Application'`,
31129
- });
31130
- return result.trim() + toExec;
31131
- }
31132
- catch (error) {
31133
- assertsError(error);
31134
- return null;
31135
- }
31136
- }
31137
- /**
31138
- * TODO: [🧠][♿] Maybe export through `@promptbook/node`
31139
- * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
31140
- */
31141
-
31142
- /**
31143
- * Attempts to locate the specified application on a Windows system by searching common installation directories.
31144
- * Returns the path to the executable if found, or null otherwise.
31145
- *
31146
- * @private within the repository
31147
- */
31148
- async function locateAppOnWindows({ appName, windowsSuffix, }) {
31149
- try {
31150
- const prefixes = [
31151
- process.env.LOCALAPPDATA,
31152
- join(process.env.LOCALAPPDATA || '', 'Programs'),
31153
- process.env.PROGRAMFILES,
31154
- process.env['PROGRAMFILES(X86)'],
31155
- ];
31156
- for (const prefix of prefixes) {
31157
- const path = prefix + windowsSuffix;
31158
- if (await isExecutable(path, $provideFilesystemForNode())) {
31159
- return path;
31160
- }
31161
- }
31162
- throw new Error(`Can not locate app ${appName} on Windows.`);
31163
- }
31164
- catch (error) {
31165
- assertsError(error);
31166
- return null;
31167
- }
31168
- }
31169
- /**
31170
- * TODO: [🧠][♿] Maybe export through `@promptbook/node`
31171
- * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
31172
- */
31173
-
31174
- /**
31175
- * Locates an application on the system
31176
- *
31177
- * @private within the repository
31178
- */
31179
- function locateApp(options) {
31180
- if (!$isRunningInNode()) {
31181
- throw new EnvironmentMismatchError('Locating apps works only in Node.js environment');
31182
- }
31183
- const { appName, linuxWhich, windowsSuffix, macOsName } = options;
31184
- if (process.platform === 'win32') {
31185
- if (windowsSuffix) {
31186
- return locateAppOnWindows({ appName, windowsSuffix });
31187
- }
31188
- else {
31189
- throw new Error(`${appName} is not available on Windows.`);
31190
- }
31191
- }
31192
- else if (process.platform === 'darwin') {
31193
- if (macOsName) {
31194
- return locateAppOnMacOs({ macOsName });
31195
- }
31196
- else {
31197
- throw new Error(`${appName} is not available on macOS.`);
31198
- }
31199
- }
31200
- else {
31201
- if (linuxWhich) {
31202
- return locateAppOnLinux({ linuxWhich });
31203
- }
31204
- else {
31205
- throw new Error(`${appName} is not available on Linux.`);
31206
- }
31207
- }
31208
- }
31209
- /**
31210
- * TODO: [🧠][♿] Maybe export through `@promptbook/node`
31211
- * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
31212
- */
31213
-
31214
32462
  /**
31215
32463
  * Locates the LibreOffice executable on the current system by searching platform-specific paths.
31216
32464
  * Returns the path to the executable if found, or null otherwise.