@promptbook/node 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.
- package/esm/index.es.js +2402 -1154
- package/esm/index.es.js.map +1 -1
- package/esm/src/book-2.0/agent-source/AgentBasicInformation.d.ts +1 -1
- package/esm/src/book-components/Chat/Chat/ChatToolCallModal.test.d.ts +2 -0
- package/esm/src/book-components/Chat/Chat/renderToolCallDetails.d.ts +4 -0
- package/esm/src/book-components/Chat/utils/getToolCallChipletInfo.timeout.test.d.ts +1 -0
- package/esm/src/book-components/Chat/utils/timeoutToolCallPresentation.d.ts +123 -0
- package/esm/src/book-components/Chat/utils/timeoutToolCallPresentation.test.d.ts +1 -0
- package/esm/src/commitments/USE_CALENDAR/USE_CALENDAR.d.ts +42 -0
- package/esm/src/commitments/USE_CALENDAR/USE_CALENDAR.test.d.ts +1 -0
- package/esm/src/commitments/USE_CALENDAR/UseCalendarToolNames.d.ts +13 -0
- package/esm/src/commitments/USE_CALENDAR/UseCalendarWallet.d.ts +9 -0
- package/esm/src/commitments/USE_CALENDAR/calendarReference.d.ts +68 -0
- package/esm/src/commitments/USE_CALENDAR/callGoogleCalendarApi.d.ts +19 -0
- package/esm/src/commitments/USE_CALENDAR/createUseCalendarToolFunctions.d.ts +8 -0
- package/esm/src/commitments/USE_CALENDAR/createUseCalendarTools.d.ts +7 -0
- package/esm/src/commitments/USE_CALENDAR/getUseCalendarToolTitles.d.ts +7 -0
- package/esm/src/commitments/USE_CALENDAR/normalizeConfiguredCalendars.d.ts +19 -0
- package/esm/src/commitments/USE_CALENDAR/resolveUseCalendarToolRuntimeOrWalletCredentialResult.d.ts +34 -0
- package/esm/src/commitments/_common/toolRuntimeContext.d.ts +9 -0
- package/esm/src/commitments/index.d.ts +2 -1
- package/esm/src/executables/apps/locateVscode.d.ts +7 -0
- package/esm/src/executables/apps/locateVscode.test.d.ts +1 -0
- package/esm/src/executables/browsers/locateBrowser.d.ts +10 -0
- package/esm/src/executables/browsers/locateBrowser.test.d.ts +1 -0
- package/esm/src/executables/browsers/locateChrome.d.ts +7 -0
- package/esm/src/executables/browsers/locateChrome.test.d.ts +1 -0
- package/esm/src/executables/browsers/locateDefaultSystemBrowser.d.ts +10 -0
- package/esm/src/executables/browsers/locateDefaultSystemBrowser.test.d.ts +1 -0
- package/esm/src/executables/browsers/locateEdge.d.ts +7 -0
- package/esm/src/executables/browsers/locateEdge.test.d.ts +1 -0
- package/esm/src/executables/browsers/locateFirefox.d.ts +7 -0
- package/esm/src/executables/browsers/locateFirefox.test.d.ts +1 -0
- package/esm/src/executables/browsers/locateInternetExplorer.d.ts +7 -0
- package/esm/src/executables/browsers/locateInternetExplorer.test.d.ts +1 -0
- package/esm/src/executables/browsers/locateSafari.d.ts +7 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/umd/index.umd.js +2407 -1158
- package/umd/index.umd.js.map +1 -1
- package/umd/src/book-2.0/agent-source/AgentBasicInformation.d.ts +1 -1
- package/umd/src/book-components/Chat/Chat/ChatToolCallModal.test.d.ts +2 -0
- package/umd/src/book-components/Chat/Chat/renderToolCallDetails.d.ts +4 -0
- package/umd/src/book-components/Chat/utils/getToolCallChipletInfo.timeout.test.d.ts +1 -0
- package/umd/src/book-components/Chat/utils/timeoutToolCallPresentation.d.ts +123 -0
- package/umd/src/book-components/Chat/utils/timeoutToolCallPresentation.test.d.ts +1 -0
- package/umd/src/commitments/USE_CALENDAR/USE_CALENDAR.d.ts +42 -0
- package/umd/src/commitments/USE_CALENDAR/USE_CALENDAR.test.d.ts +1 -0
- package/umd/src/commitments/USE_CALENDAR/UseCalendarToolNames.d.ts +13 -0
- package/umd/src/commitments/USE_CALENDAR/UseCalendarWallet.d.ts +9 -0
- package/umd/src/commitments/USE_CALENDAR/calendarReference.d.ts +68 -0
- package/umd/src/commitments/USE_CALENDAR/callGoogleCalendarApi.d.ts +19 -0
- package/umd/src/commitments/USE_CALENDAR/createUseCalendarToolFunctions.d.ts +8 -0
- package/umd/src/commitments/USE_CALENDAR/createUseCalendarTools.d.ts +7 -0
- package/umd/src/commitments/USE_CALENDAR/getUseCalendarToolTitles.d.ts +7 -0
- package/umd/src/commitments/USE_CALENDAR/normalizeConfiguredCalendars.d.ts +19 -0
- package/umd/src/commitments/USE_CALENDAR/resolveUseCalendarToolRuntimeOrWalletCredentialResult.d.ts +34 -0
- package/umd/src/commitments/_common/toolRuntimeContext.d.ts +9 -0
- package/umd/src/commitments/index.d.ts +2 -1
- package/umd/src/executables/apps/locateVscode.d.ts +7 -0
- package/umd/src/executables/apps/locateVscode.test.d.ts +1 -0
- package/umd/src/executables/browsers/locateBrowser.d.ts +10 -0
- package/umd/src/executables/browsers/locateBrowser.test.d.ts +1 -0
- package/umd/src/executables/browsers/locateChrome.d.ts +7 -0
- package/umd/src/executables/browsers/locateChrome.test.d.ts +1 -0
- package/umd/src/executables/browsers/locateDefaultSystemBrowser.d.ts +10 -0
- package/umd/src/executables/browsers/locateDefaultSystemBrowser.test.d.ts +1 -0
- package/umd/src/executables/browsers/locateEdge.d.ts +7 -0
- package/umd/src/executables/browsers/locateEdge.test.d.ts +1 -0
- package/umd/src/executables/browsers/locateFirefox.d.ts +7 -0
- package/umd/src/executables/browsers/locateFirefox.test.d.ts +1 -0
- package/umd/src/executables/browsers/locateInternetExplorer.d.ts +7 -0
- package/umd/src/executables/browsers/locateInternetExplorer.test.d.ts +1 -0
- package/umd/src/executables/browsers/locateSafari.d.ts +7 -0
- package/umd/src/version.d.ts +1 -1
package/esm/index.es.js
CHANGED
|
@@ -11,7 +11,6 @@ import { JSDOM } from 'jsdom';
|
|
|
11
11
|
import { Converter } from 'showdown';
|
|
12
12
|
import { tmpdir } from 'os';
|
|
13
13
|
import { ConfigChecker } from 'configchecker';
|
|
14
|
-
import { locateChrome } from 'locate-app';
|
|
15
14
|
import { chromium } from 'playwright';
|
|
16
15
|
import { spawn } from 'child_process';
|
|
17
16
|
import { forTime } from 'waitasecond';
|
|
@@ -39,7 +38,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
39
38
|
* @generated
|
|
40
39
|
* @see https://github.com/webgptorg/promptbook
|
|
41
40
|
*/
|
|
42
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-
|
|
41
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-12';
|
|
43
42
|
/**
|
|
44
43
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
45
44
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -14804,117 +14803,449 @@ const runBrowserResultFormatting = {
|
|
|
14804
14803
|
};
|
|
14805
14804
|
|
|
14806
14805
|
/**
|
|
14807
|
-
*
|
|
14806
|
+
* Normalize options for `execCommand` and `execCommands`
|
|
14808
14807
|
*
|
|
14809
|
-
*
|
|
14810
|
-
*/
|
|
14811
|
-
function createAbortError$1() {
|
|
14812
|
-
const error = new Error('Operation was aborted.');
|
|
14813
|
-
error.name = 'AbortError';
|
|
14814
|
-
return error;
|
|
14815
|
-
}
|
|
14816
|
-
/**
|
|
14817
|
-
* Throws when the supplied signal is already aborted.
|
|
14808
|
+
* Note: `$` is used to indicate that this function behaves differently according to `process.platform`
|
|
14818
14809
|
*
|
|
14819
|
-
* @private utility
|
|
14810
|
+
* @private internal utility of `execCommand` and `execCommands`
|
|
14820
14811
|
*/
|
|
14821
|
-
function
|
|
14822
|
-
|
|
14823
|
-
|
|
14812
|
+
function $execCommandNormalizeOptions(options) {
|
|
14813
|
+
var _a, _b, _c, _d;
|
|
14814
|
+
let command;
|
|
14815
|
+
let cwd;
|
|
14816
|
+
let crashOnError;
|
|
14817
|
+
let args = [];
|
|
14818
|
+
let timeout;
|
|
14819
|
+
let isVerbose;
|
|
14820
|
+
let env;
|
|
14821
|
+
if (typeof options === 'string') {
|
|
14822
|
+
// TODO: [1] DRY default values
|
|
14823
|
+
command = options;
|
|
14824
|
+
cwd = process.cwd();
|
|
14825
|
+
crashOnError = true;
|
|
14826
|
+
timeout = Infinity; // <- TODO: [⏳]
|
|
14827
|
+
isVerbose = DEFAULT_IS_VERBOSE;
|
|
14828
|
+
env = undefined;
|
|
14824
14829
|
}
|
|
14825
|
-
|
|
14826
|
-
|
|
14827
|
-
|
|
14828
|
-
|
|
14829
|
-
|
|
14830
|
-
|
|
14831
|
-
|
|
14832
|
-
|
|
14833
|
-
|
|
14834
|
-
|
|
14830
|
+
else {
|
|
14831
|
+
/*
|
|
14832
|
+
TODO:
|
|
14833
|
+
if ((options as any).commands !== undefined) {
|
|
14834
|
+
commands = (options as any).commands;
|
|
14835
|
+
} else {
|
|
14836
|
+
commands = [(options as any).command];
|
|
14837
|
+
}
|
|
14838
|
+
*/
|
|
14839
|
+
// TODO: [1] DRY default values
|
|
14840
|
+
command = options.command;
|
|
14841
|
+
cwd = (_a = options.cwd) !== null && _a !== void 0 ? _a : process.cwd();
|
|
14842
|
+
crashOnError = (_b = options.crashOnError) !== null && _b !== void 0 ? _b : true;
|
|
14843
|
+
timeout = (_c = options.timeout) !== null && _c !== void 0 ? _c : Infinity;
|
|
14844
|
+
isVerbose = (_d = options.isVerbose) !== null && _d !== void 0 ? _d : DEFAULT_IS_VERBOSE;
|
|
14845
|
+
env = options.env;
|
|
14835
14846
|
}
|
|
14836
|
-
|
|
14837
|
-
|
|
14838
|
-
|
|
14839
|
-
|
|
14840
|
-
|
|
14841
|
-
|
|
14842
|
-
|
|
14843
|
-
|
|
14844
|
-
|
|
14845
|
-
|
|
14846
|
-
|
|
14847
|
-
|
|
14847
|
+
// TODO: /(-[a-zA-Z0-9-]+\s+[^\s]*)|[^\s]*/g
|
|
14848
|
+
const _ = Array.from(command.matchAll(/(".*")|([^\s]*)/g))
|
|
14849
|
+
.map(([match]) => match)
|
|
14850
|
+
.filter((arg) => arg !== '');
|
|
14851
|
+
if (_.length > 1) {
|
|
14852
|
+
[command, ...args] = _;
|
|
14853
|
+
}
|
|
14854
|
+
if (options.args) {
|
|
14855
|
+
args = [...args, ...options.args];
|
|
14856
|
+
}
|
|
14857
|
+
let humanReadableCommand = !['npx', 'npm'].includes(command) ? command : args[0];
|
|
14858
|
+
if (['ts-node'].includes(humanReadableCommand)) {
|
|
14859
|
+
humanReadableCommand += ` ${args[1]}`;
|
|
14860
|
+
}
|
|
14861
|
+
if (/^win/.test(process.platform) && ['npm', 'npx'].includes(command)) {
|
|
14862
|
+
command = `${command}.cmd`;
|
|
14863
|
+
}
|
|
14864
|
+
return { command, humanReadableCommand, args, cwd, crashOnError, timeout, isVerbose, env };
|
|
14848
14865
|
}
|
|
14866
|
+
// TODO: This should show type error> execCommandNormalizeOptions({ command: '', commands: [''] });
|
|
14867
|
+
|
|
14849
14868
|
/**
|
|
14850
|
-
*
|
|
14869
|
+
* Run one command in a shell
|
|
14851
14870
|
*
|
|
14852
|
-
* @private utility for Agents Server runtime retries
|
|
14853
|
-
*/
|
|
14854
|
-
function resolveBackoffDelayMs(options) {
|
|
14855
|
-
const exponentialDelay = options.initialDelayMs * Math.pow(options.backoffFactor, Math.max(0, options.attempt - 1));
|
|
14856
|
-
const boundedDelay = Math.min(exponentialDelay, options.maxDelayMs);
|
|
14857
|
-
const jitterDelay = boundedDelay * options.jitterRatio * Math.max(0, options.random());
|
|
14858
|
-
return Math.max(0, Math.round(boundedDelay + jitterDelay));
|
|
14859
|
-
}
|
|
14860
|
-
/**
|
|
14861
|
-
* Retries one async operation with exponential backoff and jitter.
|
|
14862
14871
|
*
|
|
14863
|
-
*
|
|
14872
|
+
* Note: There are 2 similar functions in the codebase:
|
|
14873
|
+
* - `$execCommand` which runs a single command
|
|
14874
|
+
* - `$execCommands` which runs multiple commands
|
|
14875
|
+
* Note: `$` is used to indicate that this function is not a pure function - it runs a command in a shell
|
|
14876
|
+
*
|
|
14877
|
+
* @public exported from `@promptbook/node`
|
|
14864
14878
|
*/
|
|
14865
|
-
|
|
14866
|
-
|
|
14867
|
-
|
|
14868
|
-
|
|
14869
|
-
|
|
14870
|
-
|
|
14871
|
-
|
|
14872
|
-
|
|
14873
|
-
|
|
14874
|
-
|
|
14875
|
-
|
|
14876
|
-
|
|
14877
|
-
|
|
14878
|
-
|
|
14879
|
-
|
|
14879
|
+
function $execCommand(options) {
|
|
14880
|
+
if (!$isRunningInNode()) {
|
|
14881
|
+
throw new EnvironmentMismatchError('Function `$execCommand` can run only in Node environment.js');
|
|
14882
|
+
}
|
|
14883
|
+
return new Promise((resolve, reject) => {
|
|
14884
|
+
// eslint-disable-next-line prefer-const
|
|
14885
|
+
const { command, humanReadableCommand, args, cwd, crashOnError, timeout, isVerbose = DEFAULT_IS_VERBOSE, env, } = $execCommandNormalizeOptions(options);
|
|
14886
|
+
if (timeout !== Infinity) {
|
|
14887
|
+
// TODO: In waitasecond forTime(Infinity) should be equivalent to forEver()
|
|
14888
|
+
forTime(timeout).then(() => {
|
|
14889
|
+
if (crashOnError) {
|
|
14890
|
+
reject(new Error(`Command "${humanReadableCommand}" exceeded time limit of ${timeout}ms`));
|
|
14891
|
+
}
|
|
14892
|
+
else {
|
|
14893
|
+
console.warn(`Command "${humanReadableCommand}" exceeded time limit of ${timeout}ms but continues running`);
|
|
14894
|
+
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
14895
|
+
resolve('Command exceeded time limit');
|
|
14896
|
+
}
|
|
14897
|
+
});
|
|
14880
14898
|
}
|
|
14881
|
-
|
|
14882
|
-
|
|
14883
|
-
|
|
14884
|
-
|
|
14885
|
-
|
|
14899
|
+
if (isVerbose) {
|
|
14900
|
+
console.info(colors.yellow(cwd) + ' ' + colors.green(command) + ' ' + colors.blue(args.join(' ')));
|
|
14901
|
+
}
|
|
14902
|
+
try {
|
|
14903
|
+
const commandProcess = spawn(command, args, {
|
|
14904
|
+
cwd,
|
|
14905
|
+
shell: true,
|
|
14906
|
+
env: env ? { ...process.env, ...env } : process.env,
|
|
14907
|
+
});
|
|
14908
|
+
if (isVerbose) {
|
|
14909
|
+
commandProcess.on('message', (message) => {
|
|
14910
|
+
console.info({ message });
|
|
14911
|
+
});
|
|
14886
14912
|
}
|
|
14887
|
-
const
|
|
14888
|
-
|
|
14889
|
-
|
|
14890
|
-
|
|
14891
|
-
|
|
14892
|
-
|
|
14893
|
-
random,
|
|
14913
|
+
const output = [];
|
|
14914
|
+
commandProcess.stdout.on('data', (stdout) => {
|
|
14915
|
+
output.push(stdout.toString());
|
|
14916
|
+
if (isVerbose) {
|
|
14917
|
+
console.info(stdout.toString());
|
|
14918
|
+
}
|
|
14894
14919
|
});
|
|
14895
|
-
(
|
|
14896
|
-
|
|
14897
|
-
|
|
14898
|
-
|
|
14899
|
-
|
|
14920
|
+
commandProcess.stderr.on('data', (stderr) => {
|
|
14921
|
+
output.push(stderr.toString());
|
|
14922
|
+
if (isVerbose && stderr.toString().trim()) {
|
|
14923
|
+
console.warn(stderr.toString());
|
|
14924
|
+
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
14925
|
+
}
|
|
14926
|
+
});
|
|
14927
|
+
const finishWithCode = (code) => {
|
|
14928
|
+
if (code !== 0) {
|
|
14929
|
+
if (crashOnError) {
|
|
14930
|
+
reject(new Error(output.join('\n').trim() ||
|
|
14931
|
+
`Command "${humanReadableCommand}" exited with code ${code}`));
|
|
14932
|
+
}
|
|
14933
|
+
else {
|
|
14934
|
+
if (isVerbose) {
|
|
14935
|
+
console.warn(`Command "${humanReadableCommand}" exited with code ${code}`);
|
|
14936
|
+
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
14937
|
+
}
|
|
14938
|
+
resolve(spaceTrim$1(output.join('\n')));
|
|
14939
|
+
}
|
|
14940
|
+
}
|
|
14941
|
+
else {
|
|
14942
|
+
resolve(spaceTrim$1(output.join('\n')));
|
|
14943
|
+
}
|
|
14944
|
+
};
|
|
14945
|
+
commandProcess.on('close', finishWithCode);
|
|
14946
|
+
commandProcess.on('exit', finishWithCode);
|
|
14947
|
+
commandProcess.on('disconnect', () => {
|
|
14948
|
+
// Note: Unexpected disconnection should always result in rejection
|
|
14949
|
+
reject(new Error(`Command "${humanReadableCommand}" disconnected`));
|
|
14950
|
+
});
|
|
14951
|
+
commandProcess.on('error', (error) => {
|
|
14952
|
+
if (crashOnError) {
|
|
14953
|
+
reject(new Error(`Command "${humanReadableCommand}" failed: \n${error.message}`));
|
|
14954
|
+
}
|
|
14955
|
+
else {
|
|
14956
|
+
if (isVerbose) {
|
|
14957
|
+
console.warn(error);
|
|
14958
|
+
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
14959
|
+
}
|
|
14960
|
+
resolve(spaceTrim$1(output.join('\n')));
|
|
14961
|
+
}
|
|
14900
14962
|
});
|
|
14901
|
-
await sleep(delayMs, options.signal);
|
|
14902
14963
|
}
|
|
14903
|
-
|
|
14904
|
-
|
|
14964
|
+
catch (error) {
|
|
14965
|
+
// Note: Unexpected error in sync code should always result in rejection
|
|
14966
|
+
reject(error);
|
|
14967
|
+
}
|
|
14968
|
+
});
|
|
14905
14969
|
}
|
|
14906
|
-
|
|
14907
|
-
const DEFAULT_BROWSER_USER_DATA_DIR = join(tmpdir(), 'promptbook', 'browser', 'user-data');
|
|
14908
14970
|
/**
|
|
14909
|
-
*
|
|
14971
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
14910
14972
|
*/
|
|
14911
|
-
|
|
14973
|
+
|
|
14912
14974
|
/**
|
|
14913
|
-
*
|
|
14975
|
+
* Attempts to locate the specified application on a Linux system using the 'which' command.
|
|
14976
|
+
* Returns the path to the executable if found, or null otherwise.
|
|
14977
|
+
*
|
|
14978
|
+
* @private within the repository
|
|
14914
14979
|
*/
|
|
14915
|
-
|
|
14980
|
+
async function locateAppOnLinux({ linuxWhich, }) {
|
|
14981
|
+
try {
|
|
14982
|
+
const result = await $execCommand({ crashOnError: true, command: `which ${linuxWhich}` });
|
|
14983
|
+
return result.trim();
|
|
14984
|
+
}
|
|
14985
|
+
catch (error) {
|
|
14986
|
+
assertsError(error);
|
|
14987
|
+
return null;
|
|
14988
|
+
}
|
|
14989
|
+
}
|
|
14916
14990
|
/**
|
|
14917
|
-
*
|
|
14991
|
+
* TODO: [🧠][♿] Maybe export through `@promptbook/node`
|
|
14992
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
14993
|
+
*/
|
|
14994
|
+
|
|
14995
|
+
/**
|
|
14996
|
+
* Checks if the file is executable
|
|
14997
|
+
*
|
|
14998
|
+
* @private within the repository
|
|
14999
|
+
*/
|
|
15000
|
+
async function isExecutable(path, fs) {
|
|
15001
|
+
try {
|
|
15002
|
+
await fs.access(path, fs.constants.X_OK);
|
|
15003
|
+
return true;
|
|
15004
|
+
}
|
|
15005
|
+
catch (error) {
|
|
15006
|
+
return false;
|
|
15007
|
+
}
|
|
15008
|
+
}
|
|
15009
|
+
/**
|
|
15010
|
+
* Note: Not [~🟢~] because it is not directly dependent on `fs
|
|
15011
|
+
* TODO: [🖇] What about symlinks?
|
|
15012
|
+
*/
|
|
15013
|
+
|
|
15014
|
+
// Note: Module `userhome` has no types available, so it is imported using `require`
|
|
15015
|
+
// @see https://stackoverflow.com/questions/37000981/how-to-import-node-module-in-typescript-without-type-definitions
|
|
15016
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
15017
|
+
const userhome = require('userhome');
|
|
15018
|
+
/**
|
|
15019
|
+
* Attempts to locate the specified application on a macOS system by checking standard application paths and using mdfind.
|
|
15020
|
+
* Returns the path to the executable if found, or null otherwise.
|
|
15021
|
+
*
|
|
15022
|
+
* @private within the repository
|
|
15023
|
+
*/
|
|
15024
|
+
async function locateAppOnMacOs({ macOsName, }) {
|
|
15025
|
+
try {
|
|
15026
|
+
const toExec = `/Contents/MacOS/${macOsName}`;
|
|
15027
|
+
const regPath = `/Applications/${macOsName}.app` + toExec;
|
|
15028
|
+
const altPath = userhome(regPath.slice(1));
|
|
15029
|
+
if (await isExecutable(regPath, $provideFilesystemForNode())) {
|
|
15030
|
+
return regPath;
|
|
15031
|
+
}
|
|
15032
|
+
else if (await isExecutable(altPath, $provideFilesystemForNode())) {
|
|
15033
|
+
return altPath;
|
|
15034
|
+
}
|
|
15035
|
+
const result = await $execCommand({
|
|
15036
|
+
crashOnError: true,
|
|
15037
|
+
command: `mdfind 'kMDItemDisplayName == "${macOsName}" && kMDItemKind == Application'`,
|
|
15038
|
+
});
|
|
15039
|
+
return result.trim() + toExec;
|
|
15040
|
+
}
|
|
15041
|
+
catch (error) {
|
|
15042
|
+
assertsError(error);
|
|
15043
|
+
return null;
|
|
15044
|
+
}
|
|
15045
|
+
}
|
|
15046
|
+
/**
|
|
15047
|
+
* TODO: [🧠][♿] Maybe export through `@promptbook/node`
|
|
15048
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
15049
|
+
*/
|
|
15050
|
+
|
|
15051
|
+
/**
|
|
15052
|
+
* Attempts to locate the specified application on a Windows system by searching common installation directories.
|
|
15053
|
+
* Returns the path to the executable if found, or null otherwise.
|
|
15054
|
+
*
|
|
15055
|
+
* @private within the repository
|
|
15056
|
+
*/
|
|
15057
|
+
async function locateAppOnWindows({ appName, windowsSuffix, }) {
|
|
15058
|
+
try {
|
|
15059
|
+
const prefixes = [
|
|
15060
|
+
process.env.LOCALAPPDATA,
|
|
15061
|
+
join(process.env.LOCALAPPDATA || '', 'Programs'),
|
|
15062
|
+
process.env.PROGRAMFILES,
|
|
15063
|
+
process.env['PROGRAMFILES(X86)'],
|
|
15064
|
+
];
|
|
15065
|
+
for (const prefix of prefixes) {
|
|
15066
|
+
const path = prefix + windowsSuffix;
|
|
15067
|
+
if (await isExecutable(path, $provideFilesystemForNode())) {
|
|
15068
|
+
return path;
|
|
15069
|
+
}
|
|
15070
|
+
}
|
|
15071
|
+
throw new Error(`Can not locate app ${appName} on Windows.`);
|
|
15072
|
+
}
|
|
15073
|
+
catch (error) {
|
|
15074
|
+
assertsError(error);
|
|
15075
|
+
return null;
|
|
15076
|
+
}
|
|
15077
|
+
}
|
|
15078
|
+
/**
|
|
15079
|
+
* TODO: [🧠][♿] Maybe export through `@promptbook/node`
|
|
15080
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
15081
|
+
*/
|
|
15082
|
+
|
|
15083
|
+
/**
|
|
15084
|
+
* Locates an application on the system
|
|
15085
|
+
*
|
|
15086
|
+
* @private within the repository
|
|
15087
|
+
*/
|
|
15088
|
+
function locateApp(options) {
|
|
15089
|
+
if (!$isRunningInNode()) {
|
|
15090
|
+
throw new EnvironmentMismatchError('Locating apps works only in Node.js environment');
|
|
15091
|
+
}
|
|
15092
|
+
const { appName, linuxWhich, windowsSuffix, macOsName } = options;
|
|
15093
|
+
if (process.platform === 'win32') {
|
|
15094
|
+
if (windowsSuffix) {
|
|
15095
|
+
return locateAppOnWindows({ appName, windowsSuffix });
|
|
15096
|
+
}
|
|
15097
|
+
else {
|
|
15098
|
+
throw new Error(`${appName} is not available on Windows.`);
|
|
15099
|
+
}
|
|
15100
|
+
}
|
|
15101
|
+
else if (process.platform === 'darwin') {
|
|
15102
|
+
if (macOsName) {
|
|
15103
|
+
return locateAppOnMacOs({ macOsName });
|
|
15104
|
+
}
|
|
15105
|
+
else {
|
|
15106
|
+
throw new Error(`${appName} is not available on macOS.`);
|
|
15107
|
+
}
|
|
15108
|
+
}
|
|
15109
|
+
else {
|
|
15110
|
+
if (linuxWhich) {
|
|
15111
|
+
return locateAppOnLinux({ linuxWhich });
|
|
15112
|
+
}
|
|
15113
|
+
else {
|
|
15114
|
+
throw new Error(`${appName} is not available on Linux.`);
|
|
15115
|
+
}
|
|
15116
|
+
}
|
|
15117
|
+
}
|
|
15118
|
+
/**
|
|
15119
|
+
* TODO: [🧠][♿] Maybe export through `@promptbook/node`
|
|
15120
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
15121
|
+
*/
|
|
15122
|
+
|
|
15123
|
+
/**
|
|
15124
|
+
* @@@
|
|
15125
|
+
*
|
|
15126
|
+
* @private within the repository
|
|
15127
|
+
*/
|
|
15128
|
+
function locateChrome() {
|
|
15129
|
+
return locateApp({
|
|
15130
|
+
appName: 'Chrome',
|
|
15131
|
+
linuxWhich: 'google-chrome',
|
|
15132
|
+
windowsSuffix: '\\Google\\Chrome\\Application\\chrome.exe',
|
|
15133
|
+
macOsName: 'Google Chrome',
|
|
15134
|
+
});
|
|
15135
|
+
}
|
|
15136
|
+
|
|
15137
|
+
/**
|
|
15138
|
+
* Creates one standard abort error for cancelled retry loops.
|
|
15139
|
+
*
|
|
15140
|
+
* @private utility for Agents Server runtime retries
|
|
15141
|
+
*/
|
|
15142
|
+
function createAbortError$1() {
|
|
15143
|
+
const error = new Error('Operation was aborted.');
|
|
15144
|
+
error.name = 'AbortError';
|
|
15145
|
+
return error;
|
|
15146
|
+
}
|
|
15147
|
+
/**
|
|
15148
|
+
* Throws when the supplied signal is already aborted.
|
|
15149
|
+
*
|
|
15150
|
+
* @private utility for Agents Server runtime retries
|
|
15151
|
+
*/
|
|
15152
|
+
function assertNotAborted(signal) {
|
|
15153
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
15154
|
+
throw createAbortError$1();
|
|
15155
|
+
}
|
|
15156
|
+
}
|
|
15157
|
+
/**
|
|
15158
|
+
* Waits for a duration while respecting cancellation.
|
|
15159
|
+
*
|
|
15160
|
+
* @private utility for Agents Server runtime retries
|
|
15161
|
+
*/
|
|
15162
|
+
async function sleepWithAbort(delayMs, signal) {
|
|
15163
|
+
if (delayMs <= 0) {
|
|
15164
|
+
assertNotAborted(signal);
|
|
15165
|
+
return;
|
|
15166
|
+
}
|
|
15167
|
+
await new Promise((resolve, reject) => {
|
|
15168
|
+
const timeout = setTimeout(() => {
|
|
15169
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
15170
|
+
resolve();
|
|
15171
|
+
}, delayMs);
|
|
15172
|
+
const onAbort = () => {
|
|
15173
|
+
clearTimeout(timeout);
|
|
15174
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
15175
|
+
reject(createAbortError$1());
|
|
15176
|
+
};
|
|
15177
|
+
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, { once: true });
|
|
15178
|
+
});
|
|
15179
|
+
}
|
|
15180
|
+
/**
|
|
15181
|
+
* Resolves the retry wait duration for one failed attempt.
|
|
15182
|
+
*
|
|
15183
|
+
* @private utility for Agents Server runtime retries
|
|
15184
|
+
*/
|
|
15185
|
+
function resolveBackoffDelayMs(options) {
|
|
15186
|
+
const exponentialDelay = options.initialDelayMs * Math.pow(options.backoffFactor, Math.max(0, options.attempt - 1));
|
|
15187
|
+
const boundedDelay = Math.min(exponentialDelay, options.maxDelayMs);
|
|
15188
|
+
const jitterDelay = boundedDelay * options.jitterRatio * Math.max(0, options.random());
|
|
15189
|
+
return Math.max(0, Math.round(boundedDelay + jitterDelay));
|
|
15190
|
+
}
|
|
15191
|
+
/**
|
|
15192
|
+
* Retries one async operation with exponential backoff and jitter.
|
|
15193
|
+
*
|
|
15194
|
+
* @private utility for Agents Server runtime retries
|
|
15195
|
+
*/
|
|
15196
|
+
async function retryWithBackoff(operation, options) {
|
|
15197
|
+
var _a, _b, _c;
|
|
15198
|
+
const startedAt = Date.now();
|
|
15199
|
+
const totalAttempts = Math.max(1, options.retries + 1);
|
|
15200
|
+
const random = (_a = options.random) !== null && _a !== void 0 ? _a : Math.random;
|
|
15201
|
+
const sleep = (_b = options.sleep) !== null && _b !== void 0 ? _b : sleepWithAbort;
|
|
15202
|
+
for (let attempt = 1; attempt <= totalAttempts; attempt++) {
|
|
15203
|
+
assertNotAborted(options.signal);
|
|
15204
|
+
try {
|
|
15205
|
+
const value = await operation(attempt);
|
|
15206
|
+
return {
|
|
15207
|
+
value,
|
|
15208
|
+
attempts: attempt,
|
|
15209
|
+
durationMs: Date.now() - startedAt,
|
|
15210
|
+
};
|
|
15211
|
+
}
|
|
15212
|
+
catch (error) {
|
|
15213
|
+
const isLastAttempt = attempt >= totalAttempts;
|
|
15214
|
+
const isRetryable = options.shouldRetry ? options.shouldRetry(error, attempt) : true;
|
|
15215
|
+
if (isLastAttempt || !isRetryable) {
|
|
15216
|
+
throw error;
|
|
15217
|
+
}
|
|
15218
|
+
const delayMs = resolveBackoffDelayMs({
|
|
15219
|
+
attempt,
|
|
15220
|
+
initialDelayMs: options.initialDelayMs,
|
|
15221
|
+
maxDelayMs: options.maxDelayMs,
|
|
15222
|
+
backoffFactor: options.backoffFactor,
|
|
15223
|
+
jitterRatio: options.jitterRatio,
|
|
15224
|
+
random,
|
|
15225
|
+
});
|
|
15226
|
+
(_c = options.onRetry) === null || _c === void 0 ? void 0 : _c.call(options, {
|
|
15227
|
+
attempt,
|
|
15228
|
+
retries: options.retries,
|
|
15229
|
+
delayMs,
|
|
15230
|
+
error,
|
|
15231
|
+
});
|
|
15232
|
+
await sleep(delayMs, options.signal);
|
|
15233
|
+
}
|
|
15234
|
+
}
|
|
15235
|
+
throw new Error('Retry loop exited unexpectedly.');
|
|
15236
|
+
}
|
|
15237
|
+
|
|
15238
|
+
const DEFAULT_BROWSER_USER_DATA_DIR = join(tmpdir(), 'promptbook', 'browser', 'user-data');
|
|
15239
|
+
/**
|
|
15240
|
+
* Default remote browser connect timeout in milliseconds.
|
|
15241
|
+
*/
|
|
15242
|
+
const DEFAULT_REMOTE_CONNECT_TIMEOUT_MS = 10000;
|
|
15243
|
+
/**
|
|
15244
|
+
* Default retry count for remote browser connection establishment.
|
|
15245
|
+
*/
|
|
15246
|
+
const DEFAULT_REMOTE_CONNECT_RETRIES = 2;
|
|
15247
|
+
/**
|
|
15248
|
+
* Default initial retry delay for remote browser connection.
|
|
14918
15249
|
*/
|
|
14919
15250
|
const DEFAULT_REMOTE_CONNECT_BACKOFF_INITIAL_MS = 250;
|
|
14920
15251
|
/**
|
|
@@ -21357,27 +21688,154 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
21357
21688
|
*/
|
|
21358
21689
|
|
|
21359
21690
|
/**
|
|
21360
|
-
*
|
|
21691
|
+
* Base Google Calendar API URL.
|
|
21361
21692
|
*
|
|
21362
|
-
* @private
|
|
21693
|
+
* @private constant of callGoogleCalendarApi
|
|
21363
21694
|
*/
|
|
21364
|
-
const
|
|
21695
|
+
const GOOGLE_CALENDAR_API_BASE_URL = 'https://www.googleapis.com/calendar/v3';
|
|
21365
21696
|
/**
|
|
21366
|
-
*
|
|
21697
|
+
* Runs one Google Calendar API request and parses JSON response payload.
|
|
21367
21698
|
*
|
|
21368
|
-
*
|
|
21369
|
-
|
|
21370
|
-
|
|
21371
|
-
|
|
21699
|
+
* @private function of UseCalendarCommitmentDefinition
|
|
21700
|
+
*/
|
|
21701
|
+
async function callGoogleCalendarApi(accessToken, options) {
|
|
21702
|
+
const url = new URL(options.path, GOOGLE_CALENDAR_API_BASE_URL);
|
|
21703
|
+
if (options.query) {
|
|
21704
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
21705
|
+
if (value && value.trim()) {
|
|
21706
|
+
url.searchParams.set(key, value);
|
|
21707
|
+
}
|
|
21708
|
+
}
|
|
21709
|
+
}
|
|
21710
|
+
const response = await fetch(url.toString(), {
|
|
21711
|
+
method: options.method,
|
|
21712
|
+
headers: {
|
|
21713
|
+
Authorization: `Bearer ${accessToken}`,
|
|
21714
|
+
Accept: 'application/json',
|
|
21715
|
+
'Content-Type': 'application/json',
|
|
21716
|
+
},
|
|
21717
|
+
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
21718
|
+
});
|
|
21719
|
+
const textPayload = await response.text();
|
|
21720
|
+
const parsedPayload = tryParseJson$2(textPayload);
|
|
21721
|
+
if (options.allowNotFound && response.status === 404) {
|
|
21722
|
+
return null;
|
|
21723
|
+
}
|
|
21724
|
+
if (!response.ok) {
|
|
21725
|
+
throw new Error(spaceTrim$1(`
|
|
21726
|
+
Google Calendar API request failed (${response.status} ${response.statusText}):
|
|
21727
|
+
${extractGoogleCalendarApiErrorMessage(parsedPayload, textPayload)}
|
|
21728
|
+
`));
|
|
21729
|
+
}
|
|
21730
|
+
return parsedPayload;
|
|
21731
|
+
}
|
|
21732
|
+
/**
|
|
21733
|
+
* Parses raw text into JSON when possible.
|
|
21372
21734
|
*
|
|
21373
|
-
* @private
|
|
21735
|
+
* @private function of callGoogleCalendarApi
|
|
21374
21736
|
*/
|
|
21375
|
-
function
|
|
21737
|
+
function tryParseJson$2(rawText) {
|
|
21738
|
+
if (!rawText.trim()) {
|
|
21739
|
+
return {};
|
|
21740
|
+
}
|
|
21741
|
+
try {
|
|
21742
|
+
return JSON.parse(rawText);
|
|
21743
|
+
}
|
|
21744
|
+
catch (_a) {
|
|
21745
|
+
return rawText;
|
|
21746
|
+
}
|
|
21747
|
+
}
|
|
21748
|
+
/**
|
|
21749
|
+
* Extracts a user-friendly Google Calendar API error message.
|
|
21750
|
+
*
|
|
21751
|
+
* @private function of callGoogleCalendarApi
|
|
21752
|
+
*/
|
|
21753
|
+
function extractGoogleCalendarApiErrorMessage(parsedPayload, fallbackText) {
|
|
21754
|
+
if (parsedPayload && typeof parsedPayload === 'object') {
|
|
21755
|
+
const payload = parsedPayload;
|
|
21756
|
+
const errorPayload = payload.error;
|
|
21757
|
+
if (errorPayload && typeof errorPayload === 'object') {
|
|
21758
|
+
const normalizedErrorPayload = errorPayload;
|
|
21759
|
+
const message = typeof normalizedErrorPayload.message === 'string' ? normalizedErrorPayload.message : '';
|
|
21760
|
+
const errors = Array.isArray(normalizedErrorPayload.errors) ? normalizedErrorPayload.errors : [];
|
|
21761
|
+
const flattenedErrors = errors
|
|
21762
|
+
.map((errorEntry) => {
|
|
21763
|
+
if (!errorEntry || typeof errorEntry !== 'object') {
|
|
21764
|
+
return '';
|
|
21765
|
+
}
|
|
21766
|
+
const normalizedErrorEntry = errorEntry;
|
|
21767
|
+
const detailMessage = typeof normalizedErrorEntry.message === 'string' ? normalizedErrorEntry.message : '';
|
|
21768
|
+
const reason = typeof normalizedErrorEntry.reason === 'string' ? normalizedErrorEntry.reason : '';
|
|
21769
|
+
return [detailMessage, reason].filter(Boolean).join(' | ');
|
|
21770
|
+
})
|
|
21771
|
+
.filter(Boolean);
|
|
21772
|
+
if (message || flattenedErrors.length > 0) {
|
|
21773
|
+
return [message, ...flattenedErrors].filter(Boolean).join(' | ');
|
|
21774
|
+
}
|
|
21775
|
+
}
|
|
21776
|
+
}
|
|
21777
|
+
return fallbackText || 'Unknown Google Calendar API error';
|
|
21778
|
+
}
|
|
21779
|
+
|
|
21780
|
+
/**
|
|
21781
|
+
* Hostnames accepted for Google Calendar references.
|
|
21782
|
+
*
|
|
21783
|
+
* @private internal USE CALENDAR constant
|
|
21784
|
+
*/
|
|
21785
|
+
const GOOGLE_CALENDAR_HOSTNAMES = new Set(['calendar.google.com', 'www.calendar.google.com']);
|
|
21786
|
+
/**
|
|
21787
|
+
* Default Google Calendar OAuth scopes when commitment content does not list any.
|
|
21788
|
+
*
|
|
21789
|
+
* @private internal USE CALENDAR constant
|
|
21790
|
+
*/
|
|
21791
|
+
const DEFAULT_GOOGLE_CALENDAR_SCOPES = ['https://www.googleapis.com/auth/calendar'];
|
|
21792
|
+
/**
|
|
21793
|
+
* Parses one Google Calendar URL/reference into canonical details.
|
|
21794
|
+
*
|
|
21795
|
+
* Supported input forms:
|
|
21796
|
+
* - `https://calendar.google.com/...`
|
|
21797
|
+
* - `calendar.google.com/...`
|
|
21798
|
+
*
|
|
21799
|
+
* @private internal utility of USE CALENDAR commitment
|
|
21800
|
+
*/
|
|
21801
|
+
function parseGoogleCalendarReference(rawReference) {
|
|
21802
|
+
const trimmedReference = rawReference.trim();
|
|
21803
|
+
if (!trimmedReference) {
|
|
21804
|
+
return null;
|
|
21805
|
+
}
|
|
21806
|
+
const normalizedReference = trimmedReference.startsWith('calendar.google.com/')
|
|
21807
|
+
? `https://${trimmedReference}`
|
|
21808
|
+
: trimmedReference;
|
|
21809
|
+
let parsedUrl;
|
|
21810
|
+
try {
|
|
21811
|
+
parsedUrl = new URL(normalizedReference);
|
|
21812
|
+
}
|
|
21813
|
+
catch (_a) {
|
|
21814
|
+
return null;
|
|
21815
|
+
}
|
|
21816
|
+
if (!GOOGLE_CALENDAR_HOSTNAMES.has(parsedUrl.hostname.toLowerCase())) {
|
|
21817
|
+
return null;
|
|
21818
|
+
}
|
|
21819
|
+
parsedUrl.protocol = 'https:';
|
|
21820
|
+
parsedUrl.hash = '';
|
|
21821
|
+
return {
|
|
21822
|
+
provider: 'google',
|
|
21823
|
+
url: parsedUrl.toString(),
|
|
21824
|
+
calendarId: parseGoogleCalendarIdFromUrl(parsedUrl) || 'primary',
|
|
21825
|
+
scopes: [...DEFAULT_GOOGLE_CALENDAR_SCOPES],
|
|
21826
|
+
};
|
|
21827
|
+
}
|
|
21828
|
+
/**
|
|
21829
|
+
* Parses `USE CALENDAR` commitment content into calendar reference + optional instructions.
|
|
21830
|
+
*
|
|
21831
|
+
* @private internal utility of USE CALENDAR commitment
|
|
21832
|
+
*/
|
|
21833
|
+
function parseUseCalendarCommitmentContent(content) {
|
|
21376
21834
|
const trimmedContent = spaceTrim$1(content);
|
|
21377
21835
|
if (!trimmedContent) {
|
|
21378
21836
|
return {
|
|
21379
|
-
|
|
21380
|
-
|
|
21837
|
+
calendar: null,
|
|
21838
|
+
calendarUrlRaw: null,
|
|
21381
21839
|
instructions: '',
|
|
21382
21840
|
};
|
|
21383
21841
|
}
|
|
@@ -21387,718 +21845,1505 @@ function parseUseEmailCommitmentContent(content) {
|
|
|
21387
21845
|
.filter(Boolean);
|
|
21388
21846
|
if (lines.length === 0) {
|
|
21389
21847
|
return {
|
|
21390
|
-
|
|
21391
|
-
|
|
21848
|
+
calendar: null,
|
|
21849
|
+
calendarUrlRaw: null,
|
|
21392
21850
|
instructions: '',
|
|
21393
21851
|
};
|
|
21394
21852
|
}
|
|
21853
|
+
let calendarReferenceRaw = null;
|
|
21854
|
+
let calendarReference = null;
|
|
21395
21855
|
const firstLine = lines[0] || '';
|
|
21396
|
-
const
|
|
21397
|
-
|
|
21398
|
-
|
|
21399
|
-
|
|
21400
|
-
|
|
21401
|
-
|
|
21402
|
-
|
|
21403
|
-
|
|
21404
|
-
|
|
21856
|
+
const firstLineTokens = firstLine.split(/\s+/).filter(Boolean);
|
|
21857
|
+
for (const token of firstLineTokens) {
|
|
21858
|
+
const cleanedToken = token.replace(/[),.;:!?]+$/g, '');
|
|
21859
|
+
const parsedReference = parseGoogleCalendarReference(cleanedToken);
|
|
21860
|
+
if (!parsedReference) {
|
|
21861
|
+
continue;
|
|
21862
|
+
}
|
|
21863
|
+
calendarReferenceRaw = cleanedToken;
|
|
21864
|
+
calendarReference = parsedReference;
|
|
21865
|
+
break;
|
|
21405
21866
|
}
|
|
21406
|
-
|
|
21407
|
-
|
|
21867
|
+
if (!calendarReference) {
|
|
21868
|
+
const firstUrl = findFirstUrlToken(trimmedContent);
|
|
21869
|
+
if (firstUrl) {
|
|
21870
|
+
calendarReferenceRaw = firstUrl;
|
|
21871
|
+
calendarReference = parseGoogleCalendarReference(firstUrl);
|
|
21872
|
+
}
|
|
21873
|
+
}
|
|
21874
|
+
const scopes = extractGoogleCalendarScopes(trimmedContent);
|
|
21875
|
+
if (calendarReference) {
|
|
21876
|
+
calendarReference = {
|
|
21877
|
+
...calendarReference,
|
|
21878
|
+
scopes: scopes.length > 0 ? scopes : [...DEFAULT_GOOGLE_CALENDAR_SCOPES],
|
|
21879
|
+
tokenRef: extractTokenRef(trimmedContent),
|
|
21880
|
+
};
|
|
21881
|
+
}
|
|
21882
|
+
const instructionLines = [...lines];
|
|
21883
|
+
if (instructionLines.length > 0 && calendarReferenceRaw) {
|
|
21884
|
+
instructionLines[0] = removeTokenFromLine(instructionLines[0] || '', calendarReferenceRaw);
|
|
21885
|
+
}
|
|
21886
|
+
const filteredInstructionLines = instructionLines.filter((line) => !/^\s*SCOPES?\s+/i.test(line) && !line.trim().startsWith('https://www.googleapis.com/auth/'));
|
|
21408
21887
|
return {
|
|
21409
|
-
|
|
21410
|
-
|
|
21411
|
-
instructions,
|
|
21888
|
+
calendar: calendarReference,
|
|
21889
|
+
calendarUrlRaw: calendarReferenceRaw,
|
|
21890
|
+
instructions: filteredInstructionLines.join('\n').trim(),
|
|
21412
21891
|
};
|
|
21413
21892
|
}
|
|
21414
|
-
|
|
21415
21893
|
/**
|
|
21416
|
-
*
|
|
21894
|
+
* Attempts to resolve one concrete Google Calendar id from URL.
|
|
21417
21895
|
*
|
|
21418
|
-
*
|
|
21419
|
-
|
|
21896
|
+
* @private internal utility of USE CALENDAR commitment
|
|
21897
|
+
*/
|
|
21898
|
+
function parseGoogleCalendarIdFromUrl(url) {
|
|
21899
|
+
const rawCalendarId = url.searchParams.get('cid') || url.searchParams.get('src') || url.searchParams.get('calendarId');
|
|
21900
|
+
if (!rawCalendarId) {
|
|
21901
|
+
return null;
|
|
21902
|
+
}
|
|
21903
|
+
const decodedCalendarId = decodeURIComponent(rawCalendarId).trim();
|
|
21904
|
+
return decodedCalendarId || null;
|
|
21905
|
+
}
|
|
21906
|
+
/**
|
|
21907
|
+
* Finds the first URL-looking token in text.
|
|
21420
21908
|
*
|
|
21421
|
-
* @
|
|
21422
|
-
|
|
21423
|
-
|
|
21909
|
+
* @private utility of USE CALENDAR commitment
|
|
21910
|
+
*/
|
|
21911
|
+
function findFirstUrlToken(text) {
|
|
21912
|
+
const match = text.match(/https?:\/\/[^\s)]+/i);
|
|
21913
|
+
return match ? match[0] || null : null;
|
|
21914
|
+
}
|
|
21915
|
+
/**
|
|
21916
|
+
* Extracts Google Calendar OAuth scopes declared in commitment text.
|
|
21424
21917
|
*
|
|
21425
|
-
* @private
|
|
21918
|
+
* @private utility of USE CALENDAR commitment
|
|
21426
21919
|
*/
|
|
21427
|
-
|
|
21428
|
-
|
|
21429
|
-
|
|
21430
|
-
|
|
21431
|
-
|
|
21432
|
-
|
|
21433
|
-
|
|
21434
|
-
|
|
21435
|
-
|
|
21436
|
-
|
|
21437
|
-
|
|
21438
|
-
|
|
21439
|
-
|
|
21440
|
-
|
|
21441
|
-
|
|
21442
|
-
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
21443
|
-
throw new Error(`Failed to send email: ${errorData.error || response.statusText}`);
|
|
21444
|
-
}
|
|
21445
|
-
const data = await response.json();
|
|
21446
|
-
if (!data.success) {
|
|
21447
|
-
throw new Error(`Email sending failed: ${data.error || 'Unknown error'}`);
|
|
21448
|
-
}
|
|
21449
|
-
return typeof data.result === 'string' ? data.result : JSON.stringify(data.result);
|
|
21450
|
-
}
|
|
21451
|
-
catch (error) {
|
|
21452
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
21453
|
-
throw new Error(`Error sending email via browser: ${errorMessage}`);
|
|
21920
|
+
function extractGoogleCalendarScopes(content) {
|
|
21921
|
+
const scopesFromUrls = content.match(/https:\/\/www\.googleapis\.com\/auth\/[A-Za-z0-9._/-]+/gim) || [];
|
|
21922
|
+
const scopesFromKeywordLines = content
|
|
21923
|
+
.split(/\r?\n/)
|
|
21924
|
+
.map((line) => line.trim())
|
|
21925
|
+
.filter((line) => /^\s*SCOPES?\s+/i.test(line))
|
|
21926
|
+
.flatMap((line) => line
|
|
21927
|
+
.replace(/^\s*SCOPES?\s+/i, '')
|
|
21928
|
+
.split(/[,\s]+/)
|
|
21929
|
+
.map((scope) => scope.trim())
|
|
21930
|
+
.filter(Boolean))
|
|
21931
|
+
.filter((scope) => scope.startsWith('https://www.googleapis.com/auth/'));
|
|
21932
|
+
const uniqueScopes = new Set();
|
|
21933
|
+
for (const scope of [...scopesFromUrls, ...scopesFromKeywordLines]) {
|
|
21934
|
+
uniqueScopes.add(scope);
|
|
21454
21935
|
}
|
|
21936
|
+
return [...uniqueScopes];
|
|
21455
21937
|
}
|
|
21456
|
-
|
|
21457
21938
|
/**
|
|
21458
|
-
*
|
|
21939
|
+
* Extracts optional token reference marker from commitment text.
|
|
21459
21940
|
*
|
|
21460
|
-
* @private
|
|
21941
|
+
* @private utility of USE CALENDAR commitment
|
|
21461
21942
|
*/
|
|
21462
|
-
|
|
21943
|
+
function extractTokenRef(content) {
|
|
21944
|
+
var _a;
|
|
21945
|
+
const tokenRefMatch = content.match(/@[\w.-]+/);
|
|
21946
|
+
const tokenRef = (_a = tokenRefMatch === null || tokenRefMatch === void 0 ? void 0 : tokenRefMatch[0]) === null || _a === void 0 ? void 0 : _a.trim();
|
|
21947
|
+
return tokenRef || undefined;
|
|
21948
|
+
}
|
|
21463
21949
|
/**
|
|
21464
|
-
*
|
|
21950
|
+
* Removes one specific token from one instruction line.
|
|
21465
21951
|
*
|
|
21466
|
-
* @private
|
|
21952
|
+
* @private utility of USE CALENDAR commitment
|
|
21467
21953
|
*/
|
|
21468
|
-
|
|
21954
|
+
function removeTokenFromLine(line, token) {
|
|
21955
|
+
const tokens = line.split(/\s+/).filter(Boolean);
|
|
21956
|
+
const filteredTokens = tokens.filter((lineToken) => lineToken.replace(/[),.;:!?]+$/g, '') !== token);
|
|
21957
|
+
return filteredTokens.join(' ').trim();
|
|
21958
|
+
}
|
|
21469
21959
|
/**
|
|
21470
|
-
*
|
|
21960
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
21961
|
+
*/
|
|
21962
|
+
|
|
21963
|
+
/**
|
|
21964
|
+
* Wallet metadata used by USE CALENDAR when resolving Google Calendar credentials.
|
|
21471
21965
|
*
|
|
21472
|
-
* @private
|
|
21966
|
+
* @private constant of UseCalendarCommitmentDefinition
|
|
21473
21967
|
*/
|
|
21474
|
-
const
|
|
21968
|
+
const UseCalendarWallet = {
|
|
21969
|
+
service: 'google_calendar',
|
|
21970
|
+
key: 'use-calendar-google-token',
|
|
21971
|
+
};
|
|
21972
|
+
|
|
21475
21973
|
/**
|
|
21476
|
-
*
|
|
21974
|
+
* Internal error used to signal missing wallet credentials.
|
|
21477
21975
|
*
|
|
21478
|
-
*
|
|
21976
|
+
* @private class of resolveUseCalendarToolRuntimeOrWalletCredentialResult
|
|
21977
|
+
*/
|
|
21978
|
+
class CalendarWalletCredentialRequiredError extends Error {
|
|
21979
|
+
constructor(options) {
|
|
21980
|
+
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).');
|
|
21981
|
+
this.name = 'CalendarWalletCredentialRequiredError';
|
|
21982
|
+
this.service = UseCalendarWallet.service;
|
|
21983
|
+
this.key = UseCalendarWallet.key;
|
|
21984
|
+
this.calendarReference = options.calendarReference;
|
|
21985
|
+
}
|
|
21986
|
+
}
|
|
21987
|
+
/**
|
|
21988
|
+
* Resolves calendar runtime or returns a wallet-credential-required payload when missing.
|
|
21479
21989
|
*
|
|
21480
|
-
* @private
|
|
21990
|
+
* @private function of UseCalendarCommitmentDefinition
|
|
21481
21991
|
*/
|
|
21482
|
-
|
|
21483
|
-
|
|
21484
|
-
|
|
21992
|
+
function resolveUseCalendarToolRuntimeOrWalletCredentialResult(args) {
|
|
21993
|
+
try {
|
|
21994
|
+
return resolveUseCalendarToolRuntime(args);
|
|
21485
21995
|
}
|
|
21486
|
-
|
|
21487
|
-
|
|
21996
|
+
catch (error) {
|
|
21997
|
+
if (error instanceof CalendarWalletCredentialRequiredError) {
|
|
21998
|
+
return {
|
|
21999
|
+
walletResult: JSON.stringify(createCalendarWalletCredentialRequiredResult(error)),
|
|
22000
|
+
};
|
|
22001
|
+
}
|
|
22002
|
+
throw error;
|
|
21488
22003
|
}
|
|
21489
|
-
|
|
21490
|
-
|
|
21491
|
-
|
|
21492
|
-
|
|
21493
|
-
|
|
22004
|
+
}
|
|
22005
|
+
/**
|
|
22006
|
+
* Resolves runtime calendar + token for a USE CALENDAR tool call.
|
|
22007
|
+
*
|
|
22008
|
+
* @private function of resolveUseCalendarToolRuntimeOrWalletCredentialResult
|
|
22009
|
+
*/
|
|
22010
|
+
function resolveUseCalendarToolRuntime(args) {
|
|
22011
|
+
var _a, _b;
|
|
22012
|
+
const runtimeContext = (readToolRuntimeContextFromToolArgs(args) ||
|
|
22013
|
+
{});
|
|
22014
|
+
const configuredCalendars = normalizeConfiguredCalendars$1((_a = runtimeContext.calendars) === null || _a === void 0 ? void 0 : _a.connections);
|
|
22015
|
+
const calendarArgument = normalizeOptionalText$1(args.calendarUrl);
|
|
22016
|
+
let calendarReference = null;
|
|
22017
|
+
if (calendarArgument) {
|
|
22018
|
+
calendarReference = parseGoogleCalendarReference(calendarArgument);
|
|
22019
|
+
if (!calendarReference) {
|
|
22020
|
+
throw new Error(`Calendar URL "${calendarArgument}" is invalid.`);
|
|
22021
|
+
}
|
|
21494
22022
|
}
|
|
21495
|
-
|
|
21496
|
-
|
|
21497
|
-
*/
|
|
21498
|
-
get icon() {
|
|
21499
|
-
return '📧';
|
|
22023
|
+
else if (configuredCalendars.length === 1) {
|
|
22024
|
+
calendarReference = configuredCalendars[0] || null;
|
|
21500
22025
|
}
|
|
21501
|
-
|
|
21502
|
-
|
|
21503
|
-
*/
|
|
21504
|
-
get documentation() {
|
|
21505
|
-
return spaceTrim$1(`
|
|
21506
|
-
# USE EMAIL
|
|
21507
|
-
|
|
21508
|
-
Enables the agent to send outbound emails through SMTP.
|
|
21509
|
-
|
|
21510
|
-
## Key aspects
|
|
21511
|
-
|
|
21512
|
-
- The agent sends email via the \`send_email\` tool.
|
|
21513
|
-
- SMTP credentials are expected from wallet records (\`ACCESS_TOKEN\`, service \`smtp\`, key \`use-email-smtp-credentials\`).
|
|
21514
|
-
- Commitment content can optionally begin with a default sender email address:
|
|
21515
|
-
- \`USE EMAIL agent@example.com\`
|
|
21516
|
-
- Remaining commitment content is treated as optional email-writing instructions.
|
|
21517
|
-
|
|
21518
|
-
## Examples
|
|
21519
|
-
|
|
21520
|
-
\`\`\`book
|
|
21521
|
-
Writing Agent
|
|
21522
|
-
USE EMAIL agent@example.com
|
|
21523
|
-
RULE Write emails to customers according to the instructions from user.
|
|
21524
|
-
\`\`\`
|
|
21525
|
-
|
|
21526
|
-
\`\`\`book
|
|
21527
|
-
Formal Email Assistant
|
|
21528
|
-
USE EMAIL agent@example.com Keep emails concise and formal.
|
|
21529
|
-
\`\`\`
|
|
21530
|
-
`);
|
|
22026
|
+
else if (configuredCalendars.length > 1) {
|
|
22027
|
+
throw new Error('Calendar is ambiguous. Provide "calendarUrl" argument with one calendar from USE CALENDAR commitments.');
|
|
21531
22028
|
}
|
|
21532
|
-
|
|
21533
|
-
|
|
21534
|
-
const extraInstructions = formatOptionalInstructionBlock('Email instructions', parsedCommitment.instructions);
|
|
21535
|
-
const senderInstruction = parsedCommitment.senderEmail
|
|
21536
|
-
? `- Default sender address from commitment: "${parsedCommitment.senderEmail}".`
|
|
21537
|
-
: '';
|
|
21538
|
-
const updatedTools = addUseEmailTools(requirements.tools || []);
|
|
21539
|
-
return this.appendToSystemMessage({
|
|
21540
|
-
...requirements,
|
|
21541
|
-
tools: updatedTools,
|
|
21542
|
-
_metadata: {
|
|
21543
|
-
...requirements._metadata,
|
|
21544
|
-
useEmail: true,
|
|
21545
|
-
...(parsedCommitment.senderEmail ? { useEmailSender: parsedCommitment.senderEmail } : {}),
|
|
21546
|
-
},
|
|
21547
|
-
}, spaceTrim$1((block) => `
|
|
21548
|
-
Email tool:
|
|
21549
|
-
- Use "${SEND_EMAIL_TOOL_NAME}" to send outbound emails.
|
|
21550
|
-
- Prefer \`message\` argument compatible with Promptbook \`Message\` type.
|
|
21551
|
-
- Include subject in \`message.metadata.subject\` (or use legacy \`subject\` argument).
|
|
21552
|
-
- USE EMAIL credentials are read from wallet records (ACCESS_TOKEN, service "${USE_EMAIL_SMTP_WALLET_SERVICE}", key "${USE_EMAIL_SMTP_WALLET_KEY}").
|
|
21553
|
-
- Wallet secret must contain SMTP credentials in JSON format with fields \`host\`, \`port\`, \`secure\`, \`username\`, \`password\`.
|
|
21554
|
-
- If credentials are missing, ask user to add wallet credentials.
|
|
21555
|
-
${block(senderInstruction)}
|
|
21556
|
-
${block(extraInstructions)}
|
|
21557
|
-
`));
|
|
22029
|
+
else {
|
|
22030
|
+
throw new Error('Calendar is required. Provide "calendarUrl" argument in the tool call.');
|
|
21558
22031
|
}
|
|
21559
|
-
|
|
21560
|
-
|
|
21561
|
-
*/
|
|
21562
|
-
getToolTitles() {
|
|
21563
|
-
return {
|
|
21564
|
-
[SEND_EMAIL_TOOL_NAME]: 'Send email',
|
|
21565
|
-
};
|
|
22032
|
+
if (!calendarReference) {
|
|
22033
|
+
throw new Error('Calendar is required but was not resolved.');
|
|
21566
22034
|
}
|
|
21567
|
-
|
|
21568
|
-
|
|
21569
|
-
|
|
21570
|
-
|
|
21571
|
-
|
|
21572
|
-
|
|
21573
|
-
|
|
21574
|
-
|
|
21575
|
-
|
|
21576
|
-
}
|
|
21577
|
-
}
|
|
22035
|
+
const accessToken = normalizeOptionalText$1((_b = runtimeContext.calendars) === null || _b === void 0 ? void 0 : _b.googleAccessToken) || '';
|
|
22036
|
+
if (!accessToken) {
|
|
22037
|
+
throw new CalendarWalletCredentialRequiredError({
|
|
22038
|
+
calendarReference,
|
|
22039
|
+
});
|
|
22040
|
+
}
|
|
22041
|
+
if (configuredCalendars.length > 0) {
|
|
22042
|
+
const allowedCalendarUrls = new Set(configuredCalendars.map((configuredCalendar) => configuredCalendar.url));
|
|
22043
|
+
if (!allowedCalendarUrls.has(calendarReference.url)) {
|
|
22044
|
+
throw new Error(`Calendar "${calendarReference.url}" is not configured by USE CALENDAR for this agent.`);
|
|
22045
|
+
}
|
|
21578
22046
|
}
|
|
22047
|
+
return {
|
|
22048
|
+
calendarReference,
|
|
22049
|
+
accessToken,
|
|
22050
|
+
};
|
|
21579
22051
|
}
|
|
21580
22052
|
/**
|
|
21581
|
-
*
|
|
22053
|
+
* Normalizes optional calendar list from runtime context.
|
|
21582
22054
|
*
|
|
21583
|
-
* @private
|
|
22055
|
+
* @private function of resolveUseCalendarToolRuntimeOrWalletCredentialResult
|
|
21584
22056
|
*/
|
|
21585
|
-
function
|
|
21586
|
-
if (
|
|
21587
|
-
return [
|
|
22057
|
+
function normalizeConfiguredCalendars$1(rawCalendars) {
|
|
22058
|
+
if (!Array.isArray(rawCalendars)) {
|
|
22059
|
+
return [];
|
|
21588
22060
|
}
|
|
21589
|
-
|
|
21590
|
-
|
|
21591
|
-
|
|
21592
|
-
|
|
21593
|
-
|
|
21594
|
-
|
|
21595
|
-
|
|
21596
|
-
|
|
21597
|
-
|
|
21598
|
-
|
|
21599
|
-
|
|
21600
|
-
|
|
21601
|
-
|
|
21602
|
-
|
|
21603
|
-
|
|
21604
|
-
|
|
21605
|
-
|
|
21606
|
-
|
|
21607
|
-
|
|
21608
|
-
|
|
21609
|
-
|
|
21610
|
-
|
|
21611
|
-
|
|
21612
|
-
|
|
22061
|
+
const calendars = [];
|
|
22062
|
+
const knownCalendars = new Set();
|
|
22063
|
+
for (const rawCalendar of rawCalendars) {
|
|
22064
|
+
if (!rawCalendar || typeof rawCalendar !== 'object') {
|
|
22065
|
+
continue;
|
|
22066
|
+
}
|
|
22067
|
+
const calendar = rawCalendar;
|
|
22068
|
+
const rawUrl = normalizeOptionalText$1(calendar.url);
|
|
22069
|
+
if (!rawUrl) {
|
|
22070
|
+
continue;
|
|
22071
|
+
}
|
|
22072
|
+
const parsedReference = parseGoogleCalendarReference(rawUrl);
|
|
22073
|
+
if (!parsedReference) {
|
|
22074
|
+
continue;
|
|
22075
|
+
}
|
|
22076
|
+
const key = `${parsedReference.provider}|${parsedReference.url}`;
|
|
22077
|
+
if (knownCalendars.has(key)) {
|
|
22078
|
+
continue;
|
|
22079
|
+
}
|
|
22080
|
+
knownCalendars.add(key);
|
|
22081
|
+
const scopes = Array.isArray(calendar.scopes)
|
|
22082
|
+
? calendar.scopes
|
|
22083
|
+
.filter((scope) => typeof scope === 'string')
|
|
22084
|
+
.map((scope) => scope.trim())
|
|
22085
|
+
.filter(Boolean)
|
|
22086
|
+
: [];
|
|
22087
|
+
calendars.push({
|
|
22088
|
+
...parsedReference,
|
|
22089
|
+
scopes: scopes.length > 0 ? scopes : parsedReference.scopes,
|
|
22090
|
+
});
|
|
22091
|
+
}
|
|
22092
|
+
return calendars;
|
|
22093
|
+
}
|
|
22094
|
+
/**
|
|
22095
|
+
* Converts missing-wallet errors into structured tool result payloads.
|
|
22096
|
+
*
|
|
22097
|
+
* @private function of resolveUseCalendarToolRuntimeOrWalletCredentialResult
|
|
22098
|
+
*/
|
|
22099
|
+
function createCalendarWalletCredentialRequiredResult(error) {
|
|
22100
|
+
return {
|
|
22101
|
+
action: 'calendar-auth',
|
|
22102
|
+
status: 'wallet-credential-required',
|
|
22103
|
+
recordType: 'ACCESS_TOKEN',
|
|
22104
|
+
service: error.service,
|
|
22105
|
+
key: error.key,
|
|
22106
|
+
isUserScoped: false,
|
|
22107
|
+
isGlobal: false,
|
|
22108
|
+
provider: 'google',
|
|
22109
|
+
calendarUrl: error.calendarReference.url,
|
|
22110
|
+
scopes: error.calendarReference.scopes,
|
|
22111
|
+
message: error.message,
|
|
22112
|
+
};
|
|
22113
|
+
}
|
|
22114
|
+
/**
|
|
22115
|
+
* Normalizes unknown text input to trimmed non-empty string.
|
|
22116
|
+
*
|
|
22117
|
+
* @private function of resolveUseCalendarToolRuntimeOrWalletCredentialResult
|
|
22118
|
+
*/
|
|
22119
|
+
function normalizeOptionalText$1(value) {
|
|
22120
|
+
if (typeof value !== 'string') {
|
|
22121
|
+
return undefined;
|
|
22122
|
+
}
|
|
22123
|
+
const trimmedValue = value.trim();
|
|
22124
|
+
return trimmedValue || undefined;
|
|
22125
|
+
}
|
|
22126
|
+
|
|
22127
|
+
/**
|
|
22128
|
+
* Names of tools used by the USE CALENDAR commitment.
|
|
22129
|
+
*
|
|
22130
|
+
* @private constant of UseCalendarCommitmentDefinition
|
|
22131
|
+
*/
|
|
22132
|
+
const UseCalendarToolNames = {
|
|
22133
|
+
listEvents: 'calendar_list_events',
|
|
22134
|
+
getEvent: 'calendar_get_event',
|
|
22135
|
+
createEvent: 'calendar_create_event',
|
|
22136
|
+
updateEvent: 'calendar_update_event',
|
|
22137
|
+
deleteEvent: 'calendar_delete_event',
|
|
22138
|
+
inviteGuests: 'calendar_invite_guests',
|
|
22139
|
+
};
|
|
22140
|
+
|
|
22141
|
+
/**
|
|
22142
|
+
* Gets Google Calendar tool function implementations.
|
|
22143
|
+
*
|
|
22144
|
+
* @private function of UseCalendarCommitmentDefinition
|
|
22145
|
+
*/
|
|
22146
|
+
function createUseCalendarToolFunctions() {
|
|
22147
|
+
return {
|
|
22148
|
+
async [UseCalendarToolNames.listEvents](args) {
|
|
22149
|
+
return withUseCalendarRuntime(args, async ({ calendarReference, accessToken }) => {
|
|
22150
|
+
const query = {};
|
|
22151
|
+
if (normalizeOptionalText(args.timeMin)) {
|
|
22152
|
+
query.timeMin = args.timeMin.trim();
|
|
22153
|
+
}
|
|
22154
|
+
if (normalizeOptionalText(args.timeMax)) {
|
|
22155
|
+
query.timeMax = args.timeMax.trim();
|
|
22156
|
+
}
|
|
22157
|
+
if (normalizeOptionalText(args.query)) {
|
|
22158
|
+
query.q = args.query.trim();
|
|
22159
|
+
}
|
|
22160
|
+
if (typeof args.maxResults === 'number' && Number.isFinite(args.maxResults) && args.maxResults > 0) {
|
|
22161
|
+
query.maxResults = String(Math.floor(args.maxResults));
|
|
22162
|
+
}
|
|
22163
|
+
if (args.singleEvents !== undefined) {
|
|
22164
|
+
query.singleEvents = args.singleEvents ? 'true' : 'false';
|
|
22165
|
+
}
|
|
22166
|
+
if (args.orderBy === 'startTime' || args.orderBy === 'updated') {
|
|
22167
|
+
query.orderBy = args.orderBy;
|
|
22168
|
+
}
|
|
22169
|
+
if (normalizeOptionalText(args.timeZone)) {
|
|
22170
|
+
query.timeZone = args.timeZone.trim();
|
|
22171
|
+
}
|
|
22172
|
+
const payload = await callGoogleCalendarApi(accessToken, {
|
|
22173
|
+
method: 'GET',
|
|
22174
|
+
path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events`,
|
|
22175
|
+
query,
|
|
22176
|
+
});
|
|
22177
|
+
const events = ((payload === null || payload === void 0 ? void 0 : payload.items) || []).map((event) => mapGoogleCalendarEvent(event));
|
|
22178
|
+
return JSON.stringify({
|
|
22179
|
+
provider: calendarReference.provider,
|
|
22180
|
+
calendarUrl: calendarReference.url,
|
|
22181
|
+
calendarId: calendarReference.calendarId,
|
|
22182
|
+
events,
|
|
22183
|
+
nextPageToken: (payload === null || payload === void 0 ? void 0 : payload.nextPageToken) || null,
|
|
22184
|
+
nextSyncToken: (payload === null || payload === void 0 ? void 0 : payload.nextSyncToken) || null,
|
|
22185
|
+
});
|
|
22186
|
+
});
|
|
22187
|
+
},
|
|
22188
|
+
async [UseCalendarToolNames.getEvent](args) {
|
|
22189
|
+
return withUseCalendarRuntime(args, async ({ calendarReference, accessToken }) => {
|
|
22190
|
+
const eventId = normalizeRequiredText(args.eventId, 'eventId');
|
|
22191
|
+
const payload = await callGoogleCalendarApi(accessToken, {
|
|
22192
|
+
method: 'GET',
|
|
22193
|
+
path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events/${encodeURIComponent(eventId)}`,
|
|
22194
|
+
});
|
|
22195
|
+
return JSON.stringify({
|
|
22196
|
+
provider: calendarReference.provider,
|
|
22197
|
+
calendarUrl: calendarReference.url,
|
|
22198
|
+
calendarId: calendarReference.calendarId,
|
|
22199
|
+
event: mapGoogleCalendarEvent(payload || {}),
|
|
22200
|
+
});
|
|
22201
|
+
});
|
|
22202
|
+
},
|
|
22203
|
+
async [UseCalendarToolNames.createEvent](args) {
|
|
22204
|
+
return withUseCalendarRuntime(args, async ({ calendarReference, accessToken }) => {
|
|
22205
|
+
const requestBody = createGoogleCalendarEventPayload({
|
|
22206
|
+
summary: normalizeRequiredText(args.summary, 'summary'),
|
|
22207
|
+
description: normalizeOptionalText(args.description),
|
|
22208
|
+
location: normalizeOptionalText(args.location),
|
|
22209
|
+
start: normalizeRequiredText(args.start, 'start'),
|
|
22210
|
+
end: normalizeRequiredText(args.end, 'end'),
|
|
22211
|
+
timeZone: normalizeOptionalText(args.timeZone),
|
|
22212
|
+
attendees: normalizeAttendees(args.attendees),
|
|
22213
|
+
reminderMinutes: normalizeReminderMinutes(args.reminderMinutes),
|
|
22214
|
+
});
|
|
22215
|
+
const payload = await callGoogleCalendarApi(accessToken, {
|
|
22216
|
+
method: 'POST',
|
|
22217
|
+
path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events`,
|
|
22218
|
+
query: createSendUpdatesQuery(args.sendUpdates),
|
|
22219
|
+
body: requestBody,
|
|
22220
|
+
});
|
|
22221
|
+
return JSON.stringify({
|
|
22222
|
+
provider: calendarReference.provider,
|
|
22223
|
+
calendarUrl: calendarReference.url,
|
|
22224
|
+
calendarId: calendarReference.calendarId,
|
|
22225
|
+
event: mapGoogleCalendarEvent(payload || {}),
|
|
22226
|
+
});
|
|
22227
|
+
});
|
|
22228
|
+
},
|
|
22229
|
+
async [UseCalendarToolNames.updateEvent](args) {
|
|
22230
|
+
return withUseCalendarRuntime(args, async ({ calendarReference, accessToken }) => {
|
|
22231
|
+
const eventId = normalizeRequiredText(args.eventId, 'eventId');
|
|
22232
|
+
const requestBody = createGoogleCalendarEventPayload({
|
|
22233
|
+
summary: normalizeOptionalText(args.summary),
|
|
22234
|
+
description: normalizeOptionalText(args.description),
|
|
22235
|
+
location: normalizeOptionalText(args.location),
|
|
22236
|
+
start: normalizeOptionalText(args.start),
|
|
22237
|
+
end: normalizeOptionalText(args.end),
|
|
22238
|
+
timeZone: normalizeOptionalText(args.timeZone),
|
|
22239
|
+
attendees: normalizeAttendees(args.attendees),
|
|
22240
|
+
reminderMinutes: normalizeReminderMinutes(args.reminderMinutes),
|
|
22241
|
+
});
|
|
22242
|
+
const payload = await callGoogleCalendarApi(accessToken, {
|
|
22243
|
+
method: 'PATCH',
|
|
22244
|
+
path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events/${encodeURIComponent(eventId)}`,
|
|
22245
|
+
query: createSendUpdatesQuery(args.sendUpdates),
|
|
22246
|
+
body: requestBody,
|
|
22247
|
+
});
|
|
22248
|
+
return JSON.stringify({
|
|
22249
|
+
provider: calendarReference.provider,
|
|
22250
|
+
calendarUrl: calendarReference.url,
|
|
22251
|
+
calendarId: calendarReference.calendarId,
|
|
22252
|
+
event: mapGoogleCalendarEvent(payload || {}),
|
|
22253
|
+
});
|
|
22254
|
+
});
|
|
22255
|
+
},
|
|
22256
|
+
async [UseCalendarToolNames.deleteEvent](args) {
|
|
22257
|
+
return withUseCalendarRuntime(args, async ({ calendarReference, accessToken }) => {
|
|
22258
|
+
const eventId = normalizeRequiredText(args.eventId, 'eventId');
|
|
22259
|
+
await callGoogleCalendarApi(accessToken, {
|
|
22260
|
+
method: 'DELETE',
|
|
22261
|
+
path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events/${encodeURIComponent(eventId)}`,
|
|
22262
|
+
query: createSendUpdatesQuery(args.sendUpdates),
|
|
22263
|
+
});
|
|
22264
|
+
return JSON.stringify({
|
|
22265
|
+
provider: calendarReference.provider,
|
|
22266
|
+
calendarUrl: calendarReference.url,
|
|
22267
|
+
calendarId: calendarReference.calendarId,
|
|
22268
|
+
eventId,
|
|
22269
|
+
status: 'deleted',
|
|
22270
|
+
});
|
|
22271
|
+
});
|
|
22272
|
+
},
|
|
22273
|
+
async [UseCalendarToolNames.inviteGuests](args) {
|
|
22274
|
+
return withUseCalendarRuntime(args, async ({ calendarReference, accessToken }) => {
|
|
22275
|
+
const eventId = normalizeRequiredText(args.eventId, 'eventId');
|
|
22276
|
+
const guests = normalizeAttendees(args.guests);
|
|
22277
|
+
if (guests.length === 0) {
|
|
22278
|
+
throw new Error('Tool "calendar_invite_guests" requires non-empty "guests".');
|
|
22279
|
+
}
|
|
22280
|
+
const existingEvent = await callGoogleCalendarApi(accessToken, {
|
|
22281
|
+
method: 'GET',
|
|
22282
|
+
path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events/${encodeURIComponent(eventId)}`,
|
|
22283
|
+
});
|
|
22284
|
+
const existingAttendees = ((existingEvent === null || existingEvent === void 0 ? void 0 : existingEvent.attendees) || [])
|
|
22285
|
+
.map((attendee) => normalizeOptionalText(attendee.email))
|
|
22286
|
+
.filter((email) => Boolean(email));
|
|
22287
|
+
const mergedAttendees = [...new Set([...existingAttendees, ...guests])];
|
|
22288
|
+
const payload = await callGoogleCalendarApi(accessToken, {
|
|
22289
|
+
method: 'PATCH',
|
|
22290
|
+
path: `/calendars/${encodeGoogleCalendarId(calendarReference.calendarId)}/events/${encodeURIComponent(eventId)}`,
|
|
22291
|
+
query: createSendUpdatesQuery(args.sendUpdates),
|
|
21613
22292
|
body: {
|
|
21614
|
-
|
|
21615
|
-
description: 'Legacy alias for markdown body content.',
|
|
22293
|
+
attendees: mergedAttendees.map((email) => ({ email })),
|
|
21616
22294
|
},
|
|
21617
|
-
}
|
|
21618
|
-
|
|
21619
|
-
|
|
22295
|
+
});
|
|
22296
|
+
return JSON.stringify({
|
|
22297
|
+
provider: calendarReference.provider,
|
|
22298
|
+
calendarUrl: calendarReference.url,
|
|
22299
|
+
calendarId: calendarReference.calendarId,
|
|
22300
|
+
event: mapGoogleCalendarEvent(payload || {}),
|
|
22301
|
+
invitedGuests: guests,
|
|
22302
|
+
});
|
|
22303
|
+
});
|
|
21620
22304
|
},
|
|
21621
|
-
|
|
22305
|
+
};
|
|
21622
22306
|
}
|
|
21623
22307
|
/**
|
|
21624
|
-
*
|
|
22308
|
+
* Executes one tool operation with resolved USE CALENDAR runtime.
|
|
22309
|
+
*
|
|
22310
|
+
* @private function of createUseCalendarToolFunctions
|
|
21625
22311
|
*/
|
|
21626
|
-
|
|
22312
|
+
async function withUseCalendarRuntime(args, operation) {
|
|
22313
|
+
const runtime = resolveUseCalendarToolRuntimeOrWalletCredentialResult(args);
|
|
22314
|
+
if ('walletResult' in runtime) {
|
|
22315
|
+
return runtime.walletResult;
|
|
22316
|
+
}
|
|
22317
|
+
return operation(runtime);
|
|
22318
|
+
}
|
|
21627
22319
|
/**
|
|
21628
|
-
*
|
|
22320
|
+
* Encodes one Google calendar id for URL path usage.
|
|
21629
22321
|
*
|
|
21630
|
-
*
|
|
21631
|
-
|
|
22322
|
+
* @private function of createUseCalendarToolFunctions
|
|
22323
|
+
*/
|
|
22324
|
+
function encodeGoogleCalendarId(calendarId) {
|
|
22325
|
+
return encodeURIComponent(calendarId);
|
|
22326
|
+
}
|
|
22327
|
+
/**
|
|
22328
|
+
* Normalizes one required textual input.
|
|
21632
22329
|
*
|
|
21633
|
-
*
|
|
22330
|
+
* @private function of createUseCalendarToolFunctions
|
|
22331
|
+
*/
|
|
22332
|
+
function normalizeRequiredText(value, fieldName) {
|
|
22333
|
+
const normalizedValue = normalizeOptionalText(value);
|
|
22334
|
+
if (!normalizedValue) {
|
|
22335
|
+
throw new Error(`Tool "${fieldName}" requires non-empty value.`);
|
|
22336
|
+
}
|
|
22337
|
+
return normalizedValue;
|
|
22338
|
+
}
|
|
22339
|
+
/**
|
|
22340
|
+
* Normalizes unknown text input to trimmed non-empty string.
|
|
21634
22341
|
*
|
|
21635
|
-
*
|
|
21636
|
-
|
|
21637
|
-
|
|
21638
|
-
|
|
22342
|
+
* @private function of createUseCalendarToolFunctions
|
|
22343
|
+
*/
|
|
22344
|
+
function normalizeOptionalText(value) {
|
|
22345
|
+
if (typeof value !== 'string') {
|
|
22346
|
+
return undefined;
|
|
22347
|
+
}
|
|
22348
|
+
const trimmedValue = value.trim();
|
|
22349
|
+
return trimmedValue || undefined;
|
|
22350
|
+
}
|
|
22351
|
+
/**
|
|
22352
|
+
* Normalizes optional attendee list from tool input.
|
|
21639
22353
|
*
|
|
21640
|
-
* @private
|
|
22354
|
+
* @private function of createUseCalendarToolFunctions
|
|
21641
22355
|
*/
|
|
21642
|
-
|
|
21643
|
-
|
|
21644
|
-
|
|
22356
|
+
function normalizeAttendees(value) {
|
|
22357
|
+
if (!Array.isArray(value)) {
|
|
22358
|
+
return [];
|
|
21645
22359
|
}
|
|
21646
|
-
|
|
21647
|
-
|
|
22360
|
+
const normalizedAttendees = value
|
|
22361
|
+
.filter((attendee) => typeof attendee === 'string')
|
|
22362
|
+
.map((attendee) => attendee.trim())
|
|
22363
|
+
.filter(Boolean);
|
|
22364
|
+
return [...new Set(normalizedAttendees)];
|
|
22365
|
+
}
|
|
22366
|
+
/**
|
|
22367
|
+
* Normalizes optional reminder-minute offsets from tool input.
|
|
22368
|
+
*
|
|
22369
|
+
* @private function of createUseCalendarToolFunctions
|
|
22370
|
+
*/
|
|
22371
|
+
function normalizeReminderMinutes(value) {
|
|
22372
|
+
if (!Array.isArray(value)) {
|
|
22373
|
+
return [];
|
|
21648
22374
|
}
|
|
21649
|
-
|
|
21650
|
-
|
|
21651
|
-
|
|
21652
|
-
|
|
21653
|
-
|
|
22375
|
+
const reminderMinutes = value
|
|
22376
|
+
.filter((minute) => typeof minute === 'number' && Number.isFinite(minute))
|
|
22377
|
+
.map((minute) => Math.max(0, Math.floor(minute)));
|
|
22378
|
+
return [...new Set(reminderMinutes)];
|
|
22379
|
+
}
|
|
22380
|
+
/**
|
|
22381
|
+
* Builds optional `sendUpdates` query for mutating Google Calendar requests.
|
|
22382
|
+
*
|
|
22383
|
+
* @private function of createUseCalendarToolFunctions
|
|
22384
|
+
*/
|
|
22385
|
+
function createSendUpdatesQuery(sendUpdates) {
|
|
22386
|
+
if (sendUpdates === 'all' || sendUpdates === 'externalOnly' || sendUpdates === 'none') {
|
|
22387
|
+
return { sendUpdates };
|
|
21654
22388
|
}
|
|
21655
|
-
|
|
21656
|
-
|
|
21657
|
-
|
|
21658
|
-
|
|
21659
|
-
|
|
22389
|
+
return {};
|
|
22390
|
+
}
|
|
22391
|
+
/**
|
|
22392
|
+
* Creates one Google Calendar event payload from normalized tool arguments.
|
|
22393
|
+
*
|
|
22394
|
+
* @private function of createUseCalendarToolFunctions
|
|
22395
|
+
*/
|
|
22396
|
+
function createGoogleCalendarEventPayload(options) {
|
|
22397
|
+
const payload = {};
|
|
22398
|
+
if (options.summary) {
|
|
22399
|
+
payload.summary = options.summary;
|
|
21660
22400
|
}
|
|
21661
|
-
|
|
21662
|
-
|
|
21663
|
-
*/
|
|
21664
|
-
get documentation() {
|
|
21665
|
-
return spaceTrim$1(`
|
|
21666
|
-
# USE IMAGE GENERATOR
|
|
21667
|
-
|
|
21668
|
-
Enables the agent to output markdown image placeholders that trigger image generation in the user interface.
|
|
21669
|
-
|
|
21670
|
-
## Key aspects
|
|
21671
|
-
|
|
21672
|
-
- The content following \`USE IMAGE GENERATOR\` is an arbitrary text that the agent should know (e.g. style instructions or safety guidelines).
|
|
21673
|
-
- The agent does **not** call an image-generation tool directly.
|
|
21674
|
-
- The agent inserts markdown notation: \`\`.
|
|
21675
|
-
- The user interface detects the notation and generates the image asynchronously.
|
|
21676
|
-
|
|
21677
|
-
## Examples
|
|
21678
|
-
|
|
21679
|
-
\`\`\`book
|
|
21680
|
-
Visual Artist
|
|
21681
|
-
|
|
21682
|
-
PERSONA You are a creative visual artist.
|
|
21683
|
-
USE IMAGE GENERATOR
|
|
21684
|
-
RULE Always describe the generated image to the user.
|
|
21685
|
-
\`\`\`
|
|
21686
|
-
|
|
21687
|
-
\`\`\`book
|
|
21688
|
-
Interior Designer
|
|
21689
|
-
|
|
21690
|
-
PERSONA You are an interior designer who helps users visualize their space.
|
|
21691
|
-
USE IMAGE GENERATOR Professional interior design renders.
|
|
21692
|
-
ACTION Add one generated image placeholder whenever a user asks for a visual.
|
|
21693
|
-
\`\`\`
|
|
21694
|
-
`);
|
|
22401
|
+
if (options.description) {
|
|
22402
|
+
payload.description = options.description;
|
|
21695
22403
|
}
|
|
21696
|
-
|
|
21697
|
-
|
|
21698
|
-
|
|
21699
|
-
|
|
21700
|
-
|
|
21701
|
-
|
|
21702
|
-
|
|
21703
|
-
|
|
21704
|
-
|
|
21705
|
-
|
|
21706
|
-
|
|
21707
|
-
- When the user asks for an image, include markdown notation in your message:
|
|
21708
|
-
\`\`
|
|
21709
|
-
- Keep \`<alt text>\` short and descriptive.
|
|
21710
|
-
- Keep \`<prompt>\` detailed so the generated image matches the request.
|
|
21711
|
-
- You can include normal explanatory text before and after the notation.
|
|
21712
|
-
${block(extraInstructions)}
|
|
21713
|
-
`));
|
|
22404
|
+
if (options.location) {
|
|
22405
|
+
payload.location = options.location;
|
|
22406
|
+
}
|
|
22407
|
+
if (options.start) {
|
|
22408
|
+
payload.start = createGoogleCalendarDateValue(options.start, options.timeZone);
|
|
22409
|
+
}
|
|
22410
|
+
if (options.end) {
|
|
22411
|
+
payload.end = createGoogleCalendarDateValue(options.end, options.timeZone);
|
|
22412
|
+
}
|
|
22413
|
+
if (options.attendees && options.attendees.length > 0) {
|
|
22414
|
+
payload.attendees = options.attendees.map((email) => ({ email }));
|
|
21714
22415
|
}
|
|
22416
|
+
if (options.reminderMinutes && options.reminderMinutes.length > 0) {
|
|
22417
|
+
payload.reminders = {
|
|
22418
|
+
useDefault: false,
|
|
22419
|
+
overrides: options.reminderMinutes.map((minutes) => ({
|
|
22420
|
+
method: 'popup',
|
|
22421
|
+
minutes,
|
|
22422
|
+
})),
|
|
22423
|
+
};
|
|
22424
|
+
}
|
|
22425
|
+
return payload;
|
|
21715
22426
|
}
|
|
21716
22427
|
/**
|
|
21717
|
-
*
|
|
22428
|
+
* Converts date/dateTime input into a Google Calendar-compatible date object.
|
|
22429
|
+
*
|
|
22430
|
+
* @private function of createUseCalendarToolFunctions
|
|
21718
22431
|
*/
|
|
22432
|
+
function createGoogleCalendarDateValue(value, timeZone) {
|
|
22433
|
+
const isDateOnly = /^\d{4}-\d{2}-\d{2}$/.test(value);
|
|
22434
|
+
if (isDateOnly) {
|
|
22435
|
+
return {
|
|
22436
|
+
date: value,
|
|
22437
|
+
};
|
|
22438
|
+
}
|
|
22439
|
+
return {
|
|
22440
|
+
dateTime: value,
|
|
22441
|
+
...(timeZone ? { timeZone } : {}),
|
|
22442
|
+
};
|
|
22443
|
+
}
|
|
22444
|
+
/**
|
|
22445
|
+
* Maps raw Google Calendar event payload to a compact tool result object.
|
|
22446
|
+
*
|
|
22447
|
+
* @private function of createUseCalendarToolFunctions
|
|
22448
|
+
*/
|
|
22449
|
+
function mapGoogleCalendarEvent(event) {
|
|
22450
|
+
return {
|
|
22451
|
+
id: event.id || null,
|
|
22452
|
+
summary: event.summary || null,
|
|
22453
|
+
description: event.description || null,
|
|
22454
|
+
location: event.location || null,
|
|
22455
|
+
status: event.status || null,
|
|
22456
|
+
htmlLink: event.htmlLink || null,
|
|
22457
|
+
start: event.start || null,
|
|
22458
|
+
end: event.end || null,
|
|
22459
|
+
organizer: event.organizer || null,
|
|
22460
|
+
attendees: (event.attendees || []).map((attendee) => ({
|
|
22461
|
+
email: attendee.email || null,
|
|
22462
|
+
responseStatus: attendee.responseStatus || null,
|
|
22463
|
+
})),
|
|
22464
|
+
};
|
|
22465
|
+
}
|
|
21719
22466
|
|
|
21720
22467
|
/**
|
|
21721
|
-
* USE
|
|
22468
|
+
* Shared calendar URL argument description used in USE CALENDAR tool schemas.
|
|
21722
22469
|
*
|
|
21723
|
-
*
|
|
21724
|
-
|
|
22470
|
+
* @private constant of createUseCalendarTools
|
|
22471
|
+
*/
|
|
22472
|
+
const CALENDAR_URL_PARAMETER_DESCRIPTION = 'Google Calendar URL configured by USE CALENDAR (for example "https://calendar.google.com/...").';
|
|
22473
|
+
/**
|
|
22474
|
+
* Adds USE CALENDAR tool definitions while keeping already registered tools untouched.
|
|
21725
22475
|
*
|
|
21726
|
-
*
|
|
22476
|
+
* @private function of UseCalendarCommitmentDefinition
|
|
22477
|
+
*/
|
|
22478
|
+
function createUseCalendarTools(existingTools) {
|
|
22479
|
+
const updatedTools = [...existingTools];
|
|
22480
|
+
const addToolIfMissing = (tool) => {
|
|
22481
|
+
if (!updatedTools.some((existingTool) => existingTool.name === tool.name)) {
|
|
22482
|
+
updatedTools.push(tool);
|
|
22483
|
+
}
|
|
22484
|
+
};
|
|
22485
|
+
addToolIfMissing({
|
|
22486
|
+
name: UseCalendarToolNames.listEvents,
|
|
22487
|
+
description: 'List events from a configured calendar for a time range.',
|
|
22488
|
+
parameters: {
|
|
22489
|
+
type: 'object',
|
|
22490
|
+
properties: {
|
|
22491
|
+
calendarUrl: {
|
|
22492
|
+
type: 'string',
|
|
22493
|
+
description: CALENDAR_URL_PARAMETER_DESCRIPTION,
|
|
22494
|
+
},
|
|
22495
|
+
timeMin: {
|
|
22496
|
+
type: 'string',
|
|
22497
|
+
description: 'Inclusive event start bound in ISO datetime.',
|
|
22498
|
+
},
|
|
22499
|
+
timeMax: {
|
|
22500
|
+
type: 'string',
|
|
22501
|
+
description: 'Exclusive event end bound in ISO datetime.',
|
|
22502
|
+
},
|
|
22503
|
+
query: {
|
|
22504
|
+
type: 'string',
|
|
22505
|
+
description: 'Optional free-text event search query.',
|
|
22506
|
+
},
|
|
22507
|
+
maxResults: {
|
|
22508
|
+
type: 'integer',
|
|
22509
|
+
description: 'Maximum number of events to return.',
|
|
22510
|
+
},
|
|
22511
|
+
singleEvents: {
|
|
22512
|
+
type: 'boolean',
|
|
22513
|
+
description: 'Expand recurring events into individual instances.',
|
|
22514
|
+
},
|
|
22515
|
+
orderBy: {
|
|
22516
|
+
type: 'string',
|
|
22517
|
+
description: 'Optional ordering ("startTime" or "updated").',
|
|
22518
|
+
},
|
|
22519
|
+
timeZone: {
|
|
22520
|
+
type: 'string',
|
|
22521
|
+
description: 'Optional IANA timezone for response rendering.',
|
|
22522
|
+
},
|
|
22523
|
+
},
|
|
22524
|
+
required: [],
|
|
22525
|
+
},
|
|
22526
|
+
});
|
|
22527
|
+
addToolIfMissing({
|
|
22528
|
+
name: UseCalendarToolNames.getEvent,
|
|
22529
|
+
description: 'Get one event by id from a configured calendar.',
|
|
22530
|
+
parameters: {
|
|
22531
|
+
type: 'object',
|
|
22532
|
+
properties: {
|
|
22533
|
+
calendarUrl: {
|
|
22534
|
+
type: 'string',
|
|
22535
|
+
description: CALENDAR_URL_PARAMETER_DESCRIPTION,
|
|
22536
|
+
},
|
|
22537
|
+
eventId: {
|
|
22538
|
+
type: 'string',
|
|
22539
|
+
description: 'Google Calendar event id.',
|
|
22540
|
+
},
|
|
22541
|
+
},
|
|
22542
|
+
required: ['eventId'],
|
|
22543
|
+
},
|
|
22544
|
+
});
|
|
22545
|
+
addToolIfMissing({
|
|
22546
|
+
name: UseCalendarToolNames.createEvent,
|
|
22547
|
+
description: 'Create one event in a configured calendar.',
|
|
22548
|
+
parameters: {
|
|
22549
|
+
type: 'object',
|
|
22550
|
+
properties: {
|
|
22551
|
+
calendarUrl: {
|
|
22552
|
+
type: 'string',
|
|
22553
|
+
description: CALENDAR_URL_PARAMETER_DESCRIPTION,
|
|
22554
|
+
},
|
|
22555
|
+
summary: {
|
|
22556
|
+
type: 'string',
|
|
22557
|
+
description: 'Event title/summary.',
|
|
22558
|
+
},
|
|
22559
|
+
description: {
|
|
22560
|
+
type: 'string',
|
|
22561
|
+
description: 'Optional event description.',
|
|
22562
|
+
},
|
|
22563
|
+
location: {
|
|
22564
|
+
type: 'string',
|
|
22565
|
+
description: 'Optional event location.',
|
|
22566
|
+
},
|
|
22567
|
+
start: {
|
|
22568
|
+
type: 'string',
|
|
22569
|
+
description: 'Event start as ISO datetime or date.',
|
|
22570
|
+
},
|
|
22571
|
+
end: {
|
|
22572
|
+
type: 'string',
|
|
22573
|
+
description: 'Event end as ISO datetime or date.',
|
|
22574
|
+
},
|
|
22575
|
+
timeZone: {
|
|
22576
|
+
type: 'string',
|
|
22577
|
+
description: 'Optional timezone for datetime values.',
|
|
22578
|
+
},
|
|
22579
|
+
attendees: {
|
|
22580
|
+
type: 'array',
|
|
22581
|
+
description: 'Optional guest email list.',
|
|
22582
|
+
},
|
|
22583
|
+
reminderMinutes: {
|
|
22584
|
+
type: 'array',
|
|
22585
|
+
description: 'Optional popup reminder minute offsets.',
|
|
22586
|
+
},
|
|
22587
|
+
sendUpdates: {
|
|
22588
|
+
type: 'string',
|
|
22589
|
+
description: 'Guest update policy ("all", "externalOnly", "none").',
|
|
22590
|
+
},
|
|
22591
|
+
},
|
|
22592
|
+
required: ['summary', 'start', 'end'],
|
|
22593
|
+
},
|
|
22594
|
+
});
|
|
22595
|
+
addToolIfMissing({
|
|
22596
|
+
name: UseCalendarToolNames.updateEvent,
|
|
22597
|
+
description: 'Update one existing event in a configured calendar.',
|
|
22598
|
+
parameters: {
|
|
22599
|
+
type: 'object',
|
|
22600
|
+
properties: {
|
|
22601
|
+
calendarUrl: {
|
|
22602
|
+
type: 'string',
|
|
22603
|
+
description: CALENDAR_URL_PARAMETER_DESCRIPTION,
|
|
22604
|
+
},
|
|
22605
|
+
eventId: {
|
|
22606
|
+
type: 'string',
|
|
22607
|
+
description: 'Google Calendar event id.',
|
|
22608
|
+
},
|
|
22609
|
+
summary: {
|
|
22610
|
+
type: 'string',
|
|
22611
|
+
description: 'Updated event summary.',
|
|
22612
|
+
},
|
|
22613
|
+
description: {
|
|
22614
|
+
type: 'string',
|
|
22615
|
+
description: 'Updated event description.',
|
|
22616
|
+
},
|
|
22617
|
+
location: {
|
|
22618
|
+
type: 'string',
|
|
22619
|
+
description: 'Updated event location.',
|
|
22620
|
+
},
|
|
22621
|
+
start: {
|
|
22622
|
+
type: 'string',
|
|
22623
|
+
description: 'Updated event start as ISO datetime or date.',
|
|
22624
|
+
},
|
|
22625
|
+
end: {
|
|
22626
|
+
type: 'string',
|
|
22627
|
+
description: 'Updated event end as ISO datetime or date.',
|
|
22628
|
+
},
|
|
22629
|
+
timeZone: {
|
|
22630
|
+
type: 'string',
|
|
22631
|
+
description: 'Optional timezone for datetime values.',
|
|
22632
|
+
},
|
|
22633
|
+
attendees: {
|
|
22634
|
+
type: 'array',
|
|
22635
|
+
description: 'Optional replacement guest email list.',
|
|
22636
|
+
},
|
|
22637
|
+
reminderMinutes: {
|
|
22638
|
+
type: 'array',
|
|
22639
|
+
description: 'Optional replacement popup reminder minute offsets.',
|
|
22640
|
+
},
|
|
22641
|
+
sendUpdates: {
|
|
22642
|
+
type: 'string',
|
|
22643
|
+
description: 'Guest update policy ("all", "externalOnly", "none").',
|
|
22644
|
+
},
|
|
22645
|
+
},
|
|
22646
|
+
required: ['eventId'],
|
|
22647
|
+
},
|
|
22648
|
+
});
|
|
22649
|
+
addToolIfMissing({
|
|
22650
|
+
name: UseCalendarToolNames.deleteEvent,
|
|
22651
|
+
description: 'Delete one event from a configured calendar.',
|
|
22652
|
+
parameters: {
|
|
22653
|
+
type: 'object',
|
|
22654
|
+
properties: {
|
|
22655
|
+
calendarUrl: {
|
|
22656
|
+
type: 'string',
|
|
22657
|
+
description: CALENDAR_URL_PARAMETER_DESCRIPTION,
|
|
22658
|
+
},
|
|
22659
|
+
eventId: {
|
|
22660
|
+
type: 'string',
|
|
22661
|
+
description: 'Google Calendar event id.',
|
|
22662
|
+
},
|
|
22663
|
+
sendUpdates: {
|
|
22664
|
+
type: 'string',
|
|
22665
|
+
description: 'Guest update policy ("all", "externalOnly", "none").',
|
|
22666
|
+
},
|
|
22667
|
+
},
|
|
22668
|
+
required: ['eventId'],
|
|
22669
|
+
},
|
|
22670
|
+
});
|
|
22671
|
+
addToolIfMissing({
|
|
22672
|
+
name: UseCalendarToolNames.inviteGuests,
|
|
22673
|
+
description: 'Add guests to an existing event in a configured calendar.',
|
|
22674
|
+
parameters: {
|
|
22675
|
+
type: 'object',
|
|
22676
|
+
properties: {
|
|
22677
|
+
calendarUrl: {
|
|
22678
|
+
type: 'string',
|
|
22679
|
+
description: CALENDAR_URL_PARAMETER_DESCRIPTION,
|
|
22680
|
+
},
|
|
22681
|
+
eventId: {
|
|
22682
|
+
type: 'string',
|
|
22683
|
+
description: 'Google Calendar event id.',
|
|
22684
|
+
},
|
|
22685
|
+
guests: {
|
|
22686
|
+
type: 'array',
|
|
22687
|
+
description: 'Guest email list to add to the event.',
|
|
22688
|
+
},
|
|
22689
|
+
sendUpdates: {
|
|
22690
|
+
type: 'string',
|
|
22691
|
+
description: 'Guest update policy ("all", "externalOnly", "none").',
|
|
22692
|
+
},
|
|
22693
|
+
},
|
|
22694
|
+
required: ['eventId', 'guests'],
|
|
22695
|
+
},
|
|
22696
|
+
});
|
|
22697
|
+
return updatedTools;
|
|
22698
|
+
}
|
|
22699
|
+
|
|
22700
|
+
/**
|
|
22701
|
+
* Gets human-readable tool labels for USE CALENDAR functions.
|
|
21727
22702
|
*
|
|
21728
|
-
*
|
|
22703
|
+
* @private function of UseCalendarCommitmentDefinition
|
|
22704
|
+
*/
|
|
22705
|
+
function getUseCalendarToolTitles() {
|
|
22706
|
+
return {
|
|
22707
|
+
[UseCalendarToolNames.listEvents]: 'List calendar events',
|
|
22708
|
+
[UseCalendarToolNames.getEvent]: 'Get calendar event',
|
|
22709
|
+
[UseCalendarToolNames.createEvent]: 'Create calendar event',
|
|
22710
|
+
[UseCalendarToolNames.updateEvent]: 'Update calendar event',
|
|
22711
|
+
[UseCalendarToolNames.deleteEvent]: 'Delete calendar event',
|
|
22712
|
+
[UseCalendarToolNames.inviteGuests]: 'Invite calendar guests',
|
|
22713
|
+
};
|
|
22714
|
+
}
|
|
22715
|
+
|
|
22716
|
+
/**
|
|
22717
|
+
* Normalizes unknown metadata payload into a typed list of configured calendars.
|
|
21729
22718
|
*
|
|
21730
|
-
*
|
|
21731
|
-
|
|
21732
|
-
|
|
22719
|
+
* @private internal utility of USE CALENDAR commitment
|
|
22720
|
+
*/
|
|
22721
|
+
function normalizeConfiguredCalendars(rawCalendars) {
|
|
22722
|
+
if (!Array.isArray(rawCalendars)) {
|
|
22723
|
+
return [];
|
|
22724
|
+
}
|
|
22725
|
+
const uniqueCalendars = new Set();
|
|
22726
|
+
const calendars = [];
|
|
22727
|
+
for (const rawCalendar of rawCalendars) {
|
|
22728
|
+
if (!rawCalendar || typeof rawCalendar !== 'object') {
|
|
22729
|
+
continue;
|
|
22730
|
+
}
|
|
22731
|
+
const calendar = rawCalendar;
|
|
22732
|
+
const provider = normalizeProvider(calendar.provider);
|
|
22733
|
+
const url = normalizeText(calendar.url);
|
|
22734
|
+
const calendarId = normalizeText(calendar.calendarId);
|
|
22735
|
+
if (!provider || !url || !calendarId) {
|
|
22736
|
+
continue;
|
|
22737
|
+
}
|
|
22738
|
+
const uniqueKey = `${provider}|${url}`;
|
|
22739
|
+
if (uniqueCalendars.has(uniqueKey)) {
|
|
22740
|
+
continue;
|
|
22741
|
+
}
|
|
22742
|
+
uniqueCalendars.add(uniqueKey);
|
|
22743
|
+
const scopes = Array.isArray(calendar.scopes)
|
|
22744
|
+
? calendar.scopes
|
|
22745
|
+
.filter((scope) => typeof scope === 'string')
|
|
22746
|
+
.map((scope) => scope.trim())
|
|
22747
|
+
.filter(Boolean)
|
|
22748
|
+
: [];
|
|
22749
|
+
calendars.push({
|
|
22750
|
+
provider,
|
|
22751
|
+
url,
|
|
22752
|
+
calendarId,
|
|
22753
|
+
scopes,
|
|
22754
|
+
...(normalizeText(calendar.tokenRef) ? { tokenRef: normalizeText(calendar.tokenRef) } : {}),
|
|
22755
|
+
});
|
|
22756
|
+
}
|
|
22757
|
+
return calendars;
|
|
22758
|
+
}
|
|
22759
|
+
/**
|
|
22760
|
+
* Normalizes optional provider text to one supported value.
|
|
22761
|
+
*
|
|
22762
|
+
* @private function of normalizeConfiguredCalendars
|
|
22763
|
+
*/
|
|
22764
|
+
function normalizeProvider(value) {
|
|
22765
|
+
if (typeof value !== 'string') {
|
|
22766
|
+
return null;
|
|
22767
|
+
}
|
|
22768
|
+
const normalizedProvider = value.trim().toLowerCase();
|
|
22769
|
+
if (normalizedProvider === 'google') {
|
|
22770
|
+
return 'google';
|
|
22771
|
+
}
|
|
22772
|
+
return null;
|
|
22773
|
+
}
|
|
22774
|
+
/**
|
|
22775
|
+
* Normalizes unknown text input to trimmed non-empty string.
|
|
22776
|
+
*
|
|
22777
|
+
* @private function of normalizeConfiguredCalendars
|
|
22778
|
+
*/
|
|
22779
|
+
function normalizeText(value) {
|
|
22780
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
22781
|
+
}
|
|
22782
|
+
|
|
22783
|
+
/**
|
|
22784
|
+
* USE CALENDAR commitment definition.
|
|
22785
|
+
*
|
|
22786
|
+
* `USE CALENDAR` enables calendar tooling so the agent can read and manage events
|
|
22787
|
+
* in one configured Google Calendar.
|
|
22788
|
+
*
|
|
22789
|
+
* Authentication is expected through runtime context provided by the host app UI.
|
|
22790
|
+
* Hosts can provide manual wallet tokens or host-managed OAuth tokens.
|
|
21733
22791
|
*
|
|
21734
22792
|
* @private [🪔] Maybe export the commitments through some package
|
|
21735
22793
|
*/
|
|
21736
|
-
class
|
|
22794
|
+
class UseCalendarCommitmentDefinition extends BaseCommitmentDefinition {
|
|
21737
22795
|
constructor() {
|
|
21738
|
-
super('USE
|
|
22796
|
+
super('USE CALENDAR', ['CALENDAR']);
|
|
21739
22797
|
}
|
|
21740
22798
|
/**
|
|
21741
|
-
* Short one-line description of USE
|
|
22799
|
+
* Short one-line description of USE CALENDAR.
|
|
21742
22800
|
*/
|
|
21743
22801
|
get description() {
|
|
21744
|
-
return '
|
|
22802
|
+
return 'Enable calendar tools for reading and managing events through Google Calendar.';
|
|
21745
22803
|
}
|
|
21746
22804
|
/**
|
|
21747
22805
|
* Icon for this commitment.
|
|
21748
22806
|
*/
|
|
21749
22807
|
get icon() {
|
|
21750
|
-
return '
|
|
22808
|
+
return '📅';
|
|
21751
22809
|
}
|
|
21752
22810
|
/**
|
|
21753
|
-
* Markdown documentation for USE
|
|
22811
|
+
* Markdown documentation for USE CALENDAR commitment.
|
|
21754
22812
|
*/
|
|
21755
22813
|
get documentation() {
|
|
21756
22814
|
return spaceTrim$1(`
|
|
21757
|
-
# USE
|
|
22815
|
+
# USE CALENDAR
|
|
21758
22816
|
|
|
21759
|
-
|
|
22817
|
+
Enables the agent to access and manage one Google Calendar.
|
|
21760
22818
|
|
|
21761
22819
|
## Key aspects
|
|
21762
22820
|
|
|
21763
|
-
- The
|
|
21764
|
-
-
|
|
21765
|
-
-
|
|
22821
|
+
- The first URL in the commitment should point to a Google Calendar URL.
|
|
22822
|
+
- Optional \`SCOPES\` lines can provide explicit OAuth scopes.
|
|
22823
|
+
- Optional extra instructions can follow calendar reference lines.
|
|
22824
|
+
- Runtime provides Google Calendar OAuth token (manual wallet token or host-managed OAuth token).
|
|
22825
|
+
- Tools support listing events, reading one event, creating events, updating events, deleting events, and inviting guests.
|
|
21766
22826
|
|
|
21767
|
-
##
|
|
22827
|
+
## Examples
|
|
21768
22828
|
|
|
21769
22829
|
\`\`\`book
|
|
21770
|
-
|
|
22830
|
+
Scheduling Assistant
|
|
21771
22831
|
|
|
21772
|
-
PERSONA You
|
|
21773
|
-
USE
|
|
22832
|
+
PERSONA You coordinate meetings and schedules.
|
|
22833
|
+
USE CALENDAR https://calendar.google.com/calendar/u/0/r
|
|
22834
|
+
\`\`\`
|
|
22835
|
+
|
|
22836
|
+
\`\`\`book
|
|
22837
|
+
Executive Assistant
|
|
22838
|
+
|
|
22839
|
+
USE CALENDAR https://calendar.google.com/calendar/u/0/r
|
|
22840
|
+
SCOPES https://www.googleapis.com/auth/calendar.readonly
|
|
22841
|
+
RULE Ask for confirmation before deleting events.
|
|
21774
22842
|
\`\`\`
|
|
21775
22843
|
`);
|
|
21776
22844
|
}
|
|
21777
22845
|
applyToAgentModelRequirements(requirements, content) {
|
|
21778
|
-
|
|
21779
|
-
|
|
21780
|
-
|
|
21781
|
-
|
|
21782
|
-
|
|
21783
|
-
|
|
21784
|
-
|
|
21785
|
-
|
|
21786
|
-
|
|
21787
|
-
|
|
22846
|
+
var _a;
|
|
22847
|
+
const parsedCommitment = parseUseCalendarCommitmentContent(content);
|
|
22848
|
+
const existingConfiguredCalendars = normalizeConfiguredCalendars((_a = requirements._metadata) === null || _a === void 0 ? void 0 : _a.useCalendars);
|
|
22849
|
+
if (parsedCommitment.calendar) {
|
|
22850
|
+
addConfiguredCalendarIfMissing(existingConfiguredCalendars, parsedCommitment.calendar);
|
|
22851
|
+
}
|
|
22852
|
+
const calendarsList = existingConfiguredCalendars.length > 0
|
|
22853
|
+
? existingConfiguredCalendars
|
|
22854
|
+
.map((calendar) => [
|
|
22855
|
+
`- ${calendar.provider}: ${calendar.url}`,
|
|
22856
|
+
calendar.scopes.length > 0 ? ` scopes: ${calendar.scopes.join(', ')}` : '',
|
|
22857
|
+
]
|
|
22858
|
+
.filter(Boolean)
|
|
22859
|
+
.join('\n'))
|
|
22860
|
+
.join('\n')
|
|
22861
|
+
: '- Calendar is resolved from runtime context';
|
|
22862
|
+
const extraInstructions = formatOptionalInstructionBlock('Calendar instructions', parsedCommitment.instructions);
|
|
22863
|
+
return this.appendToSystemMessage({
|
|
21788
22864
|
...requirements,
|
|
21789
|
-
|
|
21790
|
-
|
|
22865
|
+
tools: createUseCalendarTools(requirements.tools || []),
|
|
22866
|
+
_metadata: {
|
|
22867
|
+
...requirements._metadata,
|
|
22868
|
+
useCalendar: true,
|
|
22869
|
+
useCalendars: existingConfiguredCalendars,
|
|
22870
|
+
},
|
|
22871
|
+
}, spaceTrim$1((block) => `
|
|
22872
|
+
Calendar tools:
|
|
22873
|
+
- You can inspect and manage events in configured calendars.
|
|
22874
|
+
- Supported operations include read, create, update, delete, invite guests, and reminders.
|
|
22875
|
+
- Configured calendars:
|
|
22876
|
+
${block(calendarsList)}
|
|
22877
|
+
- USE CALENDAR credentials are read from wallet records (ACCESS_TOKEN, service "${UseCalendarWallet.service}", key "${UseCalendarWallet.key}").
|
|
22878
|
+
- If credentials are missing, ask user to connect calendar credentials in host UI and/or add them to wallet.
|
|
22879
|
+
${block(extraInstructions)}
|
|
22880
|
+
`));
|
|
21791
22881
|
}
|
|
22882
|
+
/**
|
|
22883
|
+
* Gets human-readable titles for tool functions provided by this commitment.
|
|
22884
|
+
*/
|
|
22885
|
+
getToolTitles() {
|
|
22886
|
+
return getUseCalendarToolTitles();
|
|
22887
|
+
}
|
|
22888
|
+
/**
|
|
22889
|
+
* Gets calendar tool function implementations.
|
|
22890
|
+
*/
|
|
22891
|
+
getToolFunctions() {
|
|
22892
|
+
return createUseCalendarToolFunctions();
|
|
22893
|
+
}
|
|
22894
|
+
}
|
|
22895
|
+
/**
|
|
22896
|
+
* Adds calendar into configured calendars list if it is not already present.
|
|
22897
|
+
*
|
|
22898
|
+
* @private function of UseCalendarCommitmentDefinition
|
|
22899
|
+
*/
|
|
22900
|
+
function addConfiguredCalendarIfMissing(configuredCalendars, calendarReference) {
|
|
22901
|
+
if (configuredCalendars.some((calendar) => calendar.provider === calendarReference.provider && calendar.url === calendarReference.url)) {
|
|
22902
|
+
return;
|
|
22903
|
+
}
|
|
22904
|
+
configuredCalendars.push({
|
|
22905
|
+
provider: calendarReference.provider,
|
|
22906
|
+
url: calendarReference.url,
|
|
22907
|
+
calendarId: calendarReference.calendarId,
|
|
22908
|
+
scopes: [...calendarReference.scopes],
|
|
22909
|
+
...(calendarReference.tokenRef ? { tokenRef: calendarReference.tokenRef } : {}),
|
|
22910
|
+
});
|
|
21792
22911
|
}
|
|
21793
22912
|
/**
|
|
21794
22913
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
21795
22914
|
*/
|
|
21796
22915
|
|
|
21797
22916
|
/**
|
|
21798
|
-
* USE
|
|
22917
|
+
* Lightweight email token matcher used for `USE EMAIL` first-line parsing.
|
|
21799
22918
|
*
|
|
21800
|
-
*
|
|
21801
|
-
|
|
22919
|
+
* @private internal USE EMAIL constant
|
|
22920
|
+
*/
|
|
22921
|
+
const EMAIL_TOKEN_PATTERN = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/;
|
|
22922
|
+
/**
|
|
22923
|
+
* Parses `USE EMAIL` commitment content into optional sender email + additional instructions.
|
|
21802
22924
|
*
|
|
21803
|
-
*
|
|
22925
|
+
* Examples:
|
|
22926
|
+
* - `agent@example.com`
|
|
22927
|
+
* - `agent@example.com Keep emails concise`
|
|
22928
|
+
* - `Keep emails concise`
|
|
21804
22929
|
*
|
|
21805
|
-
*
|
|
21806
|
-
|
|
21807
|
-
|
|
22930
|
+
* @private internal utility of USE EMAIL commitment
|
|
22931
|
+
*/
|
|
22932
|
+
function parseUseEmailCommitmentContent(content) {
|
|
22933
|
+
const trimmedContent = spaceTrim$1(content);
|
|
22934
|
+
if (!trimmedContent) {
|
|
22935
|
+
return {
|
|
22936
|
+
senderEmail: null,
|
|
22937
|
+
senderEmailRaw: null,
|
|
22938
|
+
instructions: '',
|
|
22939
|
+
};
|
|
22940
|
+
}
|
|
22941
|
+
const lines = trimmedContent
|
|
22942
|
+
.split(/\r?\n/)
|
|
22943
|
+
.map((line) => line.trim())
|
|
22944
|
+
.filter(Boolean);
|
|
22945
|
+
if (lines.length === 0) {
|
|
22946
|
+
return {
|
|
22947
|
+
senderEmail: null,
|
|
22948
|
+
senderEmailRaw: null,
|
|
22949
|
+
instructions: '',
|
|
22950
|
+
};
|
|
22951
|
+
}
|
|
22952
|
+
const firstLine = lines[0] || '';
|
|
22953
|
+
const senderMatch = firstLine.match(EMAIL_TOKEN_PATTERN);
|
|
22954
|
+
const senderEmailRaw = (senderMatch === null || senderMatch === void 0 ? void 0 : senderMatch[0]) || null;
|
|
22955
|
+
const senderEmail = senderEmailRaw && isValidEmail(senderEmailRaw) ? senderEmailRaw : null;
|
|
22956
|
+
let firstLineWithoutSender = firstLine;
|
|
22957
|
+
if (senderEmailRaw) {
|
|
22958
|
+
const matchIndex = firstLine.indexOf(senderEmailRaw);
|
|
22959
|
+
const prefix = firstLine.slice(0, matchIndex).trim();
|
|
22960
|
+
const suffix = firstLine.slice(matchIndex + senderEmailRaw.length).trim();
|
|
22961
|
+
firstLineWithoutSender = [prefix, suffix].filter(Boolean).join(' ').trim();
|
|
22962
|
+
}
|
|
22963
|
+
const instructionLines = [firstLineWithoutSender, ...lines.slice(1)].filter(Boolean);
|
|
22964
|
+
const instructions = instructionLines.join('\n').trim();
|
|
22965
|
+
return {
|
|
22966
|
+
senderEmail,
|
|
22967
|
+
senderEmailRaw,
|
|
22968
|
+
instructions,
|
|
22969
|
+
};
|
|
22970
|
+
}
|
|
22971
|
+
|
|
22972
|
+
/**
|
|
22973
|
+
* Client-side safe wrapper for sending emails.
|
|
22974
|
+
*
|
|
22975
|
+
* This function proxies requests to the Agents Server API endpoint for email queuing,
|
|
22976
|
+
* making it safe to use in browser environments.
|
|
22977
|
+
*
|
|
22978
|
+
* @param args Email payload forwarded to the server-side `send_email` tool
|
|
22979
|
+
* @param agentsServerUrl The base URL of the agents server (defaults to current origin)
|
|
22980
|
+
* @returns Result string from the server-side send_email tool
|
|
22981
|
+
*
|
|
22982
|
+
* @private internal utility for USE EMAIL commitment
|
|
22983
|
+
*/
|
|
22984
|
+
async function sendEmailViaBrowser(args, agentsServerUrl) {
|
|
22985
|
+
try {
|
|
22986
|
+
const baseUrl = agentsServerUrl || (typeof window !== 'undefined' ? window.location.origin : '');
|
|
22987
|
+
if (!baseUrl) {
|
|
22988
|
+
throw new Error('Agents server URL is required in non-browser environments');
|
|
22989
|
+
}
|
|
22990
|
+
const apiUrl = new URL('/api/send-email', baseUrl);
|
|
22991
|
+
const response = await fetch(apiUrl.toString(), {
|
|
22992
|
+
method: 'POST',
|
|
22993
|
+
headers: {
|
|
22994
|
+
'Content-Type': 'application/json',
|
|
22995
|
+
},
|
|
22996
|
+
body: JSON.stringify(args),
|
|
22997
|
+
});
|
|
22998
|
+
if (!response.ok) {
|
|
22999
|
+
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
23000
|
+
throw new Error(`Failed to send email: ${errorData.error || response.statusText}`);
|
|
23001
|
+
}
|
|
23002
|
+
const data = await response.json();
|
|
23003
|
+
if (!data.success) {
|
|
23004
|
+
throw new Error(`Email sending failed: ${data.error || 'Unknown error'}`);
|
|
23005
|
+
}
|
|
23006
|
+
return typeof data.result === 'string' ? data.result : JSON.stringify(data.result);
|
|
23007
|
+
}
|
|
23008
|
+
catch (error) {
|
|
23009
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
23010
|
+
throw new Error(`Error sending email via browser: ${errorMessage}`);
|
|
23011
|
+
}
|
|
23012
|
+
}
|
|
23013
|
+
|
|
23014
|
+
/**
|
|
23015
|
+
* Tool name used by USE EMAIL.
|
|
23016
|
+
*
|
|
23017
|
+
* @private internal USE EMAIL constant
|
|
23018
|
+
*/
|
|
23019
|
+
const SEND_EMAIL_TOOL_NAME = 'send_email';
|
|
23020
|
+
/**
|
|
23021
|
+
* Wallet service used for SMTP credentials required by USE EMAIL.
|
|
23022
|
+
*
|
|
23023
|
+
* @private internal USE EMAIL constant
|
|
23024
|
+
*/
|
|
23025
|
+
const USE_EMAIL_SMTP_WALLET_SERVICE = 'smtp';
|
|
23026
|
+
/**
|
|
23027
|
+
* Wallet key used for SMTP credentials required by USE EMAIL.
|
|
23028
|
+
*
|
|
23029
|
+
* @private internal USE EMAIL constant
|
|
23030
|
+
*/
|
|
23031
|
+
const USE_EMAIL_SMTP_WALLET_KEY = 'use-email-smtp-credentials';
|
|
23032
|
+
/**
|
|
23033
|
+
* USE EMAIL commitment definition.
|
|
23034
|
+
*
|
|
23035
|
+
* The `USE EMAIL` commitment enables outbound email sending through the `send_email` tool.
|
|
21808
23036
|
*
|
|
21809
23037
|
* @private [🪔] Maybe export the commitments through some package
|
|
21810
23038
|
*/
|
|
21811
|
-
class
|
|
23039
|
+
class UseEmailCommitmentDefinition extends BaseCommitmentDefinition {
|
|
21812
23040
|
constructor() {
|
|
21813
|
-
super('USE
|
|
23041
|
+
super('USE EMAIL', ['EMAIL', 'MAIL']);
|
|
21814
23042
|
}
|
|
21815
|
-
/**
|
|
21816
|
-
* The `USE POPUP` commitment is standalone or with instructions.
|
|
21817
|
-
*/
|
|
21818
23043
|
get requiresContent() {
|
|
21819
23044
|
return false;
|
|
21820
23045
|
}
|
|
21821
23046
|
/**
|
|
21822
|
-
* Short one-line description of USE
|
|
23047
|
+
* Short one-line description of USE EMAIL.
|
|
21823
23048
|
*/
|
|
21824
23049
|
get description() {
|
|
21825
|
-
return 'Enable
|
|
23050
|
+
return 'Enable outbound email sending through a wallet-backed SMTP configuration.';
|
|
21826
23051
|
}
|
|
21827
23052
|
/**
|
|
21828
23053
|
* Icon for this commitment.
|
|
21829
23054
|
*/
|
|
21830
23055
|
get icon() {
|
|
21831
|
-
return '
|
|
23056
|
+
return '📧';
|
|
21832
23057
|
}
|
|
21833
23058
|
/**
|
|
21834
|
-
* Markdown documentation for USE
|
|
23059
|
+
* Markdown documentation for USE EMAIL commitment.
|
|
21835
23060
|
*/
|
|
21836
23061
|
get documentation() {
|
|
21837
23062
|
return spaceTrim$1(`
|
|
21838
|
-
# USE
|
|
23063
|
+
# USE EMAIL
|
|
21839
23064
|
|
|
21840
|
-
Enables the agent to
|
|
23065
|
+
Enables the agent to send outbound emails through SMTP.
|
|
21841
23066
|
|
|
21842
23067
|
## Key aspects
|
|
21843
23068
|
|
|
21844
|
-
- The
|
|
21845
|
-
-
|
|
21846
|
-
-
|
|
23069
|
+
- The agent sends email via the \`send_email\` tool.
|
|
23070
|
+
- SMTP credentials are expected from wallet records (\`ACCESS_TOKEN\`, service \`smtp\`, key \`use-email-smtp-credentials\`).
|
|
23071
|
+
- Commitment content can optionally begin with a default sender email address:
|
|
23072
|
+
- \`USE EMAIL agent@example.com\`
|
|
23073
|
+
- Remaining commitment content is treated as optional email-writing instructions.
|
|
21847
23074
|
|
|
21848
23075
|
## Examples
|
|
21849
23076
|
|
|
21850
23077
|
\`\`\`book
|
|
21851
|
-
|
|
23078
|
+
Writing Agent
|
|
23079
|
+
USE EMAIL agent@example.com
|
|
23080
|
+
RULE Write emails to customers according to the instructions from user.
|
|
23081
|
+
\`\`\`
|
|
21852
23082
|
|
|
21853
|
-
|
|
21854
|
-
|
|
23083
|
+
\`\`\`book
|
|
23084
|
+
Formal Email Assistant
|
|
23085
|
+
USE EMAIL agent@example.com Keep emails concise and formal.
|
|
21855
23086
|
\`\`\`
|
|
21856
23087
|
`);
|
|
21857
23088
|
}
|
|
21858
23089
|
applyToAgentModelRequirements(requirements, content) {
|
|
21859
|
-
const
|
|
21860
|
-
|
|
21861
|
-
const
|
|
21862
|
-
|
|
21863
|
-
|
|
21864
|
-
|
|
21865
|
-
: [
|
|
21866
|
-
...existingTools,
|
|
21867
|
-
{
|
|
21868
|
-
name: 'open_popup',
|
|
21869
|
-
description: spaceTrim$1(`
|
|
21870
|
-
Opens a popup window with a specific URL.
|
|
21871
|
-
Use this when you want to show a specific website to the user in a new window.
|
|
21872
|
-
${!content ? '' : `Constraints / instructions: ${content}`}
|
|
21873
|
-
`),
|
|
21874
|
-
parameters: {
|
|
21875
|
-
type: 'object',
|
|
21876
|
-
properties: {
|
|
21877
|
-
url: {
|
|
21878
|
-
type: 'string',
|
|
21879
|
-
description: 'The URL to open in the popup window',
|
|
21880
|
-
},
|
|
21881
|
-
},
|
|
21882
|
-
required: ['url'],
|
|
21883
|
-
},
|
|
21884
|
-
},
|
|
21885
|
-
];
|
|
21886
|
-
// Return requirements with updated tools and metadata
|
|
23090
|
+
const parsedCommitment = parseUseEmailCommitmentContent(content);
|
|
23091
|
+
const extraInstructions = formatOptionalInstructionBlock('Email instructions', parsedCommitment.instructions);
|
|
23092
|
+
const senderInstruction = parsedCommitment.senderEmail
|
|
23093
|
+
? `- Default sender address from commitment: "${parsedCommitment.senderEmail}".`
|
|
23094
|
+
: '';
|
|
23095
|
+
const updatedTools = addUseEmailTools(requirements.tools || []);
|
|
21887
23096
|
return this.appendToSystemMessage({
|
|
21888
23097
|
...requirements,
|
|
21889
23098
|
tools: updatedTools,
|
|
21890
23099
|
_metadata: {
|
|
21891
23100
|
...requirements._metadata,
|
|
21892
|
-
|
|
23101
|
+
useEmail: true,
|
|
23102
|
+
...(parsedCommitment.senderEmail ? { useEmailSender: parsedCommitment.senderEmail } : {}),
|
|
21893
23103
|
},
|
|
21894
23104
|
}, spaceTrim$1((block) => `
|
|
21895
|
-
|
|
21896
|
-
-
|
|
21897
|
-
-
|
|
23105
|
+
Email tool:
|
|
23106
|
+
- Use "${SEND_EMAIL_TOOL_NAME}" to send outbound emails.
|
|
23107
|
+
- Prefer \`message\` argument compatible with Promptbook \`Message\` type.
|
|
23108
|
+
- Include subject in \`message.metadata.subject\` (or use legacy \`subject\` argument).
|
|
23109
|
+
- USE EMAIL credentials are read from wallet records (ACCESS_TOKEN, service "${USE_EMAIL_SMTP_WALLET_SERVICE}", key "${USE_EMAIL_SMTP_WALLET_KEY}").
|
|
23110
|
+
- Wallet secret must contain SMTP credentials in JSON format with fields \`host\`, \`port\`, \`secure\`, \`username\`, \`password\`.
|
|
23111
|
+
- If credentials are missing, ask user to add wallet credentials.
|
|
23112
|
+
${block(senderInstruction)}
|
|
21898
23113
|
${block(extraInstructions)}
|
|
21899
|
-
|
|
23114
|
+
`));
|
|
21900
23115
|
}
|
|
21901
23116
|
/**
|
|
21902
23117
|
* Gets human-readable titles for tool functions provided by this commitment.
|
|
21903
23118
|
*/
|
|
21904
23119
|
getToolTitles() {
|
|
21905
23120
|
return {
|
|
21906
|
-
|
|
23121
|
+
[SEND_EMAIL_TOOL_NAME]: 'Send email',
|
|
21907
23122
|
};
|
|
21908
23123
|
}
|
|
21909
23124
|
/**
|
|
21910
|
-
* Gets the `
|
|
23125
|
+
* Gets the browser-safe `send_email` implementation.
|
|
23126
|
+
*
|
|
23127
|
+
* Node.js runtime overrides this via `getAllCommitmentsToolFunctionsForNode`.
|
|
21911
23128
|
*/
|
|
21912
23129
|
getToolFunctions() {
|
|
21913
23130
|
return {
|
|
21914
|
-
async
|
|
21915
|
-
|
|
21916
|
-
const { url } = args;
|
|
21917
|
-
if (typeof window !== 'undefined') {
|
|
21918
|
-
window.open(url, '_blank');
|
|
21919
|
-
return `Popup window with URL "${url}" was opened.`;
|
|
21920
|
-
}
|
|
21921
|
-
return spaceTrim$1(`
|
|
21922
|
-
Popup window with URL "${url}" was requested.
|
|
21923
|
-
|
|
21924
|
-
Note: The agent is currently running on the server, so the popup cannot be opened automatically.
|
|
21925
|
-
The user can open it manually from the tool call details in the chat.
|
|
21926
|
-
`);
|
|
23131
|
+
async [SEND_EMAIL_TOOL_NAME](args) {
|
|
23132
|
+
return sendEmailViaBrowser(args);
|
|
21927
23133
|
},
|
|
21928
23134
|
};
|
|
21929
23135
|
}
|
|
21930
23136
|
}
|
|
21931
23137
|
/**
|
|
21932
|
-
*
|
|
21933
|
-
*/
|
|
21934
|
-
|
|
21935
|
-
/**
|
|
21936
|
-
* Tool name used by the USE PRIVACY commitment.
|
|
23138
|
+
* Adds USE EMAIL tool definition while keeping already registered tools untouched.
|
|
21937
23139
|
*
|
|
21938
|
-
* @private
|
|
23140
|
+
* @private utility of USE EMAIL commitment
|
|
21939
23141
|
*/
|
|
21940
|
-
|
|
21941
|
-
|
|
21942
|
-
|
|
21943
|
-
|
|
21944
|
-
|
|
21945
|
-
|
|
21946
|
-
|
|
21947
|
-
|
|
21948
|
-
|
|
21949
|
-
|
|
21950
|
-
|
|
21951
|
-
|
|
21952
|
-
|
|
21953
|
-
|
|
21954
|
-
|
|
21955
|
-
|
|
21956
|
-
|
|
21957
|
-
|
|
21958
|
-
|
|
21959
|
-
|
|
21960
|
-
|
|
21961
|
-
|
|
23142
|
+
function addUseEmailTools(existingTools) {
|
|
23143
|
+
if (existingTools.some((tool) => tool.name === SEND_EMAIL_TOOL_NAME)) {
|
|
23144
|
+
return [...existingTools];
|
|
23145
|
+
}
|
|
23146
|
+
return [
|
|
23147
|
+
...existingTools,
|
|
23148
|
+
{
|
|
23149
|
+
name: SEND_EMAIL_TOOL_NAME,
|
|
23150
|
+
description: 'Send an outbound email through configured SMTP credentials. Prefer providing Message-like payload in `message`.',
|
|
23151
|
+
parameters: {
|
|
23152
|
+
type: 'object',
|
|
23153
|
+
properties: {
|
|
23154
|
+
message: {
|
|
23155
|
+
type: 'object',
|
|
23156
|
+
description: 'Preferred input payload compatible with Promptbook Message type. Use metadata.subject for subject line.',
|
|
23157
|
+
},
|
|
23158
|
+
to: {
|
|
23159
|
+
type: 'string',
|
|
23160
|
+
description: 'Legacy alias for recipients (use comma-separated emails or JSON array encoded as string).',
|
|
23161
|
+
},
|
|
23162
|
+
cc: {
|
|
23163
|
+
type: 'string',
|
|
23164
|
+
description: 'Optional CC recipients (use comma-separated emails or JSON array encoded as string).',
|
|
23165
|
+
},
|
|
23166
|
+
subject: {
|
|
23167
|
+
type: 'string',
|
|
23168
|
+
description: 'Legacy alias for subject.',
|
|
23169
|
+
},
|
|
23170
|
+
body: {
|
|
23171
|
+
type: 'string',
|
|
23172
|
+
description: 'Legacy alias for markdown body content.',
|
|
23173
|
+
},
|
|
23174
|
+
},
|
|
23175
|
+
required: [],
|
|
23176
|
+
},
|
|
23177
|
+
},
|
|
23178
|
+
];
|
|
21962
23179
|
}
|
|
21963
23180
|
/**
|
|
21964
|
-
*
|
|
21965
|
-
*
|
|
21966
|
-
* @private utility of USE PRIVACY commitment
|
|
23181
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
21967
23182
|
*/
|
|
21968
|
-
|
|
21969
|
-
return {
|
|
21970
|
-
status: PRIVACY_CONFIRMATION_REQUIRED_STATUS,
|
|
21971
|
-
message: 'Private mode requires explicit user confirmation in the UI. Ask the user to confirm the privacy prompt.',
|
|
21972
|
-
};
|
|
21973
|
-
}
|
|
23183
|
+
|
|
21974
23184
|
/**
|
|
21975
|
-
*
|
|
23185
|
+
* USE IMAGE GENERATOR commitment definition
|
|
21976
23186
|
*
|
|
21977
|
-
*
|
|
21978
|
-
|
|
21979
|
-
function createPrivacyAlreadyEnabledResult() {
|
|
21980
|
-
return {
|
|
21981
|
-
status: PRIVACY_ALREADY_ENABLED_STATUS,
|
|
21982
|
-
message: 'Private mode is already enabled for this chat session.',
|
|
21983
|
-
};
|
|
21984
|
-
}
|
|
21985
|
-
/**
|
|
21986
|
-
* USE PRIVACY commitment definition.
|
|
23187
|
+
* The `USE IMAGE GENERATOR` commitment indicates that the agent can output
|
|
23188
|
+
* markdown placeholders for UI-driven image generation.
|
|
21987
23189
|
*
|
|
21988
|
-
*
|
|
23190
|
+
* Example usage in agent source:
|
|
23191
|
+
*
|
|
23192
|
+
* ```book
|
|
23193
|
+
* USE IMAGE GENERATOR
|
|
23194
|
+
* USE IMAGE GENERATOR Create realistic images of nature
|
|
23195
|
+
* ```
|
|
21989
23196
|
*
|
|
21990
23197
|
* @private [🪔] Maybe export the commitments through some package
|
|
21991
23198
|
*/
|
|
21992
|
-
class
|
|
21993
|
-
constructor() {
|
|
21994
|
-
super(
|
|
23199
|
+
class UseImageGeneratorCommitmentDefinition extends BaseCommitmentDefinition {
|
|
23200
|
+
constructor(type = 'USE IMAGE GENERATOR') {
|
|
23201
|
+
super(type);
|
|
21995
23202
|
}
|
|
21996
23203
|
get requiresContent() {
|
|
21997
23204
|
return false;
|
|
21998
23205
|
}
|
|
21999
23206
|
/**
|
|
22000
|
-
* Short one-line description of USE
|
|
23207
|
+
* Short one-line description of USE IMAGE GENERATOR.
|
|
22001
23208
|
*/
|
|
22002
23209
|
get description() {
|
|
22003
|
-
return 'Enable the agent to
|
|
23210
|
+
return 'Enable the agent to output markdown image placeholders that the UI turns into generated images.';
|
|
22004
23211
|
}
|
|
22005
23212
|
/**
|
|
22006
23213
|
* Icon for this commitment.
|
|
22007
23214
|
*/
|
|
22008
23215
|
get icon() {
|
|
22009
|
-
return '
|
|
23216
|
+
return '🖼️';
|
|
22010
23217
|
}
|
|
22011
23218
|
/**
|
|
22012
|
-
* Markdown documentation for USE
|
|
23219
|
+
* Markdown documentation for USE IMAGE GENERATOR commitment.
|
|
22013
23220
|
*/
|
|
22014
23221
|
get documentation() {
|
|
22015
23222
|
return spaceTrim$1(`
|
|
22016
|
-
# USE
|
|
23223
|
+
# USE IMAGE GENERATOR
|
|
22017
23224
|
|
|
22018
|
-
Enables the agent to
|
|
23225
|
+
Enables the agent to output markdown image placeholders that trigger image generation in the user interface.
|
|
22019
23226
|
|
|
22020
23227
|
## Key aspects
|
|
22021
23228
|
|
|
22022
|
-
- The
|
|
22023
|
-
-
|
|
22024
|
-
-
|
|
22025
|
-
-
|
|
22026
|
-
- Proper encryption is planned for future updates, but not implemented by this commitment yet.
|
|
22027
|
-
- Optional content after \`USE PRIVACY\` can provide additional privacy instructions.
|
|
23229
|
+
- The content following \`USE IMAGE GENERATOR\` is an arbitrary text that the agent should know (e.g. style instructions or safety guidelines).
|
|
23230
|
+
- The agent does **not** call an image-generation tool directly.
|
|
23231
|
+
- The agent inserts markdown notation: \`\`.
|
|
23232
|
+
- The user interface detects the notation and generates the image asynchronously.
|
|
22028
23233
|
|
|
22029
23234
|
## Examples
|
|
22030
23235
|
|
|
22031
23236
|
\`\`\`book
|
|
22032
|
-
|
|
23237
|
+
Visual Artist
|
|
22033
23238
|
|
|
22034
|
-
PERSONA You
|
|
22035
|
-
USE
|
|
23239
|
+
PERSONA You are a creative visual artist.
|
|
23240
|
+
USE IMAGE GENERATOR
|
|
23241
|
+
RULE Always describe the generated image to the user.
|
|
22036
23242
|
\`\`\`
|
|
22037
23243
|
|
|
22038
23244
|
\`\`\`book
|
|
22039
|
-
|
|
23245
|
+
Interior Designer
|
|
22040
23246
|
|
|
22041
|
-
PERSONA You
|
|
22042
|
-
USE
|
|
23247
|
+
PERSONA You are an interior designer who helps users visualize their space.
|
|
23248
|
+
USE IMAGE GENERATOR Professional interior design renders.
|
|
23249
|
+
ACTION Add one generated image placeholder whenever a user asks for a visual.
|
|
22043
23250
|
\`\`\`
|
|
22044
23251
|
`);
|
|
22045
23252
|
}
|
|
22046
23253
|
applyToAgentModelRequirements(requirements, content) {
|
|
22047
|
-
const extraInstructions = formatOptionalInstructionBlock('
|
|
22048
|
-
const existingTools = requirements.tools || [];
|
|
22049
|
-
const tools = existingTools.some((tool) => tool.name === TURN_PRIVACY_ON_TOOL_NAME)
|
|
22050
|
-
? existingTools
|
|
22051
|
-
: [
|
|
22052
|
-
...existingTools,
|
|
22053
|
-
{
|
|
22054
|
-
name: TURN_PRIVACY_ON_TOOL_NAME,
|
|
22055
|
-
description: spaceTrim$1(`
|
|
22056
|
-
Requests turning private mode on in the chat UI.
|
|
22057
|
-
The user must explicitly confirm the action in a dialog before private mode is enabled.
|
|
22058
|
-
Use this for sensitive topics or when the user asks not to store conversation data.
|
|
22059
|
-
`),
|
|
22060
|
-
parameters: {
|
|
22061
|
-
type: 'object',
|
|
22062
|
-
properties: {},
|
|
22063
|
-
required: [],
|
|
22064
|
-
},
|
|
22065
|
-
},
|
|
22066
|
-
];
|
|
23254
|
+
const extraInstructions = formatOptionalInstructionBlock('Image instructions', content);
|
|
22067
23255
|
return this.appendToSystemMessage({
|
|
22068
23256
|
...requirements,
|
|
22069
|
-
tools,
|
|
22070
23257
|
_metadata: {
|
|
22071
23258
|
...requirements._metadata,
|
|
22072
|
-
|
|
23259
|
+
useImageGenerator: content || true,
|
|
22073
23260
|
},
|
|
22074
23261
|
}, spaceTrim$1((block) => `
|
|
22075
|
-
|
|
22076
|
-
-
|
|
22077
|
-
-
|
|
22078
|
-
|
|
22079
|
-
-
|
|
23262
|
+
Image generation:
|
|
23263
|
+
- You do not generate images directly and you do not call any image tool.
|
|
23264
|
+
- When the user asks for an image, include markdown notation in your message:
|
|
23265
|
+
\`\`
|
|
23266
|
+
- Keep \`<alt text>\` short and descriptive.
|
|
23267
|
+
- Keep \`<prompt>\` detailed so the generated image matches the request.
|
|
23268
|
+
- You can include normal explanatory text before and after the notation.
|
|
22080
23269
|
${block(extraInstructions)}
|
|
22081
23270
|
`));
|
|
22082
23271
|
}
|
|
23272
|
+
}
|
|
23273
|
+
/**
|
|
23274
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
23275
|
+
*/
|
|
23276
|
+
|
|
23277
|
+
/**
|
|
23278
|
+
* USE MCP commitment definition
|
|
23279
|
+
*
|
|
23280
|
+
* The `USE MCP` commitment allows to specify an MCP server URL which the agent will connect to
|
|
23281
|
+
* for retrieving additional instructions and actions.
|
|
23282
|
+
*
|
|
23283
|
+
* The content following `USE MCP` is the URL of the MCP server.
|
|
23284
|
+
*
|
|
23285
|
+
* Example usage in agent source:
|
|
23286
|
+
*
|
|
23287
|
+
* ```book
|
|
23288
|
+
* USE MCP http://mcp-server-url.com
|
|
23289
|
+
* ```
|
|
23290
|
+
*
|
|
23291
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
23292
|
+
*/
|
|
23293
|
+
class UseMcpCommitmentDefinition extends BaseCommitmentDefinition {
|
|
23294
|
+
constructor() {
|
|
23295
|
+
super('USE MCP', ['MCP']);
|
|
23296
|
+
}
|
|
22083
23297
|
/**
|
|
22084
|
-
*
|
|
23298
|
+
* Short one-line description of USE MCP.
|
|
22085
23299
|
*/
|
|
22086
|
-
|
|
22087
|
-
return
|
|
22088
|
-
[TURN_PRIVACY_ON_TOOL_NAME]: 'Turn privacy mode on',
|
|
22089
|
-
};
|
|
23300
|
+
get description() {
|
|
23301
|
+
return 'Connects the agent to an external MCP server for additional capabilities.';
|
|
22090
23302
|
}
|
|
22091
23303
|
/**
|
|
22092
|
-
*
|
|
23304
|
+
* Icon for this commitment.
|
|
22093
23305
|
*/
|
|
22094
|
-
|
|
23306
|
+
get icon() {
|
|
23307
|
+
return '🔌';
|
|
23308
|
+
}
|
|
23309
|
+
/**
|
|
23310
|
+
* Markdown documentation for USE MCP commitment.
|
|
23311
|
+
*/
|
|
23312
|
+
get documentation() {
|
|
23313
|
+
return spaceTrim$1(`
|
|
23314
|
+
# USE MCP
|
|
23315
|
+
|
|
23316
|
+
Connects the agent to an external Model Context Protocol (MCP) server.
|
|
23317
|
+
|
|
23318
|
+
## Key aspects
|
|
23319
|
+
|
|
23320
|
+
- The content following \`USE MCP\` must be a valid URL
|
|
23321
|
+
- Multiple MCP servers can be connected by using multiple \`USE MCP\` commitments
|
|
23322
|
+
- The agent will have access to tools and resources provided by the MCP server
|
|
23323
|
+
|
|
23324
|
+
## Example
|
|
23325
|
+
|
|
23326
|
+
\`\`\`book
|
|
23327
|
+
Company Lawyer
|
|
23328
|
+
|
|
23329
|
+
PERSONA You are a company lawyer.
|
|
23330
|
+
USE MCP http://legal-db.example.com
|
|
23331
|
+
\`\`\`
|
|
23332
|
+
`);
|
|
23333
|
+
}
|
|
23334
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
23335
|
+
const mcpServerUrl = content.trim();
|
|
23336
|
+
if (!mcpServerUrl) {
|
|
23337
|
+
return requirements;
|
|
23338
|
+
}
|
|
23339
|
+
const existingMcpServers = requirements.mcpServers || [];
|
|
23340
|
+
// Avoid duplicates
|
|
23341
|
+
if (existingMcpServers.includes(mcpServerUrl)) {
|
|
23342
|
+
return requirements;
|
|
23343
|
+
}
|
|
22095
23344
|
return {
|
|
22096
|
-
|
|
22097
|
-
|
|
22098
|
-
? createPrivacyAlreadyEnabledResult()
|
|
22099
|
-
: createPrivacyConfirmationRequiredResult();
|
|
22100
|
-
return JSON.stringify(result);
|
|
22101
|
-
},
|
|
23345
|
+
...requirements,
|
|
23346
|
+
mcpServers: [...existingMcpServers, mcpServerUrl],
|
|
22102
23347
|
};
|
|
22103
23348
|
}
|
|
22104
23349
|
}
|
|
@@ -22106,135 +23351,447 @@ class UsePrivacyCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
22106
23351
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
22107
23352
|
*/
|
|
22108
23353
|
|
|
22109
|
-
/* eslint-disable no-magic-numbers */
|
|
22110
23354
|
/**
|
|
22111
|
-
*
|
|
23355
|
+
* USE POPUP commitment definition
|
|
22112
23356
|
*
|
|
22113
|
-
*
|
|
22114
|
-
|
|
22115
|
-
const DEFAULT_ATTACHMENT_TEXT_DECODE_BYTES = 512 * 1024;
|
|
22116
|
-
/**
|
|
22117
|
-
* Marker appended when only a prefix of the payload is decoded.
|
|
23357
|
+
* The `USE POPUP` commitment indicates that the agent can open a popup window with a specific website.
|
|
23358
|
+
* This is useful, for example, when the agent writes a post on Facebook but wants the user to post it on Facebook.
|
|
22118
23359
|
*
|
|
22119
|
-
*
|
|
22120
|
-
*/
|
|
22121
|
-
const TRUNCATED_MARKER = '…[TRUNCATED]…';
|
|
22122
|
-
/**
|
|
22123
|
-
* MIME types that are trustworthy indicators of textual content.
|
|
23360
|
+
* Example usage in agent source:
|
|
22124
23361
|
*
|
|
22125
|
-
*
|
|
23362
|
+
* ```book
|
|
23363
|
+
* USE POPUP Allow to open Facebook and Linkedin
|
|
23364
|
+
* ```
|
|
23365
|
+
*
|
|
23366
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
22126
23367
|
*/
|
|
22127
|
-
|
|
22128
|
-
|
|
22129
|
-
|
|
22130
|
-
|
|
22131
|
-
|
|
22132
|
-
|
|
22133
|
-
|
|
22134
|
-
|
|
22135
|
-
|
|
22136
|
-
|
|
22137
|
-
|
|
22138
|
-
|
|
22139
|
-
|
|
22140
|
-
|
|
22141
|
-
|
|
23368
|
+
class UsePopupCommitmentDefinition extends BaseCommitmentDefinition {
|
|
23369
|
+
constructor() {
|
|
23370
|
+
super('USE POPUP', ['POPUP']);
|
|
23371
|
+
}
|
|
23372
|
+
/**
|
|
23373
|
+
* The `USE POPUP` commitment is standalone or with instructions.
|
|
23374
|
+
*/
|
|
23375
|
+
get requiresContent() {
|
|
23376
|
+
return false;
|
|
23377
|
+
}
|
|
23378
|
+
/**
|
|
23379
|
+
* Short one-line description of USE POPUP.
|
|
23380
|
+
*/
|
|
23381
|
+
get description() {
|
|
23382
|
+
return 'Enable the agent to open a popup window with a specific website.';
|
|
23383
|
+
}
|
|
23384
|
+
/**
|
|
23385
|
+
* Icon for this commitment.
|
|
23386
|
+
*/
|
|
23387
|
+
get icon() {
|
|
23388
|
+
return '🪟';
|
|
23389
|
+
}
|
|
23390
|
+
/**
|
|
23391
|
+
* Markdown documentation for USE POPUP commitment.
|
|
23392
|
+
*/
|
|
23393
|
+
get documentation() {
|
|
23394
|
+
return spaceTrim$1(`
|
|
23395
|
+
# USE POPUP
|
|
23396
|
+
|
|
23397
|
+
Enables the agent to open a popup window with a specific website.
|
|
23398
|
+
|
|
23399
|
+
## Key aspects
|
|
23400
|
+
|
|
23401
|
+
- The content following \`USE POPUP\` is an arbitrary text that the agent should know (e.g. constraints or instructions).
|
|
23402
|
+
- The actual popup opening is handled by the agent runtime (usually in the browser)
|
|
23403
|
+
- Allows the agent to open websites for the user to interact with (e.g. social media posts)
|
|
23404
|
+
|
|
23405
|
+
## Examples
|
|
23406
|
+
|
|
23407
|
+
\`\`\`book
|
|
23408
|
+
John the Copywriter
|
|
23409
|
+
|
|
23410
|
+
PERSONA You are a professional copywriter writing about CNC machines.
|
|
23411
|
+
USE POPUP Allow to open Facebook and Linkedin
|
|
23412
|
+
\`\`\`
|
|
23413
|
+
`);
|
|
23414
|
+
}
|
|
23415
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
23416
|
+
const extraInstructions = formatOptionalInstructionBlock('Popup instructions', content);
|
|
23417
|
+
// Get existing tools array or create new one
|
|
23418
|
+
const existingTools = requirements.tools || [];
|
|
23419
|
+
// Add 'open_popup' to tools if not already present
|
|
23420
|
+
const updatedTools = existingTools.some((tool) => tool.name === 'open_popup')
|
|
23421
|
+
? existingTools
|
|
23422
|
+
: [
|
|
23423
|
+
...existingTools,
|
|
23424
|
+
{
|
|
23425
|
+
name: 'open_popup',
|
|
23426
|
+
description: spaceTrim$1(`
|
|
23427
|
+
Opens a popup window with a specific URL.
|
|
23428
|
+
Use this when you want to show a specific website to the user in a new window.
|
|
23429
|
+
${!content ? '' : `Constraints / instructions: ${content}`}
|
|
23430
|
+
`),
|
|
23431
|
+
parameters: {
|
|
23432
|
+
type: 'object',
|
|
23433
|
+
properties: {
|
|
23434
|
+
url: {
|
|
23435
|
+
type: 'string',
|
|
23436
|
+
description: 'The URL to open in the popup window',
|
|
23437
|
+
},
|
|
23438
|
+
},
|
|
23439
|
+
required: ['url'],
|
|
23440
|
+
},
|
|
23441
|
+
},
|
|
23442
|
+
];
|
|
23443
|
+
// Return requirements with updated tools and metadata
|
|
23444
|
+
return this.appendToSystemMessage({
|
|
23445
|
+
...requirements,
|
|
23446
|
+
tools: updatedTools,
|
|
23447
|
+
_metadata: {
|
|
23448
|
+
...requirements._metadata,
|
|
23449
|
+
usePopup: content || true,
|
|
23450
|
+
},
|
|
23451
|
+
}, spaceTrim$1((block) => `
|
|
23452
|
+
Tool:
|
|
23453
|
+
- You can open a popup window with a specific URL using the tool "open_popup".
|
|
23454
|
+
- Use this when you want the user to see or interact with a specific website.
|
|
23455
|
+
${block(extraInstructions)}
|
|
23456
|
+
`));
|
|
23457
|
+
}
|
|
23458
|
+
/**
|
|
23459
|
+
* Gets human-readable titles for tool functions provided by this commitment.
|
|
23460
|
+
*/
|
|
23461
|
+
getToolTitles() {
|
|
23462
|
+
return {
|
|
23463
|
+
open_popup: 'Open popup',
|
|
23464
|
+
};
|
|
23465
|
+
}
|
|
23466
|
+
/**
|
|
23467
|
+
* Gets the `open_popup` tool function implementation.
|
|
23468
|
+
*/
|
|
23469
|
+
getToolFunctions() {
|
|
23470
|
+
return {
|
|
23471
|
+
async open_popup(args) {
|
|
23472
|
+
console.log('!!!! [Tool] open_popup called', { args });
|
|
23473
|
+
const { url } = args;
|
|
23474
|
+
if (typeof window !== 'undefined') {
|
|
23475
|
+
window.open(url, '_blank');
|
|
23476
|
+
return `Popup window with URL "${url}" was opened.`;
|
|
23477
|
+
}
|
|
23478
|
+
return spaceTrim$1(`
|
|
23479
|
+
Popup window with URL "${url}" was requested.
|
|
23480
|
+
|
|
23481
|
+
Note: The agent is currently running on the server, so the popup cannot be opened automatically.
|
|
23482
|
+
The user can open it manually from the tool call details in the chat.
|
|
23483
|
+
`);
|
|
23484
|
+
},
|
|
23485
|
+
};
|
|
23486
|
+
}
|
|
23487
|
+
}
|
|
22142
23488
|
/**
|
|
22143
|
-
*
|
|
22144
|
-
*
|
|
22145
|
-
* @private constant of decodeAttachmentAsText
|
|
23489
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
22146
23490
|
*/
|
|
22147
|
-
|
|
22148
|
-
'application/pdf',
|
|
22149
|
-
'application/zip',
|
|
22150
|
-
'application/gzip',
|
|
22151
|
-
'application/x-gzip',
|
|
22152
|
-
'application/x-7z-compressed',
|
|
22153
|
-
'application/vnd.rar',
|
|
22154
|
-
'application/x-rar-compressed',
|
|
22155
|
-
]);
|
|
23491
|
+
|
|
22156
23492
|
/**
|
|
22157
|
-
*
|
|
23493
|
+
* Tool name used by the USE PRIVACY commitment.
|
|
22158
23494
|
*
|
|
22159
|
-
* @private
|
|
23495
|
+
* @private internal USE PRIVACY constant
|
|
22160
23496
|
*/
|
|
22161
|
-
const
|
|
23497
|
+
const TURN_PRIVACY_ON_TOOL_NAME = 'turn_privacy_on';
|
|
22162
23498
|
/**
|
|
22163
|
-
*
|
|
23499
|
+
* Status returned when the UI should ask the user to confirm private mode.
|
|
22164
23500
|
*
|
|
22165
|
-
* @private
|
|
23501
|
+
* @private internal USE PRIVACY constant
|
|
22166
23502
|
*/
|
|
22167
|
-
const
|
|
22168
|
-
'ˇ',
|
|
22169
|
-
'˘',
|
|
22170
|
-
'˙',
|
|
22171
|
-
'˛',
|
|
22172
|
-
'˝',
|
|
22173
|
-
'¸',
|
|
22174
|
-
'¤',
|
|
22175
|
-
'¦',
|
|
22176
|
-
'¨',
|
|
22177
|
-
'¯',
|
|
22178
|
-
'²',
|
|
22179
|
-
'³',
|
|
22180
|
-
'¹',
|
|
22181
|
-
]);
|
|
23503
|
+
const PRIVACY_CONFIRMATION_REQUIRED_STATUS = 'confirmation-required';
|
|
22182
23504
|
/**
|
|
22183
|
-
*
|
|
23505
|
+
* Status returned when private mode is already enabled.
|
|
22184
23506
|
*
|
|
22185
|
-
* @private
|
|
23507
|
+
* @private internal USE PRIVACY constant
|
|
22186
23508
|
*/
|
|
22187
|
-
const
|
|
23509
|
+
const PRIVACY_ALREADY_ENABLED_STATUS = 'already-enabled';
|
|
22188
23510
|
/**
|
|
22189
|
-
*
|
|
23511
|
+
* Checks whether private mode is already enabled in runtime context.
|
|
22190
23512
|
*
|
|
22191
|
-
* @private
|
|
23513
|
+
* @private utility of USE PRIVACY commitment
|
|
22192
23514
|
*/
|
|
22193
|
-
function
|
|
22194
|
-
|
|
22195
|
-
|
|
22196
|
-
|
|
22197
|
-
if (ArrayBuffer.isView(bytes)) {
|
|
22198
|
-
return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
22199
|
-
}
|
|
22200
|
-
return new Uint8Array(bytes);
|
|
23515
|
+
function isPrivateModeEnabledInRuntimeContext(args) {
|
|
23516
|
+
var _a;
|
|
23517
|
+
const runtimeContext = readToolRuntimeContextFromToolArgs(args);
|
|
23518
|
+
return ((_a = runtimeContext === null || runtimeContext === void 0 ? void 0 : runtimeContext.memory) === null || _a === void 0 ? void 0 : _a.isPrivateMode) === true;
|
|
22201
23519
|
}
|
|
22202
23520
|
/**
|
|
22203
|
-
*
|
|
23521
|
+
* Creates a standard "confirmation required" result payload.
|
|
22204
23522
|
*
|
|
22205
|
-
* @private
|
|
23523
|
+
* @private utility of USE PRIVACY commitment
|
|
22206
23524
|
*/
|
|
22207
|
-
function
|
|
22208
|
-
|
|
22209
|
-
|
|
22210
|
-
|
|
22211
|
-
|
|
22212
|
-
return normalized || null;
|
|
23525
|
+
function createPrivacyConfirmationRequiredResult() {
|
|
23526
|
+
return {
|
|
23527
|
+
status: PRIVACY_CONFIRMATION_REQUIRED_STATUS,
|
|
23528
|
+
message: 'Private mode requires explicit user confirmation in the UI. Ask the user to confirm the privacy prompt.',
|
|
23529
|
+
};
|
|
22213
23530
|
}
|
|
22214
23531
|
/**
|
|
22215
|
-
*
|
|
23532
|
+
* Creates a standard "already enabled" result payload.
|
|
22216
23533
|
*
|
|
22217
|
-
* @private
|
|
23534
|
+
* @private utility of USE PRIVACY commitment
|
|
22218
23535
|
*/
|
|
22219
|
-
function
|
|
22220
|
-
|
|
22221
|
-
|
|
22222
|
-
|
|
22223
|
-
|
|
22224
|
-
if (!match) {
|
|
22225
|
-
return null;
|
|
22226
|
-
}
|
|
22227
|
-
return match[2].trim().toLowerCase();
|
|
23536
|
+
function createPrivacyAlreadyEnabledResult() {
|
|
23537
|
+
return {
|
|
23538
|
+
status: PRIVACY_ALREADY_ENABLED_STATUS,
|
|
23539
|
+
message: 'Private mode is already enabled for this chat session.',
|
|
23540
|
+
};
|
|
22228
23541
|
}
|
|
22229
23542
|
/**
|
|
22230
|
-
*
|
|
23543
|
+
* USE PRIVACY commitment definition.
|
|
22231
23544
|
*
|
|
22232
|
-
*
|
|
23545
|
+
* The `USE PRIVACY` commitment enables an agent to request enabling private mode.
|
|
23546
|
+
*
|
|
23547
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
22233
23548
|
*/
|
|
22234
|
-
|
|
22235
|
-
|
|
22236
|
-
|
|
22237
|
-
}
|
|
23549
|
+
class UsePrivacyCommitmentDefinition extends BaseCommitmentDefinition {
|
|
23550
|
+
constructor() {
|
|
23551
|
+
super('USE PRIVACY', ['PRIVACY']);
|
|
23552
|
+
}
|
|
23553
|
+
get requiresContent() {
|
|
23554
|
+
return false;
|
|
23555
|
+
}
|
|
23556
|
+
/**
|
|
23557
|
+
* Short one-line description of USE PRIVACY.
|
|
23558
|
+
*/
|
|
23559
|
+
get description() {
|
|
23560
|
+
return 'Enable the agent to request turning private mode on for sensitive conversations.';
|
|
23561
|
+
}
|
|
23562
|
+
/**
|
|
23563
|
+
* Icon for this commitment.
|
|
23564
|
+
*/
|
|
23565
|
+
get icon() {
|
|
23566
|
+
return '🔒';
|
|
23567
|
+
}
|
|
23568
|
+
/**
|
|
23569
|
+
* Markdown documentation for USE PRIVACY commitment.
|
|
23570
|
+
*/
|
|
23571
|
+
get documentation() {
|
|
23572
|
+
return spaceTrim$1(`
|
|
23573
|
+
# USE PRIVACY
|
|
23574
|
+
|
|
23575
|
+
Enables the agent to request turning on private mode in chat.
|
|
23576
|
+
|
|
23577
|
+
## Key aspects
|
|
23578
|
+
|
|
23579
|
+
- The tool \`turn_privacy_on\` asks the UI to show a confirmation dialog to the user.
|
|
23580
|
+
- Private mode is enabled only after explicit user confirmation in the UI.
|
|
23581
|
+
- In the current implementation, this reuses existing private mode behavior in chat.
|
|
23582
|
+
- While private mode is active, chat persistence, memory persistence, and self-learning are disabled.
|
|
23583
|
+
- Proper encryption is planned for future updates, but not implemented by this commitment yet.
|
|
23584
|
+
- Optional content after \`USE PRIVACY\` can provide additional privacy instructions.
|
|
23585
|
+
|
|
23586
|
+
## Examples
|
|
23587
|
+
|
|
23588
|
+
\`\`\`book
|
|
23589
|
+
Sensitive Assistant
|
|
23590
|
+
|
|
23591
|
+
PERSONA You help with sensitive topics where privacy is important.
|
|
23592
|
+
USE PRIVACY
|
|
23593
|
+
\`\`\`
|
|
23594
|
+
|
|
23595
|
+
\`\`\`book
|
|
23596
|
+
Compliance Assistant
|
|
23597
|
+
|
|
23598
|
+
PERSONA You assist with legal and HR conversations.
|
|
23599
|
+
USE PRIVACY Offer private mode when user asks to avoid storing data.
|
|
23600
|
+
\`\`\`
|
|
23601
|
+
`);
|
|
23602
|
+
}
|
|
23603
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
23604
|
+
const extraInstructions = formatOptionalInstructionBlock('Privacy instructions', content);
|
|
23605
|
+
const existingTools = requirements.tools || [];
|
|
23606
|
+
const tools = existingTools.some((tool) => tool.name === TURN_PRIVACY_ON_TOOL_NAME)
|
|
23607
|
+
? existingTools
|
|
23608
|
+
: [
|
|
23609
|
+
...existingTools,
|
|
23610
|
+
{
|
|
23611
|
+
name: TURN_PRIVACY_ON_TOOL_NAME,
|
|
23612
|
+
description: spaceTrim$1(`
|
|
23613
|
+
Requests turning private mode on in the chat UI.
|
|
23614
|
+
The user must explicitly confirm the action in a dialog before private mode is enabled.
|
|
23615
|
+
Use this for sensitive topics or when the user asks not to store conversation data.
|
|
23616
|
+
`),
|
|
23617
|
+
parameters: {
|
|
23618
|
+
type: 'object',
|
|
23619
|
+
properties: {},
|
|
23620
|
+
required: [],
|
|
23621
|
+
},
|
|
23622
|
+
},
|
|
23623
|
+
];
|
|
23624
|
+
return this.appendToSystemMessage({
|
|
23625
|
+
...requirements,
|
|
23626
|
+
tools,
|
|
23627
|
+
_metadata: {
|
|
23628
|
+
...requirements._metadata,
|
|
23629
|
+
usePrivacy: content || true,
|
|
23630
|
+
},
|
|
23631
|
+
}, spaceTrim$1((block) => `
|
|
23632
|
+
Privacy mode:
|
|
23633
|
+
- Use "${TURN_PRIVACY_ON_TOOL_NAME}" when the user asks for a private/sensitive conversation.
|
|
23634
|
+
- This tool requests a UI confirmation dialog. Private mode is enabled only after user confirms.
|
|
23635
|
+
- Current implementation uses the existing chat private mode (no chat persistence, memory persistence, or self-learning while active).
|
|
23636
|
+
- Do not claim that end-to-end encryption is implemented yet.
|
|
23637
|
+
${block(extraInstructions)}
|
|
23638
|
+
`));
|
|
23639
|
+
}
|
|
23640
|
+
/**
|
|
23641
|
+
* Gets human-readable titles for tool functions provided by this commitment.
|
|
23642
|
+
*/
|
|
23643
|
+
getToolTitles() {
|
|
23644
|
+
return {
|
|
23645
|
+
[TURN_PRIVACY_ON_TOOL_NAME]: 'Turn privacy mode on',
|
|
23646
|
+
};
|
|
23647
|
+
}
|
|
23648
|
+
/**
|
|
23649
|
+
* Gets the `turn_privacy_on` tool function implementation.
|
|
23650
|
+
*/
|
|
23651
|
+
getToolFunctions() {
|
|
23652
|
+
return {
|
|
23653
|
+
async [TURN_PRIVACY_ON_TOOL_NAME](args) {
|
|
23654
|
+
const result = isPrivateModeEnabledInRuntimeContext(args)
|
|
23655
|
+
? createPrivacyAlreadyEnabledResult()
|
|
23656
|
+
: createPrivacyConfirmationRequiredResult();
|
|
23657
|
+
return JSON.stringify(result);
|
|
23658
|
+
},
|
|
23659
|
+
};
|
|
23660
|
+
}
|
|
23661
|
+
}
|
|
23662
|
+
/**
|
|
23663
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
23664
|
+
*/
|
|
23665
|
+
|
|
23666
|
+
/* eslint-disable no-magic-numbers */
|
|
23667
|
+
/**
|
|
23668
|
+
* Default byte limit for one best-effort text decode.
|
|
23669
|
+
*
|
|
23670
|
+
* @private constant of decodeAttachmentAsText
|
|
23671
|
+
*/
|
|
23672
|
+
const DEFAULT_ATTACHMENT_TEXT_DECODE_BYTES = 512 * 1024;
|
|
23673
|
+
/**
|
|
23674
|
+
* Marker appended when only a prefix of the payload is decoded.
|
|
23675
|
+
*
|
|
23676
|
+
* @private constant of decodeAttachmentAsText
|
|
23677
|
+
*/
|
|
23678
|
+
const TRUNCATED_MARKER = '…[TRUNCATED]…';
|
|
23679
|
+
/**
|
|
23680
|
+
* MIME types that are trustworthy indicators of textual content.
|
|
23681
|
+
*
|
|
23682
|
+
* @private constant of decodeAttachmentAsText
|
|
23683
|
+
*/
|
|
23684
|
+
const TRUSTED_TEXT_MIME_TYPES = new Set([
|
|
23685
|
+
'application/json',
|
|
23686
|
+
'application/ld+json',
|
|
23687
|
+
'application/javascript',
|
|
23688
|
+
'application/x-javascript',
|
|
23689
|
+
'application/xml',
|
|
23690
|
+
'application/xhtml+xml',
|
|
23691
|
+
'application/x-www-form-urlencoded',
|
|
23692
|
+
'application/yaml',
|
|
23693
|
+
'application/x-yaml',
|
|
23694
|
+
'application/toml',
|
|
23695
|
+
'application/sql',
|
|
23696
|
+
'application/rtf',
|
|
23697
|
+
'application/x-subrip',
|
|
23698
|
+
]);
|
|
23699
|
+
/**
|
|
23700
|
+
* MIME types that are trustworthy indicators of binary content.
|
|
23701
|
+
*
|
|
23702
|
+
* @private constant of decodeAttachmentAsText
|
|
23703
|
+
*/
|
|
23704
|
+
const TRUSTED_BINARY_MIME_TYPES = new Set([
|
|
23705
|
+
'application/pdf',
|
|
23706
|
+
'application/zip',
|
|
23707
|
+
'application/gzip',
|
|
23708
|
+
'application/x-gzip',
|
|
23709
|
+
'application/x-7z-compressed',
|
|
23710
|
+
'application/vnd.rar',
|
|
23711
|
+
'application/x-rar-compressed',
|
|
23712
|
+
]);
|
|
23713
|
+
/**
|
|
23714
|
+
* MIME prefixes that strongly indicate binary content.
|
|
23715
|
+
*
|
|
23716
|
+
* @private constant of decodeAttachmentAsText
|
|
23717
|
+
*/
|
|
23718
|
+
const TRUSTED_BINARY_MIME_PREFIXES = ['image/', 'audio/', 'video/', 'font/', 'model/'];
|
|
23719
|
+
/**
|
|
23720
|
+
* Suspicious characters that often appear when `windows-1250` text is decoded as `windows-1252`.
|
|
23721
|
+
*
|
|
23722
|
+
* @private constant of decodeAttachmentAsText
|
|
23723
|
+
*/
|
|
23724
|
+
const SUSPICIOUS_SINGLE_BYTE_ARTIFACTS = new Set([
|
|
23725
|
+
'ˇ',
|
|
23726
|
+
'˘',
|
|
23727
|
+
'˙',
|
|
23728
|
+
'˛',
|
|
23729
|
+
'˝',
|
|
23730
|
+
'¸',
|
|
23731
|
+
'¤',
|
|
23732
|
+
'¦',
|
|
23733
|
+
'¨',
|
|
23734
|
+
'¯',
|
|
23735
|
+
'²',
|
|
23736
|
+
'³',
|
|
23737
|
+
'¹',
|
|
23738
|
+
]);
|
|
23739
|
+
/**
|
|
23740
|
+
* Supported fallback encodings tried when UTF-8 is not convincing.
|
|
23741
|
+
*
|
|
23742
|
+
* @private constant of decodeAttachmentAsText
|
|
23743
|
+
*/
|
|
23744
|
+
const FALLBACK_ENCODINGS = ['windows-1250', 'windows-1252', 'iso-8859-1'];
|
|
23745
|
+
/**
|
|
23746
|
+
* Converts unknown byte containers into `Uint8Array`.
|
|
23747
|
+
*
|
|
23748
|
+
* @private function of decodeAttachmentAsText
|
|
23749
|
+
*/
|
|
23750
|
+
function toUint8Array(bytes) {
|
|
23751
|
+
if (bytes instanceof Uint8Array) {
|
|
23752
|
+
return bytes;
|
|
23753
|
+
}
|
|
23754
|
+
if (ArrayBuffer.isView(bytes)) {
|
|
23755
|
+
return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
23756
|
+
}
|
|
23757
|
+
return new Uint8Array(bytes);
|
|
23758
|
+
}
|
|
23759
|
+
/**
|
|
23760
|
+
* Removes MIME parameters and normalizes casing.
|
|
23761
|
+
*
|
|
23762
|
+
* @private function of decodeAttachmentAsText
|
|
23763
|
+
*/
|
|
23764
|
+
function normalizeMimeType(mimeType) {
|
|
23765
|
+
if (!mimeType) {
|
|
23766
|
+
return null;
|
|
23767
|
+
}
|
|
23768
|
+
const normalized = mimeType.split(';')[0].trim().toLowerCase();
|
|
23769
|
+
return normalized || null;
|
|
23770
|
+
}
|
|
23771
|
+
/**
|
|
23772
|
+
* Extracts optional `charset=...` information from a MIME type value.
|
|
23773
|
+
*
|
|
23774
|
+
* @private function of decodeAttachmentAsText
|
|
23775
|
+
*/
|
|
23776
|
+
function extractCharset(mimeType) {
|
|
23777
|
+
if (!mimeType) {
|
|
23778
|
+
return null;
|
|
23779
|
+
}
|
|
23780
|
+
const match = mimeType.match(/charset\s*=\s*("?)([^";\s]+)\1/i);
|
|
23781
|
+
if (!match) {
|
|
23782
|
+
return null;
|
|
23783
|
+
}
|
|
23784
|
+
return match[2].trim().toLowerCase();
|
|
23785
|
+
}
|
|
23786
|
+
/**
|
|
23787
|
+
* Returns true when the MIME type is a strong textual hint.
|
|
23788
|
+
*
|
|
23789
|
+
* @private function of decodeAttachmentAsText
|
|
23790
|
+
*/
|
|
23791
|
+
function isTrustedTextMimeType(mimeType) {
|
|
23792
|
+
if (!mimeType) {
|
|
23793
|
+
return false;
|
|
23794
|
+
}
|
|
22238
23795
|
return (mimeType.startsWith('text/') ||
|
|
22239
23796
|
mimeType.endsWith('+json') ||
|
|
22240
23797
|
mimeType.endsWith('+xml') ||
|
|
@@ -25198,503 +26755,186 @@ const COMMITMENT_REGISTRY = [
|
|
|
25198
26755
|
new NoteCommitmentDefinition('COMMENT'),
|
|
25199
26756
|
new NoteCommitmentDefinition('NONCE'),
|
|
25200
26757
|
new NoteCommitmentDefinition('TODO'),
|
|
25201
|
-
new GoalCommitmentDefinition('GOAL'),
|
|
25202
|
-
new GoalCommitmentDefinition('GOALS'),
|
|
25203
|
-
new InitialMessageCommitmentDefinition(),
|
|
25204
|
-
new UserMessageCommitmentDefinition(),
|
|
25205
|
-
new InternalMessageCommitmentDefinition(),
|
|
25206
|
-
new AgentMessageCommitmentDefinition(),
|
|
25207
|
-
new MessageSuffixCommitmentDefinition(),
|
|
25208
|
-
new MessageCommitmentDefinition('MESSAGE'),
|
|
25209
|
-
new MessageCommitmentDefinition('MESSAGES'),
|
|
25210
|
-
new ScenarioCommitmentDefinition('SCENARIO'),
|
|
25211
|
-
new ScenarioCommitmentDefinition('SCENARIOS'),
|
|
25212
|
-
new DeleteCommitmentDefinition('DELETE'),
|
|
25213
|
-
new DeleteCommitmentDefinition('CANCEL'),
|
|
25214
|
-
new DeleteCommitmentDefinition('DISCARD'),
|
|
25215
|
-
new DeleteCommitmentDefinition('REMOVE'),
|
|
25216
|
-
new DictionaryCommitmentDefinition(),
|
|
25217
|
-
new OpenCommitmentDefinition(),
|
|
25218
|
-
new ClosedCommitmentDefinition(),
|
|
25219
|
-
new TeamCommitmentDefinition(),
|
|
25220
|
-
new UseBrowserCommitmentDefinition(),
|
|
25221
|
-
new UseSearchEngineCommitmentDefinition(),
|
|
25222
|
-
new UseSpawnCommitmentDefinition(),
|
|
25223
|
-
new UseTimeoutCommitmentDefinition(),
|
|
25224
|
-
new UseTimeCommitmentDefinition(),
|
|
25225
|
-
new UseUserLocationCommitmentDefinition(),
|
|
25226
|
-
new
|
|
25227
|
-
new
|
|
25228
|
-
new
|
|
25229
|
-
new
|
|
25230
|
-
new
|
|
25231
|
-
new
|
|
25232
|
-
new
|
|
25233
|
-
|
|
25234
|
-
|
|
25235
|
-
new NotYetImplementedCommitmentDefinition('
|
|
25236
|
-
new NotYetImplementedCommitmentDefinition('
|
|
25237
|
-
new NotYetImplementedCommitmentDefinition('
|
|
25238
|
-
new NotYetImplementedCommitmentDefinition('
|
|
25239
|
-
new NotYetImplementedCommitmentDefinition('
|
|
25240
|
-
|
|
25241
|
-
|
|
25242
|
-
|
|
25243
|
-
* TODO: [🧠] Maybe create through standardized $register
|
|
25244
|
-
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
25245
|
-
*/
|
|
25246
|
-
|
|
25247
|
-
/**
|
|
25248
|
-
* Gets all available commitment definitions
|
|
25249
|
-
* @returns Array of all commitment definitions
|
|
25250
|
-
*
|
|
25251
|
-
* @public exported from `@promptbook/core`
|
|
25252
|
-
*/
|
|
25253
|
-
function getAllCommitmentDefinitions() {
|
|
25254
|
-
return $deepFreeze([...COMMITMENT_REGISTRY]);
|
|
25255
|
-
}
|
|
25256
|
-
|
|
25257
|
-
/**
|
|
25258
|
-
* Collects tool functions from all commitment definitions.
|
|
25259
|
-
*
|
|
25260
|
-
* @returns Map of tool function implementations.
|
|
25261
|
-
* @private internal helper for commitment tool registry
|
|
25262
|
-
*/
|
|
25263
|
-
function collectCommitmentToolFunctions() {
|
|
25264
|
-
const allToolFunctions = {};
|
|
25265
|
-
for (const commitmentDefinition of getAllCommitmentDefinitions()) {
|
|
25266
|
-
const toolFunctions = commitmentDefinition.getToolFunctions();
|
|
25267
|
-
for (const [funcName, funcImpl] of Object.entries(toolFunctions)) {
|
|
25268
|
-
if (allToolFunctions[funcName] !== undefined &&
|
|
25269
|
-
just(false) /* <- Note: [??] How to deal with commitment aliases */) {
|
|
25270
|
-
throw new UnexpectedError(`Duplicate tool function name detected: \`${funcName}\` provided by commitment \`${commitmentDefinition.type}\``);
|
|
25271
|
-
}
|
|
25272
|
-
allToolFunctions[funcName] = funcImpl;
|
|
25273
|
-
}
|
|
25274
|
-
}
|
|
25275
|
-
return allToolFunctions;
|
|
25276
|
-
}
|
|
25277
|
-
/**
|
|
25278
|
-
* Creates a proxy that resolves tool functions on demand.
|
|
25279
|
-
*
|
|
25280
|
-
* @param getFunctions - Provider of current tool functions.
|
|
25281
|
-
* @returns Proxy exposing tool functions as properties.
|
|
25282
|
-
* @private internal helper for commitment tool registry
|
|
25283
|
-
*/
|
|
25284
|
-
function createToolFunctionsProxy(getFunctions) {
|
|
25285
|
-
const resolveFunctions = () => getFunctions();
|
|
25286
|
-
return new Proxy({}, {
|
|
25287
|
-
get(_target, prop) {
|
|
25288
|
-
if (typeof prop !== 'string') {
|
|
25289
|
-
return undefined;
|
|
25290
|
-
}
|
|
25291
|
-
return resolveFunctions()[prop];
|
|
25292
|
-
},
|
|
25293
|
-
has(_target, prop) {
|
|
25294
|
-
if (typeof prop !== 'string') {
|
|
25295
|
-
return false;
|
|
25296
|
-
}
|
|
25297
|
-
return prop in resolveFunctions();
|
|
25298
|
-
},
|
|
25299
|
-
ownKeys() {
|
|
25300
|
-
return Object.keys(resolveFunctions());
|
|
25301
|
-
},
|
|
25302
|
-
getOwnPropertyDescriptor(_target, prop) {
|
|
25303
|
-
if (typeof prop !== 'string') {
|
|
25304
|
-
return undefined;
|
|
25305
|
-
}
|
|
25306
|
-
const value = resolveFunctions()[prop];
|
|
25307
|
-
if (value === undefined) {
|
|
25308
|
-
return undefined;
|
|
25309
|
-
}
|
|
25310
|
-
return {
|
|
25311
|
-
enumerable: true,
|
|
25312
|
-
configurable: true,
|
|
25313
|
-
writable: false,
|
|
25314
|
-
value,
|
|
25315
|
-
};
|
|
25316
|
-
},
|
|
25317
|
-
});
|
|
25318
|
-
}
|
|
25319
|
-
/**
|
|
25320
|
-
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
25321
|
-
*/
|
|
25322
|
-
|
|
25323
|
-
const nodeToolFunctions = {
|
|
25324
|
-
/**
|
|
25325
|
-
* @@@
|
|
25326
|
-
*
|
|
25327
|
-
* Note: [??] This function has implementation both for browser and node, this is the full one for node
|
|
25328
|
-
*/
|
|
25329
|
-
async fetch_url_content(args) {
|
|
25330
|
-
console.log('!!!! [Tool] fetch_url_content called', { args });
|
|
25331
|
-
const { url } = args;
|
|
25332
|
-
return await fetchUrlContent(url);
|
|
25333
|
-
},
|
|
25334
|
-
/**
|
|
25335
|
-
* @@@
|
|
25336
|
-
*
|
|
25337
|
-
* Note: [??] This function has implementation both for browser and node, this is the server one for node
|
|
25338
|
-
*/
|
|
25339
|
-
run_browser: resolveRunBrowserToolForNode(),
|
|
25340
|
-
/**
|
|
25341
|
-
* @@@
|
|
25342
|
-
*
|
|
25343
|
-
* Note: [??] This function has implementation both for browser and node, this is the server one for node
|
|
25344
|
-
*/
|
|
25345
|
-
send_email: resolveSendEmailToolForNode(),
|
|
25346
|
-
/**
|
|
25347
|
-
* @@@
|
|
25348
|
-
*
|
|
25349
|
-
* Note: [??] This function has implementation both for browser and node, this is the server one for node
|
|
25350
|
-
*/
|
|
25351
|
-
spawn_agent: resolveSpawnAgentToolForNode(),
|
|
25352
|
-
// TODO: !!!! Unhardcode, make proper server function register from definitions
|
|
25353
|
-
};
|
|
25354
|
-
const nodeToolFunctionsProxy = createToolFunctionsProxy(() => ({
|
|
25355
|
-
...collectCommitmentToolFunctions(),
|
|
25356
|
-
...nodeToolFunctions,
|
|
25357
|
-
}));
|
|
25358
|
-
/**
|
|
25359
|
-
* Gets all function implementations provided by all commitments
|
|
25360
|
-
*
|
|
25361
|
-
* Note: This function is intended for server use, there is also equivalent `getAllCommitmentsToolFunctionsForBrowser` for browser use
|
|
25362
|
-
*
|
|
25363
|
-
* @public exported from `@promptbook/node`
|
|
25364
|
-
*/
|
|
25365
|
-
function getAllCommitmentsToolFunctionsForNode() {
|
|
25366
|
-
if (!$isRunningInNode()) {
|
|
25367
|
-
throw new EnvironmentMismatchError(spaceTrim(`
|
|
25368
|
-
Function getAllCommitmentsToolFunctionsForNode should be run in Node.js environment.
|
|
25369
|
-
|
|
25370
|
-
- In browser use getAllCommitmentsToolFunctionsForBrowser instead.
|
|
25371
|
-
- This function can include server-only tools which cannot run in browser environment.
|
|
25372
|
-
|
|
25373
|
-
`));
|
|
25374
|
-
}
|
|
25375
|
-
return nodeToolFunctionsProxy;
|
|
25376
|
-
}
|
|
25377
|
-
/**
|
|
25378
|
-
* Note: [??] Code in this file should never be never released in packages that could be imported into browser environment
|
|
25379
|
-
* TODO: [??] Unite `xxxForServer` and `xxxForNode` naming
|
|
25380
|
-
*/
|
|
25381
|
-
|
|
25382
|
-
/**
|
|
25383
|
-
* Normalize options for `execCommand` and `execCommands`
|
|
25384
|
-
*
|
|
25385
|
-
* Note: `$` is used to indicate that this function behaves differently according to `process.platform`
|
|
25386
|
-
*
|
|
25387
|
-
* @private internal utility of `execCommand` and `execCommands`
|
|
25388
|
-
*/
|
|
25389
|
-
function $execCommandNormalizeOptions(options) {
|
|
25390
|
-
var _a, _b, _c, _d;
|
|
25391
|
-
let command;
|
|
25392
|
-
let cwd;
|
|
25393
|
-
let crashOnError;
|
|
25394
|
-
let args = [];
|
|
25395
|
-
let timeout;
|
|
25396
|
-
let isVerbose;
|
|
25397
|
-
let env;
|
|
25398
|
-
if (typeof options === 'string') {
|
|
25399
|
-
// TODO: [1] DRY default values
|
|
25400
|
-
command = options;
|
|
25401
|
-
cwd = process.cwd();
|
|
25402
|
-
crashOnError = true;
|
|
25403
|
-
timeout = Infinity; // <- TODO: [⏳]
|
|
25404
|
-
isVerbose = DEFAULT_IS_VERBOSE;
|
|
25405
|
-
env = undefined;
|
|
25406
|
-
}
|
|
25407
|
-
else {
|
|
25408
|
-
/*
|
|
25409
|
-
TODO:
|
|
25410
|
-
if ((options as any).commands !== undefined) {
|
|
25411
|
-
commands = (options as any).commands;
|
|
25412
|
-
} else {
|
|
25413
|
-
commands = [(options as any).command];
|
|
25414
|
-
}
|
|
25415
|
-
*/
|
|
25416
|
-
// TODO: [1] DRY default values
|
|
25417
|
-
command = options.command;
|
|
25418
|
-
cwd = (_a = options.cwd) !== null && _a !== void 0 ? _a : process.cwd();
|
|
25419
|
-
crashOnError = (_b = options.crashOnError) !== null && _b !== void 0 ? _b : true;
|
|
25420
|
-
timeout = (_c = options.timeout) !== null && _c !== void 0 ? _c : Infinity;
|
|
25421
|
-
isVerbose = (_d = options.isVerbose) !== null && _d !== void 0 ? _d : DEFAULT_IS_VERBOSE;
|
|
25422
|
-
env = options.env;
|
|
25423
|
-
}
|
|
25424
|
-
// TODO: /(-[a-zA-Z0-9-]+\s+[^\s]*)|[^\s]*/g
|
|
25425
|
-
const _ = Array.from(command.matchAll(/(".*")|([^\s]*)/g))
|
|
25426
|
-
.map(([match]) => match)
|
|
25427
|
-
.filter((arg) => arg !== '');
|
|
25428
|
-
if (_.length > 1) {
|
|
25429
|
-
[command, ...args] = _;
|
|
25430
|
-
}
|
|
25431
|
-
if (options.args) {
|
|
25432
|
-
args = [...args, ...options.args];
|
|
25433
|
-
}
|
|
25434
|
-
let humanReadableCommand = !['npx', 'npm'].includes(command) ? command : args[0];
|
|
25435
|
-
if (['ts-node'].includes(humanReadableCommand)) {
|
|
25436
|
-
humanReadableCommand += ` ${args[1]}`;
|
|
25437
|
-
}
|
|
25438
|
-
if (/^win/.test(process.platform) && ['npm', 'npx'].includes(command)) {
|
|
25439
|
-
command = `${command}.cmd`;
|
|
25440
|
-
}
|
|
25441
|
-
return { command, humanReadableCommand, args, cwd, crashOnError, timeout, isVerbose, env };
|
|
25442
|
-
}
|
|
25443
|
-
// TODO: This should show type error> execCommandNormalizeOptions({ command: '', commands: [''] });
|
|
25444
|
-
|
|
25445
|
-
/**
|
|
25446
|
-
* Run one command in a shell
|
|
25447
|
-
*
|
|
25448
|
-
*
|
|
25449
|
-
* Note: There are 2 similar functions in the codebase:
|
|
25450
|
-
* - `$execCommand` which runs a single command
|
|
25451
|
-
* - `$execCommands` which runs multiple commands
|
|
25452
|
-
* Note: `$` is used to indicate that this function is not a pure function - it runs a command in a shell
|
|
25453
|
-
*
|
|
25454
|
-
* @public exported from `@promptbook/node`
|
|
25455
|
-
*/
|
|
25456
|
-
function $execCommand(options) {
|
|
25457
|
-
if (!$isRunningInNode()) {
|
|
25458
|
-
throw new EnvironmentMismatchError('Function `$execCommand` can run only in Node environment.js');
|
|
25459
|
-
}
|
|
25460
|
-
return new Promise((resolve, reject) => {
|
|
25461
|
-
// eslint-disable-next-line prefer-const
|
|
25462
|
-
const { command, humanReadableCommand, args, cwd, crashOnError, timeout, isVerbose = DEFAULT_IS_VERBOSE, env, } = $execCommandNormalizeOptions(options);
|
|
25463
|
-
if (timeout !== Infinity) {
|
|
25464
|
-
// TODO: In waitasecond forTime(Infinity) should be equivalent to forEver()
|
|
25465
|
-
forTime(timeout).then(() => {
|
|
25466
|
-
if (crashOnError) {
|
|
25467
|
-
reject(new Error(`Command "${humanReadableCommand}" exceeded time limit of ${timeout}ms`));
|
|
25468
|
-
}
|
|
25469
|
-
else {
|
|
25470
|
-
console.warn(`Command "${humanReadableCommand}" exceeded time limit of ${timeout}ms but continues running`);
|
|
25471
|
-
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
25472
|
-
resolve('Command exceeded time limit');
|
|
25473
|
-
}
|
|
25474
|
-
});
|
|
25475
|
-
}
|
|
25476
|
-
if (isVerbose) {
|
|
25477
|
-
console.info(colors.yellow(cwd) + ' ' + colors.green(command) + ' ' + colors.blue(args.join(' ')));
|
|
25478
|
-
}
|
|
25479
|
-
try {
|
|
25480
|
-
const commandProcess = spawn(command, args, {
|
|
25481
|
-
cwd,
|
|
25482
|
-
shell: true,
|
|
25483
|
-
env: env ? { ...process.env, ...env } : process.env,
|
|
25484
|
-
});
|
|
25485
|
-
if (isVerbose) {
|
|
25486
|
-
commandProcess.on('message', (message) => {
|
|
25487
|
-
console.info({ message });
|
|
25488
|
-
});
|
|
25489
|
-
}
|
|
25490
|
-
const output = [];
|
|
25491
|
-
commandProcess.stdout.on('data', (stdout) => {
|
|
25492
|
-
output.push(stdout.toString());
|
|
25493
|
-
if (isVerbose) {
|
|
25494
|
-
console.info(stdout.toString());
|
|
25495
|
-
}
|
|
25496
|
-
});
|
|
25497
|
-
commandProcess.stderr.on('data', (stderr) => {
|
|
25498
|
-
output.push(stderr.toString());
|
|
25499
|
-
if (isVerbose && stderr.toString().trim()) {
|
|
25500
|
-
console.warn(stderr.toString());
|
|
25501
|
-
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
25502
|
-
}
|
|
25503
|
-
});
|
|
25504
|
-
const finishWithCode = (code) => {
|
|
25505
|
-
if (code !== 0) {
|
|
25506
|
-
if (crashOnError) {
|
|
25507
|
-
reject(new Error(output.join('\n').trim() ||
|
|
25508
|
-
`Command "${humanReadableCommand}" exited with code ${code}`));
|
|
25509
|
-
}
|
|
25510
|
-
else {
|
|
25511
|
-
if (isVerbose) {
|
|
25512
|
-
console.warn(`Command "${humanReadableCommand}" exited with code ${code}`);
|
|
25513
|
-
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
25514
|
-
}
|
|
25515
|
-
resolve(spaceTrim$1(output.join('\n')));
|
|
25516
|
-
}
|
|
25517
|
-
}
|
|
25518
|
-
else {
|
|
25519
|
-
resolve(spaceTrim$1(output.join('\n')));
|
|
25520
|
-
}
|
|
25521
|
-
};
|
|
25522
|
-
commandProcess.on('close', finishWithCode);
|
|
25523
|
-
commandProcess.on('exit', finishWithCode);
|
|
25524
|
-
commandProcess.on('disconnect', () => {
|
|
25525
|
-
// Note: Unexpected disconnection should always result in rejection
|
|
25526
|
-
reject(new Error(`Command "${humanReadableCommand}" disconnected`));
|
|
25527
|
-
});
|
|
25528
|
-
commandProcess.on('error', (error) => {
|
|
25529
|
-
if (crashOnError) {
|
|
25530
|
-
reject(new Error(`Command "${humanReadableCommand}" failed: \n${error.message}`));
|
|
25531
|
-
}
|
|
25532
|
-
else {
|
|
25533
|
-
if (isVerbose) {
|
|
25534
|
-
console.warn(error);
|
|
25535
|
-
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
25536
|
-
}
|
|
25537
|
-
resolve(spaceTrim$1(output.join('\n')));
|
|
25538
|
-
}
|
|
25539
|
-
});
|
|
25540
|
-
}
|
|
25541
|
-
catch (error) {
|
|
25542
|
-
// Note: Unexpected error in sync code should always result in rejection
|
|
25543
|
-
reject(error);
|
|
25544
|
-
}
|
|
25545
|
-
});
|
|
25546
|
-
}
|
|
25547
|
-
/**
|
|
25548
|
-
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
25549
|
-
*/
|
|
25550
|
-
|
|
25551
|
-
/**
|
|
25552
|
-
* Attempts to locate the specified application on a Linux system using the 'which' command.
|
|
25553
|
-
* Returns the path to the executable if found, or null otherwise.
|
|
25554
|
-
*
|
|
25555
|
-
* @private within the repository
|
|
25556
|
-
*/
|
|
25557
|
-
async function locateAppOnLinux({ linuxWhich, }) {
|
|
25558
|
-
try {
|
|
25559
|
-
const result = await $execCommand({ crashOnError: true, command: `which ${linuxWhich}` });
|
|
25560
|
-
return result.trim();
|
|
25561
|
-
}
|
|
25562
|
-
catch (error) {
|
|
25563
|
-
assertsError(error);
|
|
25564
|
-
return null;
|
|
25565
|
-
}
|
|
25566
|
-
}
|
|
26758
|
+
new GoalCommitmentDefinition('GOAL'),
|
|
26759
|
+
new GoalCommitmentDefinition('GOALS'),
|
|
26760
|
+
new InitialMessageCommitmentDefinition(),
|
|
26761
|
+
new UserMessageCommitmentDefinition(),
|
|
26762
|
+
new InternalMessageCommitmentDefinition(),
|
|
26763
|
+
new AgentMessageCommitmentDefinition(),
|
|
26764
|
+
new MessageSuffixCommitmentDefinition(),
|
|
26765
|
+
new MessageCommitmentDefinition('MESSAGE'),
|
|
26766
|
+
new MessageCommitmentDefinition('MESSAGES'),
|
|
26767
|
+
new ScenarioCommitmentDefinition('SCENARIO'),
|
|
26768
|
+
new ScenarioCommitmentDefinition('SCENARIOS'),
|
|
26769
|
+
new DeleteCommitmentDefinition('DELETE'),
|
|
26770
|
+
new DeleteCommitmentDefinition('CANCEL'),
|
|
26771
|
+
new DeleteCommitmentDefinition('DISCARD'),
|
|
26772
|
+
new DeleteCommitmentDefinition('REMOVE'),
|
|
26773
|
+
new DictionaryCommitmentDefinition(),
|
|
26774
|
+
new OpenCommitmentDefinition(),
|
|
26775
|
+
new ClosedCommitmentDefinition(),
|
|
26776
|
+
new TeamCommitmentDefinition(),
|
|
26777
|
+
new UseBrowserCommitmentDefinition(),
|
|
26778
|
+
new UseSearchEngineCommitmentDefinition(),
|
|
26779
|
+
new UseSpawnCommitmentDefinition(),
|
|
26780
|
+
new UseTimeoutCommitmentDefinition(),
|
|
26781
|
+
new UseTimeCommitmentDefinition(),
|
|
26782
|
+
new UseUserLocationCommitmentDefinition(),
|
|
26783
|
+
new UseCalendarCommitmentDefinition(),
|
|
26784
|
+
new UseEmailCommitmentDefinition(),
|
|
26785
|
+
new UsePopupCommitmentDefinition(),
|
|
26786
|
+
new UseImageGeneratorCommitmentDefinition('USE IMAGE GENERATOR'),
|
|
26787
|
+
new UseMcpCommitmentDefinition(),
|
|
26788
|
+
new UsePrivacyCommitmentDefinition(),
|
|
26789
|
+
new UseProjectCommitmentDefinition(),
|
|
26790
|
+
new UseCommitmentDefinition(),
|
|
26791
|
+
// Not yet implemented commitments (using placeholder)
|
|
26792
|
+
new NotYetImplementedCommitmentDefinition('EXPECT'),
|
|
26793
|
+
new NotYetImplementedCommitmentDefinition('BEHAVIOUR'),
|
|
26794
|
+
new NotYetImplementedCommitmentDefinition('BEHAVIOURS'),
|
|
26795
|
+
new NotYetImplementedCommitmentDefinition('AVOID'),
|
|
26796
|
+
new NotYetImplementedCommitmentDefinition('AVOIDANCE'),
|
|
26797
|
+
new NotYetImplementedCommitmentDefinition('CONTEXT'),
|
|
26798
|
+
// <- TODO: Prompt: Leverage aliases instead of duplicating commitment definitions
|
|
26799
|
+
];
|
|
25567
26800
|
/**
|
|
25568
|
-
* TODO: [🧠]
|
|
25569
|
-
* Note: [
|
|
26801
|
+
* TODO: [🧠] Maybe create through standardized $register
|
|
26802
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
25570
26803
|
*/
|
|
25571
26804
|
|
|
25572
26805
|
/**
|
|
25573
|
-
*
|
|
26806
|
+
* Gets all available commitment definitions
|
|
26807
|
+
* @returns Array of all commitment definitions
|
|
25574
26808
|
*
|
|
25575
|
-
* @
|
|
26809
|
+
* @public exported from `@promptbook/core`
|
|
25576
26810
|
*/
|
|
25577
|
-
|
|
25578
|
-
|
|
25579
|
-
await fs.access(path, fs.constants.X_OK);
|
|
25580
|
-
return true;
|
|
25581
|
-
}
|
|
25582
|
-
catch (error) {
|
|
25583
|
-
return false;
|
|
25584
|
-
}
|
|
26811
|
+
function getAllCommitmentDefinitions() {
|
|
26812
|
+
return $deepFreeze([...COMMITMENT_REGISTRY]);
|
|
25585
26813
|
}
|
|
25586
|
-
/**
|
|
25587
|
-
* Note: Not [~🟢~] because it is not directly dependent on `fs
|
|
25588
|
-
* TODO: [🖇] What about symlinks?
|
|
25589
|
-
*/
|
|
25590
26814
|
|
|
25591
|
-
// Note: Module `userhome` has no types available, so it is imported using `require`
|
|
25592
|
-
// @see https://stackoverflow.com/questions/37000981/how-to-import-node-module-in-typescript-without-type-definitions
|
|
25593
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
25594
|
-
const userhome = require('userhome');
|
|
25595
26815
|
/**
|
|
25596
|
-
*
|
|
25597
|
-
* Returns the path to the executable if found, or null otherwise.
|
|
26816
|
+
* Collects tool functions from all commitment definitions.
|
|
25598
26817
|
*
|
|
25599
|
-
* @
|
|
26818
|
+
* @returns Map of tool function implementations.
|
|
26819
|
+
* @private internal helper for commitment tool registry
|
|
25600
26820
|
*/
|
|
25601
|
-
|
|
25602
|
-
|
|
25603
|
-
|
|
25604
|
-
const
|
|
25605
|
-
const
|
|
25606
|
-
|
|
25607
|
-
|
|
25608
|
-
|
|
25609
|
-
|
|
25610
|
-
|
|
26821
|
+
function collectCommitmentToolFunctions() {
|
|
26822
|
+
const allToolFunctions = {};
|
|
26823
|
+
for (const commitmentDefinition of getAllCommitmentDefinitions()) {
|
|
26824
|
+
const toolFunctions = commitmentDefinition.getToolFunctions();
|
|
26825
|
+
for (const [funcName, funcImpl] of Object.entries(toolFunctions)) {
|
|
26826
|
+
if (allToolFunctions[funcName] !== undefined &&
|
|
26827
|
+
just(false) /* <- Note: [??] How to deal with commitment aliases */) {
|
|
26828
|
+
throw new UnexpectedError(`Duplicate tool function name detected: \`${funcName}\` provided by commitment \`${commitmentDefinition.type}\``);
|
|
26829
|
+
}
|
|
26830
|
+
allToolFunctions[funcName] = funcImpl;
|
|
25611
26831
|
}
|
|
25612
|
-
const result = await $execCommand({
|
|
25613
|
-
crashOnError: true,
|
|
25614
|
-
command: `mdfind 'kMDItemDisplayName == "${macOsName}" && kMDItemKind == Application'`,
|
|
25615
|
-
});
|
|
25616
|
-
return result.trim() + toExec;
|
|
25617
|
-
}
|
|
25618
|
-
catch (error) {
|
|
25619
|
-
assertsError(error);
|
|
25620
|
-
return null;
|
|
25621
26832
|
}
|
|
26833
|
+
return allToolFunctions;
|
|
25622
26834
|
}
|
|
25623
26835
|
/**
|
|
25624
|
-
*
|
|
25625
|
-
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
25626
|
-
*/
|
|
25627
|
-
|
|
25628
|
-
/**
|
|
25629
|
-
* Attempts to locate the specified application on a Windows system by searching common installation directories.
|
|
25630
|
-
* Returns the path to the executable if found, or null otherwise.
|
|
26836
|
+
* Creates a proxy that resolves tool functions on demand.
|
|
25631
26837
|
*
|
|
25632
|
-
* @
|
|
26838
|
+
* @param getFunctions - Provider of current tool functions.
|
|
26839
|
+
* @returns Proxy exposing tool functions as properties.
|
|
26840
|
+
* @private internal helper for commitment tool registry
|
|
25633
26841
|
*/
|
|
25634
|
-
|
|
25635
|
-
|
|
25636
|
-
|
|
25637
|
-
|
|
25638
|
-
|
|
25639
|
-
|
|
25640
|
-
process.env['PROGRAMFILES(X86)'],
|
|
25641
|
-
];
|
|
25642
|
-
for (const prefix of prefixes) {
|
|
25643
|
-
const path = prefix + windowsSuffix;
|
|
25644
|
-
if (await isExecutable(path, $provideFilesystemForNode())) {
|
|
25645
|
-
return path;
|
|
26842
|
+
function createToolFunctionsProxy(getFunctions) {
|
|
26843
|
+
const resolveFunctions = () => getFunctions();
|
|
26844
|
+
return new Proxy({}, {
|
|
26845
|
+
get(_target, prop) {
|
|
26846
|
+
if (typeof prop !== 'string') {
|
|
26847
|
+
return undefined;
|
|
25646
26848
|
}
|
|
25647
|
-
|
|
25648
|
-
|
|
25649
|
-
|
|
25650
|
-
|
|
25651
|
-
|
|
25652
|
-
|
|
25653
|
-
|
|
26849
|
+
return resolveFunctions()[prop];
|
|
26850
|
+
},
|
|
26851
|
+
has(_target, prop) {
|
|
26852
|
+
if (typeof prop !== 'string') {
|
|
26853
|
+
return false;
|
|
26854
|
+
}
|
|
26855
|
+
return prop in resolveFunctions();
|
|
26856
|
+
},
|
|
26857
|
+
ownKeys() {
|
|
26858
|
+
return Object.keys(resolveFunctions());
|
|
26859
|
+
},
|
|
26860
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
26861
|
+
if (typeof prop !== 'string') {
|
|
26862
|
+
return undefined;
|
|
26863
|
+
}
|
|
26864
|
+
const value = resolveFunctions()[prop];
|
|
26865
|
+
if (value === undefined) {
|
|
26866
|
+
return undefined;
|
|
26867
|
+
}
|
|
26868
|
+
return {
|
|
26869
|
+
enumerable: true,
|
|
26870
|
+
configurable: true,
|
|
26871
|
+
writable: false,
|
|
26872
|
+
value,
|
|
26873
|
+
};
|
|
26874
|
+
},
|
|
26875
|
+
});
|
|
25654
26876
|
}
|
|
25655
26877
|
/**
|
|
25656
|
-
*
|
|
25657
|
-
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
26878
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
25658
26879
|
*/
|
|
25659
26880
|
|
|
26881
|
+
const nodeToolFunctions = {
|
|
26882
|
+
/**
|
|
26883
|
+
* @@@
|
|
26884
|
+
*
|
|
26885
|
+
* Note: [??] This function has implementation both for browser and node, this is the full one for node
|
|
26886
|
+
*/
|
|
26887
|
+
async fetch_url_content(args) {
|
|
26888
|
+
console.log('!!!! [Tool] fetch_url_content called', { args });
|
|
26889
|
+
const { url } = args;
|
|
26890
|
+
return await fetchUrlContent(url);
|
|
26891
|
+
},
|
|
26892
|
+
/**
|
|
26893
|
+
* @@@
|
|
26894
|
+
*
|
|
26895
|
+
* Note: [??] This function has implementation both for browser and node, this is the server one for node
|
|
26896
|
+
*/
|
|
26897
|
+
run_browser: resolveRunBrowserToolForNode(),
|
|
26898
|
+
/**
|
|
26899
|
+
* @@@
|
|
26900
|
+
*
|
|
26901
|
+
* Note: [??] This function has implementation both for browser and node, this is the server one for node
|
|
26902
|
+
*/
|
|
26903
|
+
send_email: resolveSendEmailToolForNode(),
|
|
26904
|
+
/**
|
|
26905
|
+
* @@@
|
|
26906
|
+
*
|
|
26907
|
+
* Note: [??] This function has implementation both for browser and node, this is the server one for node
|
|
26908
|
+
*/
|
|
26909
|
+
spawn_agent: resolveSpawnAgentToolForNode(),
|
|
26910
|
+
// TODO: !!!! Unhardcode, make proper server function register from definitions
|
|
26911
|
+
};
|
|
26912
|
+
const nodeToolFunctionsProxy = createToolFunctionsProxy(() => ({
|
|
26913
|
+
...collectCommitmentToolFunctions(),
|
|
26914
|
+
...nodeToolFunctions,
|
|
26915
|
+
}));
|
|
25660
26916
|
/**
|
|
25661
|
-
*
|
|
26917
|
+
* Gets all function implementations provided by all commitments
|
|
25662
26918
|
*
|
|
25663
|
-
*
|
|
26919
|
+
* Note: This function is intended for server use, there is also equivalent `getAllCommitmentsToolFunctionsForBrowser` for browser use
|
|
26920
|
+
*
|
|
26921
|
+
* @public exported from `@promptbook/node`
|
|
25664
26922
|
*/
|
|
25665
|
-
function
|
|
26923
|
+
function getAllCommitmentsToolFunctionsForNode() {
|
|
25666
26924
|
if (!$isRunningInNode()) {
|
|
25667
|
-
throw new EnvironmentMismatchError(
|
|
25668
|
-
|
|
25669
|
-
|
|
25670
|
-
|
|
25671
|
-
|
|
25672
|
-
|
|
25673
|
-
|
|
25674
|
-
else {
|
|
25675
|
-
throw new Error(`${appName} is not available on Windows.`);
|
|
25676
|
-
}
|
|
25677
|
-
}
|
|
25678
|
-
else if (process.platform === 'darwin') {
|
|
25679
|
-
if (macOsName) {
|
|
25680
|
-
return locateAppOnMacOs({ macOsName });
|
|
25681
|
-
}
|
|
25682
|
-
else {
|
|
25683
|
-
throw new Error(`${appName} is not available on macOS.`);
|
|
25684
|
-
}
|
|
25685
|
-
}
|
|
25686
|
-
else {
|
|
25687
|
-
if (linuxWhich) {
|
|
25688
|
-
return locateAppOnLinux({ linuxWhich });
|
|
25689
|
-
}
|
|
25690
|
-
else {
|
|
25691
|
-
throw new Error(`${appName} is not available on Linux.`);
|
|
25692
|
-
}
|
|
26925
|
+
throw new EnvironmentMismatchError(spaceTrim(`
|
|
26926
|
+
Function getAllCommitmentsToolFunctionsForNode should be run in Node.js environment.
|
|
26927
|
+
|
|
26928
|
+
- In browser use getAllCommitmentsToolFunctionsForBrowser instead.
|
|
26929
|
+
- This function can include server-only tools which cannot run in browser environment.
|
|
26930
|
+
|
|
26931
|
+
`));
|
|
25693
26932
|
}
|
|
26933
|
+
return nodeToolFunctionsProxy;
|
|
25694
26934
|
}
|
|
25695
26935
|
/**
|
|
25696
|
-
*
|
|
25697
|
-
*
|
|
26936
|
+
* Note: [??] Code in this file should never be never released in packages that could be imported into browser environment
|
|
26937
|
+
* TODO: [??] Unite `xxxForServer` and `xxxForNode` naming
|
|
25698
26938
|
*/
|
|
25699
26939
|
|
|
25700
26940
|
/**
|
|
@@ -27759,6 +28999,14 @@ function parseAgentSource(agentSource) {
|
|
|
27759
28999
|
});
|
|
27760
29000
|
continue;
|
|
27761
29001
|
}
|
|
29002
|
+
if (commitment.type === 'USE CALENDAR') {
|
|
29003
|
+
capabilities.push({
|
|
29004
|
+
type: 'calendar',
|
|
29005
|
+
label: 'Calendar',
|
|
29006
|
+
iconName: 'Calendar',
|
|
29007
|
+
});
|
|
29008
|
+
continue;
|
|
29009
|
+
}
|
|
27762
29010
|
if (commitment.type === 'FROM') {
|
|
27763
29011
|
const content = spaceTrim$2(commitment.content).split(/\r?\n/)[0] || '';
|
|
27764
29012
|
if (content === 'Adam' || content === '' /* <- Note: Adam is implicit */) {
|