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