@promptbook/wizard 0.112.0-57 → 0.112.0-59
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 +273 -244
- package/esm/index.es.js.map +1 -1
- package/esm/src/commitments/_base/BaseCommitmentDefinition.d.ts +26 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/umd/index.umd.js +273 -244
- package/umd/index.umd.js.map +1 -1
- package/umd/src/commitments/_base/BaseCommitmentDefinition.d.ts +26 -0
- package/umd/src/version.d.ts +1 -1
package/esm/index.es.js
CHANGED
|
@@ -38,7 +38,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
38
38
|
* @generated
|
|
39
39
|
* @see https://github.com/webgptorg/promptbook
|
|
40
40
|
*/
|
|
41
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-
|
|
41
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-59';
|
|
42
42
|
/**
|
|
43
43
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
44
44
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -19488,36 +19488,38 @@ function createAggregatedUseCommitmentSystemMessage(type, additionalInstructions
|
|
|
19488
19488
|
switch (type) {
|
|
19489
19489
|
case 'USE TIME':
|
|
19490
19490
|
return spaceTrim$1((block) => `
|
|
19491
|
-
Time and date context
|
|
19492
|
-
|
|
19493
|
-
-
|
|
19491
|
+
## Time and date context
|
|
19492
|
+
|
|
19493
|
+
- It is ${moment().format('MMMM YYYY')} now.
|
|
19494
|
+
- If you need more precise current time information, use the tool \`get_current_time\`.
|
|
19494
19495
|
${block(formatOptionalInstructionBlock('Time instructions', combinedAdditionalInstructions))}
|
|
19495
19496
|
`);
|
|
19496
19497
|
case 'USE BROWSER':
|
|
19497
19498
|
return spaceTrim$1((block) => `
|
|
19498
|
-
|
|
19499
|
-
|
|
19500
|
-
-
|
|
19501
|
-
|
|
19499
|
+
## Browser
|
|
19500
|
+
|
|
19501
|
+
- Use \`fetch_url_content\` to retrieve content from specific URLs (webpages or documents) using scrapers.
|
|
19502
|
+
- Use \`run_browser\` for real interactive browser automation (navigation, clicks, typing, waiting, scrolling).
|
|
19503
|
+
- When you need to know information from a specific website or document, use the tools provided.
|
|
19502
19504
|
${block(formatOptionalInstructionBlock('Browser instructions', combinedAdditionalInstructions))}
|
|
19503
19505
|
`);
|
|
19504
19506
|
case 'USE SEARCH ENGINE':
|
|
19505
19507
|
return spaceTrim$1((block) => `
|
|
19506
|
-
|
|
19507
|
-
|
|
19508
|
-
-
|
|
19509
|
-
-
|
|
19510
|
-
-
|
|
19511
|
-
-
|
|
19508
|
+
## Web Search
|
|
19509
|
+
|
|
19510
|
+
- Use \`web_search\` to find up-to-date information or facts.
|
|
19511
|
+
- When you need to know some information from the internet, use the search tool provided.
|
|
19512
|
+
- Do not make up information when you can search for it.
|
|
19513
|
+
- Do not tell the user you cannot search for information, YOU CAN.
|
|
19512
19514
|
${block(formatOptionalInstructionBlock('Search instructions', combinedAdditionalInstructions))}
|
|
19513
19515
|
`);
|
|
19514
19516
|
case 'USE DEEPSEARCH':
|
|
19515
19517
|
return spaceTrim$1((block) => `
|
|
19516
|
-
|
|
19517
|
-
|
|
19518
|
-
-
|
|
19519
|
-
-
|
|
19520
|
-
-
|
|
19518
|
+
## Deep Research
|
|
19519
|
+
|
|
19520
|
+
- Use \`deep_search\` for broader research tasks that need multi-step investigation, comparison, or synthesis across multiple sources.
|
|
19521
|
+
- Prefer it over quick search when the user asks for a well-grounded brief, report, or deeper investigation.
|
|
19522
|
+
- Do not pretend you cannot research current information when this tool is available.
|
|
19521
19523
|
${block(formatOptionalInstructionBlock('DeepSearch instructions', combinedAdditionalInstructions))}
|
|
19522
19524
|
`);
|
|
19523
19525
|
}
|
|
@@ -19759,6 +19761,49 @@ class BaseCommitmentDefinition {
|
|
|
19759
19761
|
return this.appendToSystemMessage(requirements, commentSection);
|
|
19760
19762
|
}
|
|
19761
19763
|
}
|
|
19764
|
+
/**
|
|
19765
|
+
* Helper method to append a bullet point to an existing `## SectionTitle` section in the system
|
|
19766
|
+
* message, or to create a new section when it does not yet exist.
|
|
19767
|
+
*
|
|
19768
|
+
* Handles the case where the same commitment type appears multiple times in the book source and
|
|
19769
|
+
* all entries should be grouped under one shared heading rather than emitting a duplicate block.
|
|
19770
|
+
*
|
|
19771
|
+
* @param requirements - Current model requirements.
|
|
19772
|
+
* @param sectionTitle - Section title without the `##` prefix.
|
|
19773
|
+
* @param bulletContent - Bullet content without the leading `- ` prefix.
|
|
19774
|
+
* @returns Requirements with the bullet appended to the section.
|
|
19775
|
+
*/
|
|
19776
|
+
appendBulletPointToSection(requirements, sectionTitle, bulletContent) {
|
|
19777
|
+
const sectionHeader = `## ${sectionTitle}`;
|
|
19778
|
+
const bullet = `- ${bulletContent}`;
|
|
19779
|
+
if (requirements.systemMessage.includes(sectionHeader)) {
|
|
19780
|
+
// Append bullet to end of existing section, before the next h2 heading or end of message
|
|
19781
|
+
const newSystemMessage = requirements.systemMessage.replace(new RegExp(`(## ${sectionTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\n\\n)([\\s\\S]*?)(?=\\n\\n##|$)`), `$1$2\n${bullet}`);
|
|
19782
|
+
return { ...requirements, systemMessage: newSystemMessage };
|
|
19783
|
+
}
|
|
19784
|
+
return this.appendToSystemMessage(requirements, `${sectionHeader}\n\n${bullet}`, '\n\n');
|
|
19785
|
+
}
|
|
19786
|
+
/**
|
|
19787
|
+
* Helper method to replace an existing `## SectionTitle` section in the system message, or to
|
|
19788
|
+
* append a new one when the section does not yet exist.
|
|
19789
|
+
*
|
|
19790
|
+
* Use this when a commitment type can appear multiple times and each subsequent occurrence should
|
|
19791
|
+
* update the single shared section rather than appending a duplicate block.
|
|
19792
|
+
*
|
|
19793
|
+
* @param requirements - Current model requirements.
|
|
19794
|
+
* @param sectionTitle - Section title without the `##` prefix.
|
|
19795
|
+
* @param sectionContent - Full section content including the `## Title` header line.
|
|
19796
|
+
* @returns Requirements with the section replaced or appended.
|
|
19797
|
+
*/
|
|
19798
|
+
replaceOrCreateSection(requirements, sectionTitle, sectionContent) {
|
|
19799
|
+
const sectionHeader = `## ${sectionTitle}`;
|
|
19800
|
+
if (requirements.systemMessage.includes(sectionHeader)) {
|
|
19801
|
+
// Replace all text from the heading until the next h2 heading or end of message
|
|
19802
|
+
const newSystemMessage = requirements.systemMessage.replace(new RegExp(`## ${sectionTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?(?=\\n\\n##|$)`), sectionContent);
|
|
19803
|
+
return { ...requirements, systemMessage: newSystemMessage };
|
|
19804
|
+
}
|
|
19805
|
+
return this.appendToSystemMessage(requirements, sectionContent, '\n\n');
|
|
19806
|
+
}
|
|
19762
19807
|
/**
|
|
19763
19808
|
* Gets tool function implementations provided by this commitment
|
|
19764
19809
|
*
|
|
@@ -20220,20 +20265,16 @@ class DictionaryCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
20220
20265
|
if (!trimmedContent) {
|
|
20221
20266
|
return requirements;
|
|
20222
20267
|
}
|
|
20223
|
-
//
|
|
20268
|
+
// Store the entry in metadata for debugging and inspection
|
|
20224
20269
|
const existingDictionary = ((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.DICTIONARY) || '';
|
|
20225
|
-
// Merge the new dictionary entry with existing entries
|
|
20226
20270
|
const mergedDictionary = existingDictionary ? `${existingDictionary}\n${trimmedContent}` : trimmedContent;
|
|
20227
|
-
// Store the merged dictionary in metadata for debugging and inspection
|
|
20228
20271
|
const updatedMetadata = {
|
|
20229
20272
|
...requirements._metadata,
|
|
20230
20273
|
DICTIONARY: mergedDictionary,
|
|
20231
20274
|
};
|
|
20232
|
-
//
|
|
20233
|
-
// Format: "# DICTIONARY\nTerm: definition\nTerm: definition..."
|
|
20234
|
-
const dictionarySection = `# DICTIONARY\n${mergedDictionary}`;
|
|
20275
|
+
// Append each dictionary entry as a bullet point under ## Dictionary
|
|
20235
20276
|
return {
|
|
20236
|
-
...this.
|
|
20277
|
+
...this.appendBulletPointToSection(requirements, 'Dictionary', trimmedContent),
|
|
20237
20278
|
_metadata: updatedMetadata,
|
|
20238
20279
|
};
|
|
20239
20280
|
}
|
|
@@ -20506,10 +20547,10 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
20506
20547
|
if (!trimmedContent) {
|
|
20507
20548
|
return requirements;
|
|
20508
20549
|
}
|
|
20509
|
-
// Add goal to the system message
|
|
20510
|
-
const goalSection =
|
|
20550
|
+
// Add goal as a proper h2 section to the system message
|
|
20551
|
+
const goalSection = `## Goal\n\n${trimmedContent}`;
|
|
20511
20552
|
const requirementsWithGoal = this.appendToSystemMessage(requirements, goalSection, '\n\n');
|
|
20512
|
-
return this.appendToPromptSuffix(requirementsWithGoal,
|
|
20553
|
+
return this.appendToPromptSuffix(requirementsWithGoal, trimmedContent);
|
|
20513
20554
|
}
|
|
20514
20555
|
}
|
|
20515
20556
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -20893,11 +20934,8 @@ class LanguageCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
20893
20934
|
if (!trimmedContent) {
|
|
20894
20935
|
return requirements;
|
|
20895
20936
|
}
|
|
20896
|
-
// Add language
|
|
20897
|
-
const languageSection =
|
|
20898
|
-
${block(trimmedContent)}
|
|
20899
|
-
<- You are speaking these languages in your responses to the user.
|
|
20900
|
-
`));
|
|
20937
|
+
// Add language as a bullet under a ## Language section
|
|
20938
|
+
const languageSection = `## Language\n\n- Your language is ${trimmedContent}`;
|
|
20901
20939
|
return this.appendToSystemMessage(requirements, languageSection, '\n\n');
|
|
20902
20940
|
}
|
|
20903
20941
|
}
|
|
@@ -20922,15 +20960,16 @@ const MemoryToolNames = {
|
|
|
20922
20960
|
*/
|
|
20923
20961
|
function createMemorySystemMessage(extraInstructions) {
|
|
20924
20962
|
return spaceTrim$1((block) => `
|
|
20925
|
-
Memory
|
|
20926
|
-
|
|
20927
|
-
-
|
|
20928
|
-
-
|
|
20929
|
-
-
|
|
20930
|
-
-
|
|
20931
|
-
-
|
|
20932
|
-
-
|
|
20933
|
-
-
|
|
20963
|
+
## Memory
|
|
20964
|
+
|
|
20965
|
+
- Prefer storing agent-scoped memories; only make them global when the fact should apply across all your agents.
|
|
20966
|
+
- You can use persistent user memory tools.
|
|
20967
|
+
- Use \`${MemoryToolNames.retrieve}\` to load relevant memory before answering.
|
|
20968
|
+
- Use \`${MemoryToolNames.store}\` to save stable user-specific facts that improve future help.
|
|
20969
|
+
- Use \`${MemoryToolNames.update}\` to refresh an existing memory when the content changes.
|
|
20970
|
+
- Use \`${MemoryToolNames.delete}\` to delete memories that are no longer accurate (deletions are soft and hidden from future queries).
|
|
20971
|
+
- Store concise memory items and avoid duplicates.
|
|
20972
|
+
- Never claim memory was saved or loaded unless the tool confirms it.
|
|
20934
20973
|
${block(extraInstructions)}
|
|
20935
20974
|
`);
|
|
20936
20975
|
}
|
|
@@ -21748,10 +21787,8 @@ class MessageCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
21748
21787
|
if (!trimmedContent) {
|
|
21749
21788
|
return requirements;
|
|
21750
21789
|
}
|
|
21751
|
-
// Create message section for system message
|
|
21752
|
-
const messageSection = `Previous Message: ${trimmedContent}`;
|
|
21753
21790
|
// Messages represent conversation history and should be included for context
|
|
21754
|
-
return this.
|
|
21791
|
+
return this.appendBulletPointToSection(requirements, 'Previous messages', trimmedContent);
|
|
21755
21792
|
}
|
|
21756
21793
|
}
|
|
21757
21794
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -26545,10 +26582,9 @@ class RuleCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
26545
26582
|
if (!trimmedContent) {
|
|
26546
26583
|
return requirements;
|
|
26547
26584
|
}
|
|
26548
|
-
//
|
|
26549
|
-
const
|
|
26550
|
-
|
|
26551
|
-
return this.appendToPromptSuffix(requirementsWithRule, ruleSection);
|
|
26585
|
+
// Group all rules under a single ## Rules section as bullet points
|
|
26586
|
+
const requirementsWithRule = this.appendBulletPointToSection(requirements, 'Rules', trimmedContent);
|
|
26587
|
+
return this.appendToPromptSuffix(requirementsWithRule, `- ${trimmedContent}`);
|
|
26552
26588
|
}
|
|
26553
26589
|
}
|
|
26554
26590
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -26784,10 +26820,8 @@ class ScenarioCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
26784
26820
|
if (!trimmedContent) {
|
|
26785
26821
|
return requirements;
|
|
26786
26822
|
}
|
|
26787
|
-
// Create scenario section for system message
|
|
26788
|
-
const scenarioSection = `Scenario: ${trimmedContent}`;
|
|
26789
26823
|
// Scenarios provide important contextual information that affects behavior
|
|
26790
|
-
return this.
|
|
26824
|
+
return this.appendBulletPointToSection(requirements, 'Scenarios', trimmedContent);
|
|
26791
26825
|
}
|
|
26792
26826
|
}
|
|
26793
26827
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -27170,8 +27204,8 @@ const teamToolTitles = {};
|
|
|
27170
27204
|
* @private
|
|
27171
27205
|
*/
|
|
27172
27206
|
const TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES = [
|
|
27173
|
-
'-
|
|
27174
|
-
'-
|
|
27207
|
+
'- If a teammate is relevant to the request, consult that teammate using the matching tool.',
|
|
27208
|
+
'- Do not ask the user for information that a listed teammate can provide directly.',
|
|
27175
27209
|
];
|
|
27176
27210
|
/**
|
|
27177
27211
|
* Constant for remote agents by Url.
|
|
@@ -27299,7 +27333,7 @@ class TeamCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
27299
27333
|
toolName: entry.toolName,
|
|
27300
27334
|
});
|
|
27301
27335
|
}
|
|
27302
|
-
const teamSystemMessage = this.createSystemMessageSection('Teammates
|
|
27336
|
+
const teamSystemMessage = this.createSystemMessageSection('Teammates', buildTeamSystemMessageBody(teamEntries));
|
|
27303
27337
|
return this.appendToSystemMessage({
|
|
27304
27338
|
...requirements,
|
|
27305
27339
|
tools: updatedTools,
|
|
@@ -27987,96 +28021,6 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
27987
28021
|
}
|
|
27988
28022
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
27989
28023
|
|
|
27990
|
-
/**
|
|
27991
|
-
* Base Google Calendar API URL.
|
|
27992
|
-
*
|
|
27993
|
-
* @private constant of callGoogleCalendarApi
|
|
27994
|
-
*/
|
|
27995
|
-
const GOOGLE_CALENDAR_API_BASE_URL = 'https://www.googleapis.com/calendar/v3';
|
|
27996
|
-
/**
|
|
27997
|
-
* Runs one Google Calendar API request and parses JSON response payload.
|
|
27998
|
-
*
|
|
27999
|
-
* @private function of UseCalendarCommitmentDefinition
|
|
28000
|
-
*/
|
|
28001
|
-
async function callGoogleCalendarApi(accessToken, options) {
|
|
28002
|
-
const url = new URL(options.path, GOOGLE_CALENDAR_API_BASE_URL);
|
|
28003
|
-
if (options.query) {
|
|
28004
|
-
for (const [key, value] of Object.entries(options.query)) {
|
|
28005
|
-
if (value && value.trim()) {
|
|
28006
|
-
url.searchParams.set(key, value);
|
|
28007
|
-
}
|
|
28008
|
-
}
|
|
28009
|
-
}
|
|
28010
|
-
const response = await fetch(url.toString(), {
|
|
28011
|
-
method: options.method,
|
|
28012
|
-
headers: {
|
|
28013
|
-
Authorization: `Bearer ${accessToken}`,
|
|
28014
|
-
Accept: 'application/json',
|
|
28015
|
-
'Content-Type': 'application/json',
|
|
28016
|
-
},
|
|
28017
|
-
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
28018
|
-
});
|
|
28019
|
-
const textPayload = await response.text();
|
|
28020
|
-
const parsedPayload = tryParseJson$2(textPayload);
|
|
28021
|
-
if (options.allowNotFound && response.status === 404) {
|
|
28022
|
-
return null;
|
|
28023
|
-
}
|
|
28024
|
-
if (!response.ok) {
|
|
28025
|
-
throw new Error(spaceTrim$1(`
|
|
28026
|
-
Google Calendar API request failed (${response.status} ${response.statusText}):
|
|
28027
|
-
${extractGoogleCalendarApiErrorMessage(parsedPayload, textPayload)}
|
|
28028
|
-
`));
|
|
28029
|
-
}
|
|
28030
|
-
return parsedPayload;
|
|
28031
|
-
}
|
|
28032
|
-
/**
|
|
28033
|
-
* Parses raw text into JSON when possible.
|
|
28034
|
-
*
|
|
28035
|
-
* @private function of callGoogleCalendarApi
|
|
28036
|
-
*/
|
|
28037
|
-
function tryParseJson$2(rawText) {
|
|
28038
|
-
if (!rawText.trim()) {
|
|
28039
|
-
return {};
|
|
28040
|
-
}
|
|
28041
|
-
try {
|
|
28042
|
-
return JSON.parse(rawText);
|
|
28043
|
-
}
|
|
28044
|
-
catch (_a) {
|
|
28045
|
-
return rawText;
|
|
28046
|
-
}
|
|
28047
|
-
}
|
|
28048
|
-
/**
|
|
28049
|
-
* Extracts a user-friendly Google Calendar API error message.
|
|
28050
|
-
*
|
|
28051
|
-
* @private function of callGoogleCalendarApi
|
|
28052
|
-
*/
|
|
28053
|
-
function extractGoogleCalendarApiErrorMessage(parsedPayload, fallbackText) {
|
|
28054
|
-
if (parsedPayload && typeof parsedPayload === 'object') {
|
|
28055
|
-
const payload = parsedPayload;
|
|
28056
|
-
const errorPayload = payload.error;
|
|
28057
|
-
if (errorPayload && typeof errorPayload === 'object') {
|
|
28058
|
-
const normalizedErrorPayload = errorPayload;
|
|
28059
|
-
const message = typeof normalizedErrorPayload.message === 'string' ? normalizedErrorPayload.message : '';
|
|
28060
|
-
const errors = Array.isArray(normalizedErrorPayload.errors) ? normalizedErrorPayload.errors : [];
|
|
28061
|
-
const flattenedErrors = errors
|
|
28062
|
-
.map((errorEntry) => {
|
|
28063
|
-
if (!errorEntry || typeof errorEntry !== 'object') {
|
|
28064
|
-
return '';
|
|
28065
|
-
}
|
|
28066
|
-
const normalizedErrorEntry = errorEntry;
|
|
28067
|
-
const detailMessage = typeof normalizedErrorEntry.message === 'string' ? normalizedErrorEntry.message : '';
|
|
28068
|
-
const reason = typeof normalizedErrorEntry.reason === 'string' ? normalizedErrorEntry.reason : '';
|
|
28069
|
-
return [detailMessage, reason].filter(Boolean).join(' | ');
|
|
28070
|
-
})
|
|
28071
|
-
.filter(Boolean);
|
|
28072
|
-
if (message || flattenedErrors.length > 0) {
|
|
28073
|
-
return [message, ...flattenedErrors].filter(Boolean).join(' | ');
|
|
28074
|
-
}
|
|
28075
|
-
}
|
|
28076
|
-
}
|
|
28077
|
-
return fallbackText || 'Unknown Google Calendar API error';
|
|
28078
|
-
}
|
|
28079
|
-
|
|
28080
28024
|
/**
|
|
28081
28025
|
* Hostnames accepted for Google Calendar references.
|
|
28082
28026
|
*
|
|
@@ -28258,6 +28202,96 @@ function removeTokenFromLine(line, token) {
|
|
|
28258
28202
|
}
|
|
28259
28203
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
28260
28204
|
|
|
28205
|
+
/**
|
|
28206
|
+
* Base Google Calendar API URL.
|
|
28207
|
+
*
|
|
28208
|
+
* @private constant of callGoogleCalendarApi
|
|
28209
|
+
*/
|
|
28210
|
+
const GOOGLE_CALENDAR_API_BASE_URL = 'https://www.googleapis.com/calendar/v3';
|
|
28211
|
+
/**
|
|
28212
|
+
* Runs one Google Calendar API request and parses JSON response payload.
|
|
28213
|
+
*
|
|
28214
|
+
* @private function of UseCalendarCommitmentDefinition
|
|
28215
|
+
*/
|
|
28216
|
+
async function callGoogleCalendarApi(accessToken, options) {
|
|
28217
|
+
const url = new URL(options.path, GOOGLE_CALENDAR_API_BASE_URL);
|
|
28218
|
+
if (options.query) {
|
|
28219
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
28220
|
+
if (value && value.trim()) {
|
|
28221
|
+
url.searchParams.set(key, value);
|
|
28222
|
+
}
|
|
28223
|
+
}
|
|
28224
|
+
}
|
|
28225
|
+
const response = await fetch(url.toString(), {
|
|
28226
|
+
method: options.method,
|
|
28227
|
+
headers: {
|
|
28228
|
+
Authorization: `Bearer ${accessToken}`,
|
|
28229
|
+
Accept: 'application/json',
|
|
28230
|
+
'Content-Type': 'application/json',
|
|
28231
|
+
},
|
|
28232
|
+
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
28233
|
+
});
|
|
28234
|
+
const textPayload = await response.text();
|
|
28235
|
+
const parsedPayload = tryParseJson$2(textPayload);
|
|
28236
|
+
if (options.allowNotFound && response.status === 404) {
|
|
28237
|
+
return null;
|
|
28238
|
+
}
|
|
28239
|
+
if (!response.ok) {
|
|
28240
|
+
throw new Error(spaceTrim$1(`
|
|
28241
|
+
Google Calendar API request failed (${response.status} ${response.statusText}):
|
|
28242
|
+
${extractGoogleCalendarApiErrorMessage(parsedPayload, textPayload)}
|
|
28243
|
+
`));
|
|
28244
|
+
}
|
|
28245
|
+
return parsedPayload;
|
|
28246
|
+
}
|
|
28247
|
+
/**
|
|
28248
|
+
* Parses raw text into JSON when possible.
|
|
28249
|
+
*
|
|
28250
|
+
* @private function of callGoogleCalendarApi
|
|
28251
|
+
*/
|
|
28252
|
+
function tryParseJson$2(rawText) {
|
|
28253
|
+
if (!rawText.trim()) {
|
|
28254
|
+
return {};
|
|
28255
|
+
}
|
|
28256
|
+
try {
|
|
28257
|
+
return JSON.parse(rawText);
|
|
28258
|
+
}
|
|
28259
|
+
catch (_a) {
|
|
28260
|
+
return rawText;
|
|
28261
|
+
}
|
|
28262
|
+
}
|
|
28263
|
+
/**
|
|
28264
|
+
* Extracts a user-friendly Google Calendar API error message.
|
|
28265
|
+
*
|
|
28266
|
+
* @private function of callGoogleCalendarApi
|
|
28267
|
+
*/
|
|
28268
|
+
function extractGoogleCalendarApiErrorMessage(parsedPayload, fallbackText) {
|
|
28269
|
+
if (parsedPayload && typeof parsedPayload === 'object') {
|
|
28270
|
+
const payload = parsedPayload;
|
|
28271
|
+
const errorPayload = payload.error;
|
|
28272
|
+
if (errorPayload && typeof errorPayload === 'object') {
|
|
28273
|
+
const normalizedErrorPayload = errorPayload;
|
|
28274
|
+
const message = typeof normalizedErrorPayload.message === 'string' ? normalizedErrorPayload.message : '';
|
|
28275
|
+
const errors = Array.isArray(normalizedErrorPayload.errors) ? normalizedErrorPayload.errors : [];
|
|
28276
|
+
const flattenedErrors = errors
|
|
28277
|
+
.map((errorEntry) => {
|
|
28278
|
+
if (!errorEntry || typeof errorEntry !== 'object') {
|
|
28279
|
+
return '';
|
|
28280
|
+
}
|
|
28281
|
+
const normalizedErrorEntry = errorEntry;
|
|
28282
|
+
const detailMessage = typeof normalizedErrorEntry.message === 'string' ? normalizedErrorEntry.message : '';
|
|
28283
|
+
const reason = typeof normalizedErrorEntry.reason === 'string' ? normalizedErrorEntry.reason : '';
|
|
28284
|
+
return [detailMessage, reason].filter(Boolean).join(' | ');
|
|
28285
|
+
})
|
|
28286
|
+
.filter(Boolean);
|
|
28287
|
+
if (message || flattenedErrors.length > 0) {
|
|
28288
|
+
return [message, ...flattenedErrors].filter(Boolean).join(' | ');
|
|
28289
|
+
}
|
|
28290
|
+
}
|
|
28291
|
+
}
|
|
28292
|
+
return fallbackText || 'Unknown Google Calendar API error';
|
|
28293
|
+
}
|
|
28294
|
+
|
|
28261
28295
|
/**
|
|
28262
28296
|
* Wallet metadata used by USE CALENDAR when resolving Google Calendar credentials.
|
|
28263
28297
|
*
|
|
@@ -29158,18 +29192,20 @@ class UseCalendarCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
29158
29192
|
if (parsedCommitment.calendar) {
|
|
29159
29193
|
addConfiguredCalendarIfMissing(existingConfiguredCalendars, parsedCommitment.calendar);
|
|
29160
29194
|
}
|
|
29161
|
-
const
|
|
29162
|
-
? existingConfiguredCalendars
|
|
29163
|
-
|
|
29164
|
-
`- ${calendar.provider}: ${calendar.url}`,
|
|
29165
|
-
calendar.scopes.length > 0 ? ` scopes: ${calendar.scopes.join(', ')}` : '',
|
|
29166
|
-
]
|
|
29167
|
-
.filter(Boolean)
|
|
29168
|
-
.join('\n'))
|
|
29169
|
-
.join('\n')
|
|
29170
|
-
: '- Calendar is resolved from runtime context';
|
|
29195
|
+
const calendarBullets = existingConfiguredCalendars.length > 0
|
|
29196
|
+
? existingConfiguredCalendars.map((calendar) => `- ${calendar.provider}: ${calendar.url}`).join('\n')
|
|
29197
|
+
: '- Calendar is resolved from runtime context';
|
|
29171
29198
|
const extraInstructions = formatOptionalInstructionBlock('Calendar instructions', parsedCommitment.instructions);
|
|
29172
|
-
|
|
29199
|
+
const calendarSectionContent = spaceTrim$1((block) => `
|
|
29200
|
+
## Calendar
|
|
29201
|
+
|
|
29202
|
+
- 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.
|
|
29203
|
+
- Supported operations include read, create, update, delete, invite guests, and reminders.
|
|
29204
|
+
- Configured calendars:
|
|
29205
|
+
${block(calendarBullets)}
|
|
29206
|
+
${block(extraInstructions)}
|
|
29207
|
+
`);
|
|
29208
|
+
return this.replaceOrCreateSection({
|
|
29173
29209
|
...requirements,
|
|
29174
29210
|
tools: createUseCalendarTools(requirements.tools || []),
|
|
29175
29211
|
_metadata: {
|
|
@@ -29177,16 +29213,7 @@ class UseCalendarCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
29177
29213
|
useCalendar: true,
|
|
29178
29214
|
useCalendars: existingConfiguredCalendars,
|
|
29179
29215
|
},
|
|
29180
|
-
},
|
|
29181
|
-
Calendar tools:
|
|
29182
|
-
- You can inspect and manage events in configured calendars.
|
|
29183
|
-
- Supported operations include read, create, update, delete, invite guests, and reminders.
|
|
29184
|
-
- Configured calendars:
|
|
29185
|
-
${block(calendarsList)}
|
|
29186
|
-
- USE CALENDAR credentials are read from wallet records (ACCESS_TOKEN, service "${UseCalendarWallet.service}", key "${UseCalendarWallet.key}").
|
|
29187
|
-
- If credentials are missing, ask user to connect calendar credentials in host UI and/or add them to wallet.
|
|
29188
|
-
${block(extraInstructions)}
|
|
29189
|
-
`));
|
|
29216
|
+
}, 'Calendar', calendarSectionContent);
|
|
29190
29217
|
}
|
|
29191
29218
|
/**
|
|
29192
29219
|
* Gets human-readable titles for tool functions provided by this commitment.
|
|
@@ -29523,18 +29550,6 @@ async function sendEmailViaBrowser(args, agentsServerUrl) {
|
|
|
29523
29550
|
* @private internal USE EMAIL constant
|
|
29524
29551
|
*/
|
|
29525
29552
|
const SEND_EMAIL_TOOL_NAME = 'send_email';
|
|
29526
|
-
/**
|
|
29527
|
-
* Wallet service used for SMTP credentials required by USE EMAIL.
|
|
29528
|
-
*
|
|
29529
|
-
* @private internal USE EMAIL constant
|
|
29530
|
-
*/
|
|
29531
|
-
const USE_EMAIL_SMTP_WALLET_SERVICE = 'smtp';
|
|
29532
|
-
/**
|
|
29533
|
-
* Wallet key used for SMTP credentials required by USE EMAIL.
|
|
29534
|
-
*
|
|
29535
|
-
* @private internal USE EMAIL constant
|
|
29536
|
-
*/
|
|
29537
|
-
const USE_EMAIL_SMTP_WALLET_KEY = 'use-email-smtp-credentials';
|
|
29538
29553
|
/**
|
|
29539
29554
|
* USE EMAIL commitment definition.
|
|
29540
29555
|
*
|
|
@@ -29593,31 +29608,41 @@ class UseEmailCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
29593
29608
|
`);
|
|
29594
29609
|
}
|
|
29595
29610
|
applyToAgentModelRequirements(requirements, content) {
|
|
29611
|
+
var _a;
|
|
29596
29612
|
const parsedCommitment = parseUseEmailCommitmentContent(content);
|
|
29597
|
-
const extraInstructions = formatOptionalInstructionBlock('Email instructions', parsedCommitment.instructions);
|
|
29598
|
-
const senderInstruction = parsedCommitment.senderEmail
|
|
29599
|
-
? `- Default sender address from commitment: "${parsedCommitment.senderEmail}".`
|
|
29600
|
-
: '';
|
|
29601
29613
|
const updatedTools = addUseEmailTools(requirements.tools || []);
|
|
29602
|
-
|
|
29614
|
+
// Collect all configured sender emails across multiple USE EMAIL commitments
|
|
29615
|
+
const existingSenders = Array.isArray((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.useEmailSenders)
|
|
29616
|
+
? [...requirements._metadata.useEmailSenders]
|
|
29617
|
+
: [];
|
|
29618
|
+
if (parsedCommitment.senderEmail && !existingSenders.includes(parsedCommitment.senderEmail)) {
|
|
29619
|
+
existingSenders.push(parsedCommitment.senderEmail);
|
|
29620
|
+
}
|
|
29621
|
+
const senderBullets = existingSenders.length > 0
|
|
29622
|
+
? existingSenders
|
|
29623
|
+
.map((email, index) => index === 0
|
|
29624
|
+
? `- Default sender address: "${email}".`
|
|
29625
|
+
: `- Additional sender address: "${email}".`)
|
|
29626
|
+
.join('\n')
|
|
29627
|
+
: '';
|
|
29628
|
+
const extraInstructions = formatOptionalInstructionBlock('Email instructions', parsedCommitment.instructions);
|
|
29629
|
+
const emailSectionContent = spaceTrim$1((block) => `
|
|
29630
|
+
## Emails
|
|
29631
|
+
|
|
29632
|
+
- Use \`${SEND_EMAIL_TOOL_NAME}\` to send outbound emails.
|
|
29633
|
+
${block(senderBullets)}
|
|
29634
|
+
${block(extraInstructions)}
|
|
29635
|
+
`);
|
|
29636
|
+
return this.replaceOrCreateSection({
|
|
29603
29637
|
...requirements,
|
|
29604
29638
|
tools: updatedTools,
|
|
29605
29639
|
_metadata: {
|
|
29606
29640
|
...requirements._metadata,
|
|
29607
29641
|
useEmail: true,
|
|
29608
29642
|
...(parsedCommitment.senderEmail ? { useEmailSender: parsedCommitment.senderEmail } : {}),
|
|
29643
|
+
useEmailSenders: existingSenders,
|
|
29609
29644
|
},
|
|
29610
|
-
},
|
|
29611
|
-
Email tool:
|
|
29612
|
-
- Use "${SEND_EMAIL_TOOL_NAME}" to send outbound emails.
|
|
29613
|
-
- Prefer \`message\` argument compatible with Promptbook \`Message\` type.
|
|
29614
|
-
- Include subject in \`message.metadata.subject\` (or use legacy \`subject\` argument).
|
|
29615
|
-
- USE EMAIL credentials are read from wallet records (ACCESS_TOKEN, service "${USE_EMAIL_SMTP_WALLET_SERVICE}", key "${USE_EMAIL_SMTP_WALLET_KEY}").
|
|
29616
|
-
- Wallet secret must contain SMTP credentials in JSON format with fields \`host\`, \`port\`, \`secure\`, \`username\`, \`password\`.
|
|
29617
|
-
- If credentials are missing, ask user to add wallet credentials.
|
|
29618
|
-
${block(senderInstruction)}
|
|
29619
|
-
${block(extraInstructions)}
|
|
29620
|
-
`));
|
|
29645
|
+
}, 'Emails', emailSectionContent);
|
|
29621
29646
|
}
|
|
29622
29647
|
/**
|
|
29623
29648
|
* Gets human-readable titles for tool functions provided by this commitment.
|
|
@@ -29653,13 +29678,13 @@ function addUseEmailTools(existingTools) {
|
|
|
29653
29678
|
...existingTools,
|
|
29654
29679
|
{
|
|
29655
29680
|
name: SEND_EMAIL_TOOL_NAME,
|
|
29656
|
-
description: 'Send an outbound email
|
|
29681
|
+
description: 'Send an outbound email.',
|
|
29657
29682
|
parameters: {
|
|
29658
29683
|
type: 'object',
|
|
29659
29684
|
properties: {
|
|
29660
29685
|
message: {
|
|
29661
29686
|
type: 'object',
|
|
29662
|
-
description: '
|
|
29687
|
+
description: 'Email payload. Use metadata.subject for the subject line.',
|
|
29663
29688
|
},
|
|
29664
29689
|
to: {
|
|
29665
29690
|
type: 'string',
|
|
@@ -29763,13 +29788,14 @@ class UseImageGeneratorCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
29763
29788
|
useImageGenerator: content || true,
|
|
29764
29789
|
},
|
|
29765
29790
|
}, spaceTrim$1((block) => `
|
|
29766
|
-
Image generation
|
|
29767
|
-
|
|
29768
|
-
-
|
|
29769
|
-
|
|
29770
|
-
|
|
29771
|
-
-
|
|
29772
|
-
-
|
|
29791
|
+
## Image generation
|
|
29792
|
+
|
|
29793
|
+
- You do not generate images directly and you do not call any image tool.
|
|
29794
|
+
- When the user asks for an image, include markdown notation in your message:
|
|
29795
|
+
\`\`
|
|
29796
|
+
- Keep \`<alt text>\` short and descriptive.
|
|
29797
|
+
- Keep \`<prompt>\` detailed so the generated image matches the request.
|
|
29798
|
+
- You can include normal explanatory text before and after the notation.
|
|
29773
29799
|
${block(extraInstructions)}
|
|
29774
29800
|
`));
|
|
29775
29801
|
}
|
|
@@ -29949,11 +29975,12 @@ class UsePopupCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
29949
29975
|
usePopup: content || true,
|
|
29950
29976
|
},
|
|
29951
29977
|
}, spaceTrim$1((block) => `
|
|
29952
|
-
|
|
29953
|
-
|
|
29954
|
-
-
|
|
29978
|
+
## Popup
|
|
29979
|
+
|
|
29980
|
+
- You can open a popup window with a specific URL using the tool \`open_popup\`.
|
|
29981
|
+
- Use this when you want the user to see or interact with a specific website.
|
|
29955
29982
|
${block(extraInstructions)}
|
|
29956
|
-
|
|
29983
|
+
`));
|
|
29957
29984
|
}
|
|
29958
29985
|
/**
|
|
29959
29986
|
* Gets human-readable titles for tool functions provided by this commitment.
|
|
@@ -30127,11 +30154,12 @@ class UsePrivacyCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
30127
30154
|
usePrivacy: content || true,
|
|
30128
30155
|
},
|
|
30129
30156
|
}, spaceTrim$1((block) => `
|
|
30130
|
-
Privacy
|
|
30131
|
-
|
|
30132
|
-
-
|
|
30133
|
-
-
|
|
30134
|
-
-
|
|
30157
|
+
## Privacy
|
|
30158
|
+
|
|
30159
|
+
- Use \`${TURN_PRIVACY_ON_TOOL_NAME}\` when the user asks for a private/sensitive conversation.
|
|
30160
|
+
- This tool requests a UI confirmation dialog. Private mode is enabled only after user confirms.
|
|
30161
|
+
- Current implementation uses the existing chat private mode (no chat persistence, memory persistence, or self-learning while active).
|
|
30162
|
+
- Do not claim that end-to-end encryption is implemented yet.
|
|
30135
30163
|
${block(extraInstructions)}
|
|
30136
30164
|
`));
|
|
30137
30165
|
}
|
|
@@ -31755,9 +31783,16 @@ class UseProjectCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
31755
31783
|
}
|
|
31756
31784
|
const existingConfiguredProjects = normalizeConfiguredProjects((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.useProjects);
|
|
31757
31785
|
addConfiguredProjectIfMissing(existingConfiguredProjects, parsedCommitment.repository);
|
|
31758
|
-
const repositoriesList = existingConfiguredProjects.map((project) => `- ${project.url}`).join('\n');
|
|
31759
31786
|
const extraInstructions = formatOptionalInstructionBlock('Project instructions', parsedCommitment.instructions);
|
|
31760
|
-
|
|
31787
|
+
const sectionContent = spaceTrim$1((block) => `
|
|
31788
|
+
- You can inspect and edit configured GitHub repositories using project tools.
|
|
31789
|
+
- Configured repositories:
|
|
31790
|
+
${block(existingConfiguredProjects.map((project) => `- ${project.url}`).join('\n'))}
|
|
31791
|
+
- When a repository is not obvious from context, pass \`repository\` in tool arguments explicitly.
|
|
31792
|
+
- If credentials are missing, ask the user to connect their GitHub account in the host UI.
|
|
31793
|
+
${block(extraInstructions)}
|
|
31794
|
+
`);
|
|
31795
|
+
return this.replaceOrCreateSection({
|
|
31761
31796
|
...requirements,
|
|
31762
31797
|
tools: createUseProjectTools(requirements.tools || []),
|
|
31763
31798
|
_metadata: {
|
|
@@ -31765,16 +31800,7 @@ class UseProjectCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
31765
31800
|
useProject: true,
|
|
31766
31801
|
useProjects: existingConfiguredProjects,
|
|
31767
31802
|
},
|
|
31768
|
-
},
|
|
31769
|
-
Project tools:
|
|
31770
|
-
- You can inspect and edit configured GitHub repositories using project tools.
|
|
31771
|
-
- Configured repositories:
|
|
31772
|
-
${block(repositoriesList)}
|
|
31773
|
-
- When a repository is not obvious from context, pass "repository" in tool arguments explicitly.
|
|
31774
|
-
- USE PROJECT credentials are read from wallet records (ACCESS_TOKEN, service "${UseProjectWallet.service}", key "${UseProjectWallet.key}").
|
|
31775
|
-
- If credentials are missing, ask the user to connect credentials in host UI and/or add them to wallet.
|
|
31776
|
-
${block(extraInstructions)}
|
|
31777
|
-
`));
|
|
31803
|
+
}, 'GitHub repositories', sectionContent);
|
|
31778
31804
|
}
|
|
31779
31805
|
/**
|
|
31780
31806
|
* Gets human-readable titles for tool functions provided by this commitment.
|
|
@@ -32121,11 +32147,12 @@ class UseSpawnCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
32121
32147
|
useSpawn: content || true,
|
|
32122
32148
|
},
|
|
32123
32149
|
}, spaceTrim$1((block) => `
|
|
32124
|
-
Spawning agents
|
|
32125
|
-
|
|
32126
|
-
-
|
|
32127
|
-
-
|
|
32128
|
-
-
|
|
32150
|
+
## Spawning agents
|
|
32151
|
+
|
|
32152
|
+
- Use \`${SPAWN_AGENT_TOOL_NAME}\` only when user asks to create a persistent new agent.
|
|
32153
|
+
- Pass full agent source in \`source\`.
|
|
32154
|
+
- Keep \`source\` concise; the maximum accepted length is ${CREATE_AGENT_INPUT_SOURCE_MAX_LENGTH} characters.
|
|
32155
|
+
- Do not add unknown fields in tool arguments.
|
|
32129
32156
|
${block(extraInstructions)}
|
|
32130
32157
|
`));
|
|
32131
32158
|
}
|
|
@@ -32159,13 +32186,14 @@ class UseSpawnCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
32159
32186
|
*/
|
|
32160
32187
|
function createTimeoutSystemMessage(extraInstructions) {
|
|
32161
32188
|
return spaceTrim$1((block) => `
|
|
32162
|
-
Timeout scheduling
|
|
32163
|
-
|
|
32164
|
-
-
|
|
32165
|
-
-
|
|
32166
|
-
-
|
|
32167
|
-
-
|
|
32168
|
-
-
|
|
32189
|
+
## Timeout scheduling
|
|
32190
|
+
|
|
32191
|
+
- Use \`set_timeout\` to wake this same chat thread in the future.
|
|
32192
|
+
- Use \`list_timeouts\` to review timeout ids/details across all chats for the same user+agent scope.
|
|
32193
|
+
- \`cancel_timeout\` accepts either one timeout id or \`allActive: true\` to cancel all active timeouts in this same user+agent scope.
|
|
32194
|
+
- Use \`update_timeout\` to pause/resume, edit next run, edit recurrence, or update timeout payload details.
|
|
32195
|
+
- 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\`.
|
|
32196
|
+
- Do not claim a timer was set or cancelled unless the tool confirms it.
|
|
32169
32197
|
${block(extraInstructions)}
|
|
32170
32198
|
`);
|
|
32171
32199
|
}
|
|
@@ -33218,10 +33246,11 @@ class UseUserLocationCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
33218
33246
|
useUserLocation: content || true,
|
|
33219
33247
|
},
|
|
33220
33248
|
}, spaceTrim$1((block) => `
|
|
33221
|
-
User location
|
|
33222
|
-
|
|
33223
|
-
-
|
|
33224
|
-
-
|
|
33249
|
+
## User location
|
|
33250
|
+
|
|
33251
|
+
- Use \`${GET_USER_LOCATION_TOOL_NAME}\` only when location is needed for a better answer.
|
|
33252
|
+
- If the tool returns "unavailable" or "permission-denied", ask user to share location or provide city manually.
|
|
33253
|
+
- Do not invent coordinates or local facts when location is unavailable.
|
|
33225
33254
|
${block(extraInstructions)}
|
|
33226
33255
|
`));
|
|
33227
33256
|
}
|
|
@@ -34235,7 +34264,7 @@ function createExampleInteractionsContent(parseResult, samples) {
|
|
|
34235
34264
|
if (examples.length === 0) {
|
|
34236
34265
|
return null;
|
|
34237
34266
|
}
|
|
34238
|
-
return
|
|
34267
|
+
return `## Sample of communication with the agent:\n\n${examples.join('\n\n')}`;
|
|
34239
34268
|
}
|
|
34240
34269
|
/**
|
|
34241
34270
|
* Collects the individual lines used in the example interaction section.
|
|
@@ -34251,11 +34280,11 @@ function collectExampleInteractionLines(parseResult, samples) {
|
|
|
34251
34280
|
const examples = [];
|
|
34252
34281
|
const initialMessage = (_a = parseResult.commitments.find((commitment) => commitment.type === 'INITIAL MESSAGE')) === null || _a === void 0 ? void 0 : _a.content;
|
|
34253
34282
|
if (initialMessage) {
|
|
34254
|
-
examples.push(
|
|
34283
|
+
examples.push(`**Agent:**\n${initialMessage}`);
|
|
34255
34284
|
}
|
|
34256
34285
|
if (samples && samples.length > 0) {
|
|
34257
34286
|
for (const sample of samples) {
|
|
34258
|
-
examples.push(
|
|
34287
|
+
examples.push(`**User:** ${sample.question}\n\n**Agent:**\n${sample.answer}`);
|
|
34259
34288
|
}
|
|
34260
34289
|
}
|
|
34261
34290
|
return examples;
|