@everworker/oneringai 0.3.1 → 0.4.0
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/README.md +173 -22
- package/dist/capabilities/agents/index.cjs +14 -2
- package/dist/capabilities/agents/index.cjs.map +1 -1
- package/dist/capabilities/agents/index.d.cts +1 -1
- package/dist/capabilities/agents/index.d.ts +1 -1
- package/dist/capabilities/agents/index.js +14 -2
- package/dist/capabilities/agents/index.js.map +1 -1
- package/dist/capabilities/images/index.cjs.map +1 -1
- package/dist/capabilities/images/index.js.map +1 -1
- package/dist/{index-WlQwiNF8.d.cts → index-CR5PHkck.d.cts} +6 -1
- package/dist/{index-BeZcWDiq.d.ts → index-Cb7N9QIj.d.ts} +6 -1
- package/dist/index.cjs +2807 -619
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1620 -773
- package/dist/index.d.ts +1620 -773
- package/dist/index.js +2720 -553
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var crypto2 = require('crypto');
|
|
4
4
|
var jose = require('jose');
|
|
5
|
-
var
|
|
5
|
+
var fs19 = require('fs');
|
|
6
6
|
var eventemitter3 = require('eventemitter3');
|
|
7
7
|
var path2 = require('path');
|
|
8
8
|
var TurndownService = require('turndown');
|
|
@@ -17,7 +17,7 @@ var z = require('zod/v4');
|
|
|
17
17
|
var spawn = require('cross-spawn');
|
|
18
18
|
var process2 = require('process');
|
|
19
19
|
var stream = require('stream');
|
|
20
|
-
var
|
|
20
|
+
var fs18 = require('fs/promises');
|
|
21
21
|
var simpleIcons = require('simple-icons');
|
|
22
22
|
var child_process = require('child_process');
|
|
23
23
|
var util = require('util');
|
|
@@ -45,7 +45,7 @@ function _interopNamespace(e) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
var crypto2__namespace = /*#__PURE__*/_interopNamespace(crypto2);
|
|
48
|
-
var
|
|
48
|
+
var fs19__namespace = /*#__PURE__*/_interopNamespace(fs19);
|
|
49
49
|
var path2__namespace = /*#__PURE__*/_interopNamespace(path2);
|
|
50
50
|
var TurndownService__default = /*#__PURE__*/_interopDefault(TurndownService);
|
|
51
51
|
var os2__namespace = /*#__PURE__*/_interopNamespace(os2);
|
|
@@ -55,7 +55,7 @@ var z4mini__namespace = /*#__PURE__*/_interopNamespace(z4mini);
|
|
|
55
55
|
var z__namespace = /*#__PURE__*/_interopNamespace(z);
|
|
56
56
|
var spawn__default = /*#__PURE__*/_interopDefault(spawn);
|
|
57
57
|
var process2__default = /*#__PURE__*/_interopDefault(process2);
|
|
58
|
-
var
|
|
58
|
+
var fs18__namespace = /*#__PURE__*/_interopNamespace(fs18);
|
|
59
59
|
var simpleIcons__namespace = /*#__PURE__*/_interopNamespace(simpleIcons);
|
|
60
60
|
var vm__namespace = /*#__PURE__*/_interopNamespace(vm);
|
|
61
61
|
|
|
@@ -673,7 +673,7 @@ var init_JWTBearer = __esm({
|
|
|
673
673
|
this.privateKey = config.privateKey;
|
|
674
674
|
} else if (config.privateKeyPath) {
|
|
675
675
|
try {
|
|
676
|
-
this.privateKey =
|
|
676
|
+
this.privateKey = fs19__namespace.readFileSync(config.privateKeyPath, "utf8");
|
|
677
677
|
} catch (error) {
|
|
678
678
|
throw new Error(`Failed to read private key from ${config.privateKeyPath}: ${error.message}`);
|
|
679
679
|
}
|
|
@@ -1432,10 +1432,10 @@ var init_Logger = __esm({
|
|
|
1432
1432
|
initFileStream(filePath) {
|
|
1433
1433
|
try {
|
|
1434
1434
|
const dir = path2__namespace.dirname(filePath);
|
|
1435
|
-
if (!
|
|
1436
|
-
|
|
1435
|
+
if (!fs19__namespace.existsSync(dir)) {
|
|
1436
|
+
fs19__namespace.mkdirSync(dir, { recursive: true });
|
|
1437
1437
|
}
|
|
1438
|
-
this.fileStream =
|
|
1438
|
+
this.fileStream = fs19__namespace.createWriteStream(filePath, {
|
|
1439
1439
|
flags: "a",
|
|
1440
1440
|
// append mode
|
|
1441
1441
|
encoding: "utf8"
|
|
@@ -9165,12 +9165,12 @@ var require_dist = __commonJS({
|
|
|
9165
9165
|
throw new Error(`Unknown format "${name}"`);
|
|
9166
9166
|
return f;
|
|
9167
9167
|
};
|
|
9168
|
-
function addFormats(ajv, list,
|
|
9168
|
+
function addFormats(ajv, list, fs20, exportName) {
|
|
9169
9169
|
var _a;
|
|
9170
9170
|
var _b;
|
|
9171
9171
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
9172
9172
|
for (const f of list)
|
|
9173
|
-
ajv.addFormat(f,
|
|
9173
|
+
ajv.addFormat(f, fs20[f]);
|
|
9174
9174
|
}
|
|
9175
9175
|
module.exports = exports$1 = formatsPlugin;
|
|
9176
9176
|
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
@@ -10173,6 +10173,11 @@ var DEFAULT_ALLOWLIST = [
|
|
|
10173
10173
|
"instructions_remove",
|
|
10174
10174
|
"instructions_list",
|
|
10175
10175
|
"instructions_clear",
|
|
10176
|
+
// User info tools (user-specific data - safe)
|
|
10177
|
+
"user_info_set",
|
|
10178
|
+
"user_info_get",
|
|
10179
|
+
"user_info_remove",
|
|
10180
|
+
"user_info_clear",
|
|
10176
10181
|
// Meta-tools (internal coordination)
|
|
10177
10182
|
"_start_planning",
|
|
10178
10183
|
"_modify_plan",
|
|
@@ -14897,7 +14902,7 @@ var FilePersistentInstructionsStorage = class {
|
|
|
14897
14902
|
*/
|
|
14898
14903
|
async load() {
|
|
14899
14904
|
try {
|
|
14900
|
-
const raw = await
|
|
14905
|
+
const raw = await fs19.promises.readFile(this.filePath, "utf-8");
|
|
14901
14906
|
const data = JSON.parse(raw);
|
|
14902
14907
|
if (data.version === 2 && Array.isArray(data.entries)) {
|
|
14903
14908
|
return data.entries.length > 0 ? data.entries : null;
|
|
@@ -14909,7 +14914,7 @@ var FilePersistentInstructionsStorage = class {
|
|
|
14909
14914
|
}
|
|
14910
14915
|
}
|
|
14911
14916
|
try {
|
|
14912
|
-
const content = await
|
|
14917
|
+
const content = await fs19.promises.readFile(this.legacyFilePath, "utf-8");
|
|
14913
14918
|
const trimmed = content.trim();
|
|
14914
14919
|
if (!trimmed) return null;
|
|
14915
14920
|
const now = Date.now();
|
|
@@ -14939,11 +14944,11 @@ var FilePersistentInstructionsStorage = class {
|
|
|
14939
14944
|
};
|
|
14940
14945
|
const tempPath = `${this.filePath}.tmp`;
|
|
14941
14946
|
try {
|
|
14942
|
-
await
|
|
14943
|
-
await
|
|
14947
|
+
await fs19.promises.writeFile(tempPath, JSON.stringify(data, null, 2), "utf-8");
|
|
14948
|
+
await fs19.promises.rename(tempPath, this.filePath);
|
|
14944
14949
|
} catch (error) {
|
|
14945
14950
|
try {
|
|
14946
|
-
await
|
|
14951
|
+
await fs19.promises.unlink(tempPath);
|
|
14947
14952
|
} catch {
|
|
14948
14953
|
}
|
|
14949
14954
|
throw error;
|
|
@@ -14955,7 +14960,7 @@ var FilePersistentInstructionsStorage = class {
|
|
|
14955
14960
|
*/
|
|
14956
14961
|
async delete() {
|
|
14957
14962
|
try {
|
|
14958
|
-
await
|
|
14963
|
+
await fs19.promises.unlink(this.filePath);
|
|
14959
14964
|
} catch (error) {
|
|
14960
14965
|
if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
|
|
14961
14966
|
throw error;
|
|
@@ -14968,11 +14973,11 @@ var FilePersistentInstructionsStorage = class {
|
|
|
14968
14973
|
*/
|
|
14969
14974
|
async exists() {
|
|
14970
14975
|
try {
|
|
14971
|
-
await
|
|
14976
|
+
await fs19.promises.access(this.filePath);
|
|
14972
14977
|
return true;
|
|
14973
14978
|
} catch {
|
|
14974
14979
|
try {
|
|
14975
|
-
await
|
|
14980
|
+
await fs19.promises.access(this.legacyFilePath);
|
|
14976
14981
|
return true;
|
|
14977
14982
|
} catch {
|
|
14978
14983
|
return false;
|
|
@@ -14996,7 +15001,7 @@ var FilePersistentInstructionsStorage = class {
|
|
|
14996
15001
|
*/
|
|
14997
15002
|
async ensureDirectory() {
|
|
14998
15003
|
try {
|
|
14999
|
-
await
|
|
15004
|
+
await fs19.promises.mkdir(this.directory, { recursive: true });
|
|
15000
15005
|
} catch (error) {
|
|
15001
15006
|
if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
|
|
15002
15007
|
throw error;
|
|
@@ -15008,7 +15013,7 @@ var FilePersistentInstructionsStorage = class {
|
|
|
15008
15013
|
*/
|
|
15009
15014
|
async removeLegacyFile() {
|
|
15010
15015
|
try {
|
|
15011
|
-
await
|
|
15016
|
+
await fs19.promises.unlink(this.legacyFilePath);
|
|
15012
15017
|
} catch (error) {
|
|
15013
15018
|
if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
|
|
15014
15019
|
console.warn(`Failed to remove legacy instructions file: ${this.legacyFilePath}`);
|
|
@@ -15472,6 +15477,572 @@ ${entry.content}`).join("\n\n");
|
|
|
15472
15477
|
};
|
|
15473
15478
|
}
|
|
15474
15479
|
};
|
|
15480
|
+
function getDefaultBaseDirectory2() {
|
|
15481
|
+
const platform2 = process.platform;
|
|
15482
|
+
if (platform2 === "win32") {
|
|
15483
|
+
const appData = process.env.APPDATA || process.env.LOCALAPPDATA;
|
|
15484
|
+
if (appData) {
|
|
15485
|
+
return path2.join(appData, "oneringai", "users");
|
|
15486
|
+
}
|
|
15487
|
+
}
|
|
15488
|
+
return path2.join(os2.homedir(), ".oneringai", "users");
|
|
15489
|
+
}
|
|
15490
|
+
var DEFAULT_USER_ID = "default";
|
|
15491
|
+
function sanitizeUserId(userId) {
|
|
15492
|
+
if (!userId) return DEFAULT_USER_ID;
|
|
15493
|
+
return userId.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase() || DEFAULT_USER_ID;
|
|
15494
|
+
}
|
|
15495
|
+
var FileUserInfoStorage = class {
|
|
15496
|
+
baseDirectory;
|
|
15497
|
+
filename;
|
|
15498
|
+
constructor(config) {
|
|
15499
|
+
this.baseDirectory = config?.baseDirectory ?? getDefaultBaseDirectory2();
|
|
15500
|
+
this.filename = config?.filename ?? "user_info.json";
|
|
15501
|
+
}
|
|
15502
|
+
/**
|
|
15503
|
+
* Get the directory path for a specific user
|
|
15504
|
+
*/
|
|
15505
|
+
getUserDirectory(userId) {
|
|
15506
|
+
const sanitizedId = sanitizeUserId(userId);
|
|
15507
|
+
return path2.join(this.baseDirectory, sanitizedId);
|
|
15508
|
+
}
|
|
15509
|
+
/**
|
|
15510
|
+
* Get the file path for a specific user
|
|
15511
|
+
*/
|
|
15512
|
+
getUserFilePath(userId) {
|
|
15513
|
+
return path2.join(this.getUserDirectory(userId), this.filename);
|
|
15514
|
+
}
|
|
15515
|
+
/**
|
|
15516
|
+
* Load user info entries from file for a specific user
|
|
15517
|
+
*/
|
|
15518
|
+
async load(userId) {
|
|
15519
|
+
const filePath = this.getUserFilePath(userId);
|
|
15520
|
+
try {
|
|
15521
|
+
const raw = await fs19.promises.readFile(filePath, "utf-8");
|
|
15522
|
+
const data = JSON.parse(raw);
|
|
15523
|
+
if (data.version === 1 && Array.isArray(data.entries)) {
|
|
15524
|
+
return data.entries.length > 0 ? data.entries : null;
|
|
15525
|
+
}
|
|
15526
|
+
return null;
|
|
15527
|
+
} catch (error) {
|
|
15528
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) {
|
|
15529
|
+
throw error;
|
|
15530
|
+
}
|
|
15531
|
+
return null;
|
|
15532
|
+
}
|
|
15533
|
+
}
|
|
15534
|
+
/**
|
|
15535
|
+
* Save user info entries to file for a specific user
|
|
15536
|
+
* Creates directory if it doesn't exist.
|
|
15537
|
+
*/
|
|
15538
|
+
async save(userId, entries) {
|
|
15539
|
+
const directory = this.getUserDirectory(userId);
|
|
15540
|
+
const filePath = this.getUserFilePath(userId);
|
|
15541
|
+
await this.ensureDirectory(directory);
|
|
15542
|
+
const data = {
|
|
15543
|
+
version: 1,
|
|
15544
|
+
userId: userId || DEFAULT_USER_ID,
|
|
15545
|
+
entries
|
|
15546
|
+
};
|
|
15547
|
+
const tempPath = `${filePath}.tmp`;
|
|
15548
|
+
try {
|
|
15549
|
+
await fs19.promises.writeFile(tempPath, JSON.stringify(data, null, 2), "utf-8");
|
|
15550
|
+
await fs19.promises.rename(tempPath, filePath);
|
|
15551
|
+
} catch (error) {
|
|
15552
|
+
try {
|
|
15553
|
+
await fs19.promises.unlink(tempPath);
|
|
15554
|
+
} catch {
|
|
15555
|
+
}
|
|
15556
|
+
throw error;
|
|
15557
|
+
}
|
|
15558
|
+
}
|
|
15559
|
+
/**
|
|
15560
|
+
* Delete user info file for a specific user
|
|
15561
|
+
*/
|
|
15562
|
+
async delete(userId) {
|
|
15563
|
+
const filePath = this.getUserFilePath(userId);
|
|
15564
|
+
try {
|
|
15565
|
+
await fs19.promises.unlink(filePath);
|
|
15566
|
+
} catch (error) {
|
|
15567
|
+
if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
|
|
15568
|
+
throw error;
|
|
15569
|
+
}
|
|
15570
|
+
}
|
|
15571
|
+
}
|
|
15572
|
+
/**
|
|
15573
|
+
* Check if user info file exists for a specific user
|
|
15574
|
+
*/
|
|
15575
|
+
async exists(userId) {
|
|
15576
|
+
const filePath = this.getUserFilePath(userId);
|
|
15577
|
+
try {
|
|
15578
|
+
await fs19.promises.access(filePath);
|
|
15579
|
+
return true;
|
|
15580
|
+
} catch {
|
|
15581
|
+
return false;
|
|
15582
|
+
}
|
|
15583
|
+
}
|
|
15584
|
+
/**
|
|
15585
|
+
* Get the file path for a specific user (for display/debugging)
|
|
15586
|
+
*/
|
|
15587
|
+
getPath(userId) {
|
|
15588
|
+
return this.getUserFilePath(userId);
|
|
15589
|
+
}
|
|
15590
|
+
/**
|
|
15591
|
+
* Ensure the directory exists
|
|
15592
|
+
*/
|
|
15593
|
+
async ensureDirectory(directory) {
|
|
15594
|
+
try {
|
|
15595
|
+
await fs19.promises.mkdir(directory, { recursive: true });
|
|
15596
|
+
} catch (error) {
|
|
15597
|
+
if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
|
|
15598
|
+
throw error;
|
|
15599
|
+
}
|
|
15600
|
+
}
|
|
15601
|
+
}
|
|
15602
|
+
};
|
|
15603
|
+
|
|
15604
|
+
// src/core/context-nextgen/plugins/UserInfoPluginNextGen.ts
|
|
15605
|
+
init_StorageRegistry();
|
|
15606
|
+
var DEFAULT_MAX_TOTAL_SIZE = 1e5;
|
|
15607
|
+
var DEFAULT_MAX_ENTRIES2 = 100;
|
|
15608
|
+
var KEY_MAX_LENGTH2 = 100;
|
|
15609
|
+
var KEY_PATTERN2 = /^[a-zA-Z0-9_-]+$/;
|
|
15610
|
+
var USER_INFO_INSTRUCTIONS = `User Info stores key-value information about the current user.
|
|
15611
|
+
Data is user-specific and persists across sessions and agents.
|
|
15612
|
+
User info is automatically shown in context \u2014 no need to call user_info_get every turn.
|
|
15613
|
+
|
|
15614
|
+
**To manage:**
|
|
15615
|
+
- \`user_info_set(key, value, description?)\`: Store/update user information
|
|
15616
|
+
- \`user_info_get(key?)\`: Retrieve one entry by key, or all entries if no key
|
|
15617
|
+
- \`user_info_remove(key)\`: Remove a specific entry
|
|
15618
|
+
- \`user_info_clear(confirm: true)\`: Remove all entries (destructive!)
|
|
15619
|
+
|
|
15620
|
+
**Use for:** User preferences, context, metadata (theme, language, timezone, role, etc.) It is also perfectly fine to search the web and other external sources for information about the user and then store it in user info for future use.
|
|
15621
|
+
|
|
15622
|
+
**Important:** Do not store sensitive information (passwords, tokens, PII) in user info. It is not encrypted and may be accessible to other parts of the system. Always follow best practices for security.
|
|
15623
|
+
|
|
15624
|
+
**Rules after each user message:** If the user provides new information about themselves, update user info accordingly. If they ask to change or remove existing information, do that as well. Always keep user info up to date with the latest information provided by the user. Learn about the user proactively!`;
|
|
15625
|
+
var userInfoSetDefinition = {
|
|
15626
|
+
type: "function",
|
|
15627
|
+
function: {
|
|
15628
|
+
name: "user_info_set",
|
|
15629
|
+
description: `Store or update user information by key. Data persists across sessions.
|
|
15630
|
+
If the key exists, it will be updated. If not, a new entry is created.`,
|
|
15631
|
+
parameters: {
|
|
15632
|
+
type: "object",
|
|
15633
|
+
properties: {
|
|
15634
|
+
key: {
|
|
15635
|
+
type: "string",
|
|
15636
|
+
description: "Unique key for the information (alphanumeric, dash, underscore; max 100 chars)"
|
|
15637
|
+
},
|
|
15638
|
+
value: {
|
|
15639
|
+
description: "Value to store (any JSON-serializable data: string, number, boolean, object, array)"
|
|
15640
|
+
},
|
|
15641
|
+
description: {
|
|
15642
|
+
type: "string",
|
|
15643
|
+
description: "Optional description for self-documentation"
|
|
15644
|
+
}
|
|
15645
|
+
},
|
|
15646
|
+
required: ["key", "value"]
|
|
15647
|
+
}
|
|
15648
|
+
}
|
|
15649
|
+
};
|
|
15650
|
+
var userInfoGetDefinition = {
|
|
15651
|
+
type: "function",
|
|
15652
|
+
function: {
|
|
15653
|
+
name: "user_info_get",
|
|
15654
|
+
description: "Retrieve user information. If key is provided, returns that entry. Otherwise returns all entries.",
|
|
15655
|
+
parameters: {
|
|
15656
|
+
type: "object",
|
|
15657
|
+
properties: {
|
|
15658
|
+
key: {
|
|
15659
|
+
type: "string",
|
|
15660
|
+
description: "Key of the entry to retrieve (optional - omit to get all entries)"
|
|
15661
|
+
}
|
|
15662
|
+
},
|
|
15663
|
+
required: []
|
|
15664
|
+
}
|
|
15665
|
+
}
|
|
15666
|
+
};
|
|
15667
|
+
var userInfoRemoveDefinition = {
|
|
15668
|
+
type: "function",
|
|
15669
|
+
function: {
|
|
15670
|
+
name: "user_info_remove",
|
|
15671
|
+
description: "Remove a specific user information entry by key.",
|
|
15672
|
+
parameters: {
|
|
15673
|
+
type: "object",
|
|
15674
|
+
properties: {
|
|
15675
|
+
key: {
|
|
15676
|
+
type: "string",
|
|
15677
|
+
description: "Key of the entry to remove"
|
|
15678
|
+
}
|
|
15679
|
+
},
|
|
15680
|
+
required: ["key"]
|
|
15681
|
+
}
|
|
15682
|
+
}
|
|
15683
|
+
};
|
|
15684
|
+
var userInfoClearDefinition = {
|
|
15685
|
+
type: "function",
|
|
15686
|
+
function: {
|
|
15687
|
+
name: "user_info_clear",
|
|
15688
|
+
description: "Clear all user information entries (DESTRUCTIVE). Requires confirmation.",
|
|
15689
|
+
parameters: {
|
|
15690
|
+
type: "object",
|
|
15691
|
+
properties: {
|
|
15692
|
+
confirm: {
|
|
15693
|
+
type: "boolean",
|
|
15694
|
+
description: "Must be true to confirm deletion"
|
|
15695
|
+
}
|
|
15696
|
+
},
|
|
15697
|
+
required: ["confirm"]
|
|
15698
|
+
}
|
|
15699
|
+
}
|
|
15700
|
+
};
|
|
15701
|
+
function validateKey2(key) {
|
|
15702
|
+
if (typeof key !== "string") return "Key must be a string";
|
|
15703
|
+
const trimmed = key.trim();
|
|
15704
|
+
if (trimmed.length === 0) return "Key cannot be empty";
|
|
15705
|
+
if (trimmed.length > KEY_MAX_LENGTH2) return `Key exceeds maximum length (${KEY_MAX_LENGTH2} chars)`;
|
|
15706
|
+
if (!KEY_PATTERN2.test(trimmed)) return "Key must contain only alphanumeric characters, dashes, and underscores";
|
|
15707
|
+
return null;
|
|
15708
|
+
}
|
|
15709
|
+
function getValueType(value) {
|
|
15710
|
+
if (value === null) return "null";
|
|
15711
|
+
if (Array.isArray(value)) return "array";
|
|
15712
|
+
return typeof value;
|
|
15713
|
+
}
|
|
15714
|
+
function calculateValueSize(value) {
|
|
15715
|
+
const json = JSON.stringify(value);
|
|
15716
|
+
return Buffer.byteLength(json, "utf-8");
|
|
15717
|
+
}
|
|
15718
|
+
function buildStorageContext(toolContext) {
|
|
15719
|
+
const global2 = exports.StorageRegistry.getContext();
|
|
15720
|
+
if (global2) return global2;
|
|
15721
|
+
if (toolContext?.userId) return { userId: toolContext.userId };
|
|
15722
|
+
return void 0;
|
|
15723
|
+
}
|
|
15724
|
+
function formatValue(value) {
|
|
15725
|
+
if (value === null) return "null";
|
|
15726
|
+
if (typeof value === "string") return value;
|
|
15727
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
15728
|
+
return JSON.stringify(value);
|
|
15729
|
+
}
|
|
15730
|
+
var UserInfoPluginNextGen = class {
|
|
15731
|
+
name = "user_info";
|
|
15732
|
+
_destroyed = false;
|
|
15733
|
+
_storage = null;
|
|
15734
|
+
/** In-memory cache of entries */
|
|
15735
|
+
_entries = /* @__PURE__ */ new Map();
|
|
15736
|
+
/** Whether entries have been loaded from storage */
|
|
15737
|
+
_initialized = false;
|
|
15738
|
+
maxTotalSize;
|
|
15739
|
+
maxEntries;
|
|
15740
|
+
estimator = simpleTokenEstimator;
|
|
15741
|
+
explicitStorage;
|
|
15742
|
+
/** UserId for getContent() and lazy initialization */
|
|
15743
|
+
userId;
|
|
15744
|
+
_tokenCache = null;
|
|
15745
|
+
_instructionsTokenCache = null;
|
|
15746
|
+
constructor(config) {
|
|
15747
|
+
this.maxTotalSize = config?.maxTotalSize ?? DEFAULT_MAX_TOTAL_SIZE;
|
|
15748
|
+
this.maxEntries = config?.maxEntries ?? DEFAULT_MAX_ENTRIES2;
|
|
15749
|
+
this.explicitStorage = config?.storage;
|
|
15750
|
+
this.userId = config?.userId;
|
|
15751
|
+
}
|
|
15752
|
+
// ============================================================================
|
|
15753
|
+
// IContextPluginNextGen Implementation
|
|
15754
|
+
// ============================================================================
|
|
15755
|
+
getInstructions() {
|
|
15756
|
+
return USER_INFO_INSTRUCTIONS;
|
|
15757
|
+
}
|
|
15758
|
+
async getContent() {
|
|
15759
|
+
await this.ensureInitialized();
|
|
15760
|
+
if (this._entries.size === 0) {
|
|
15761
|
+
this._tokenCache = 0;
|
|
15762
|
+
return null;
|
|
15763
|
+
}
|
|
15764
|
+
const rendered = this.renderContent();
|
|
15765
|
+
this._tokenCache = this.estimator.estimateTokens(rendered);
|
|
15766
|
+
return rendered;
|
|
15767
|
+
}
|
|
15768
|
+
getContents() {
|
|
15769
|
+
return new Map(this._entries);
|
|
15770
|
+
}
|
|
15771
|
+
getTokenSize() {
|
|
15772
|
+
return this._tokenCache ?? 0;
|
|
15773
|
+
}
|
|
15774
|
+
getInstructionsTokenSize() {
|
|
15775
|
+
if (this._instructionsTokenCache === null) {
|
|
15776
|
+
this._instructionsTokenCache = this.estimator.estimateTokens(USER_INFO_INSTRUCTIONS);
|
|
15777
|
+
}
|
|
15778
|
+
return this._instructionsTokenCache;
|
|
15779
|
+
}
|
|
15780
|
+
isCompactable() {
|
|
15781
|
+
return false;
|
|
15782
|
+
}
|
|
15783
|
+
async compact(_targetTokensToFree) {
|
|
15784
|
+
return 0;
|
|
15785
|
+
}
|
|
15786
|
+
getTools() {
|
|
15787
|
+
return [
|
|
15788
|
+
this.createUserInfoSetTool(),
|
|
15789
|
+
this.createUserInfoGetTool(),
|
|
15790
|
+
this.createUserInfoRemoveTool(),
|
|
15791
|
+
this.createUserInfoClearTool()
|
|
15792
|
+
];
|
|
15793
|
+
}
|
|
15794
|
+
destroy() {
|
|
15795
|
+
if (this._destroyed) return;
|
|
15796
|
+
this._entries.clear();
|
|
15797
|
+
this._destroyed = true;
|
|
15798
|
+
this._tokenCache = null;
|
|
15799
|
+
}
|
|
15800
|
+
getState() {
|
|
15801
|
+
return {
|
|
15802
|
+
version: 1,
|
|
15803
|
+
entries: Array.from(this._entries.values()),
|
|
15804
|
+
userId: this.userId
|
|
15805
|
+
};
|
|
15806
|
+
}
|
|
15807
|
+
restoreState(state) {
|
|
15808
|
+
if (!state || typeof state !== "object") return;
|
|
15809
|
+
const s = state;
|
|
15810
|
+
if ("version" in s && s.version === 1 && Array.isArray(s.entries)) {
|
|
15811
|
+
this._entries.clear();
|
|
15812
|
+
for (const entry of s.entries) {
|
|
15813
|
+
this._entries.set(entry.id, entry);
|
|
15814
|
+
}
|
|
15815
|
+
this._initialized = true;
|
|
15816
|
+
this._tokenCache = null;
|
|
15817
|
+
}
|
|
15818
|
+
}
|
|
15819
|
+
// ============================================================================
|
|
15820
|
+
// Public API
|
|
15821
|
+
// ============================================================================
|
|
15822
|
+
/**
|
|
15823
|
+
* Check if initialized
|
|
15824
|
+
*/
|
|
15825
|
+
get isInitialized() {
|
|
15826
|
+
return this._initialized;
|
|
15827
|
+
}
|
|
15828
|
+
// ============================================================================
|
|
15829
|
+
// Private Helpers
|
|
15830
|
+
// ============================================================================
|
|
15831
|
+
assertNotDestroyed() {
|
|
15832
|
+
if (this._destroyed) {
|
|
15833
|
+
throw new Error("UserInfoPluginNextGen is destroyed");
|
|
15834
|
+
}
|
|
15835
|
+
}
|
|
15836
|
+
/**
|
|
15837
|
+
* Lazy load entries from storage
|
|
15838
|
+
*/
|
|
15839
|
+
async ensureInitialized() {
|
|
15840
|
+
if (this._initialized || this._destroyed) return;
|
|
15841
|
+
try {
|
|
15842
|
+
const storage = this.resolveStorage();
|
|
15843
|
+
const entries = await storage.load(this.userId);
|
|
15844
|
+
this._entries.clear();
|
|
15845
|
+
if (entries) {
|
|
15846
|
+
for (const entry of entries) {
|
|
15847
|
+
this._entries.set(entry.id, entry);
|
|
15848
|
+
}
|
|
15849
|
+
}
|
|
15850
|
+
this._initialized = true;
|
|
15851
|
+
} catch (error) {
|
|
15852
|
+
console.warn(`Failed to load user info for userId '${this.userId ?? "default"}':`, error);
|
|
15853
|
+
this._entries.clear();
|
|
15854
|
+
this._initialized = true;
|
|
15855
|
+
}
|
|
15856
|
+
this._tokenCache = null;
|
|
15857
|
+
}
|
|
15858
|
+
/**
|
|
15859
|
+
* Render entries as markdown for context injection
|
|
15860
|
+
*/
|
|
15861
|
+
renderContent() {
|
|
15862
|
+
const sorted = Array.from(this._entries.values()).sort((a, b) => a.createdAt - b.createdAt);
|
|
15863
|
+
return sorted.map((entry) => `### ${entry.id}
|
|
15864
|
+
${formatValue(entry.value)}`).join("\n\n");
|
|
15865
|
+
}
|
|
15866
|
+
/**
|
|
15867
|
+
* Resolve storage instance (lazy singleton)
|
|
15868
|
+
*/
|
|
15869
|
+
resolveStorage(context) {
|
|
15870
|
+
if (this._storage) return this._storage;
|
|
15871
|
+
if (this.explicitStorage) {
|
|
15872
|
+
this._storage = this.explicitStorage;
|
|
15873
|
+
return this._storage;
|
|
15874
|
+
}
|
|
15875
|
+
const factory = exports.StorageRegistry.get("userInfo");
|
|
15876
|
+
if (factory) {
|
|
15877
|
+
this._storage = factory(buildStorageContext(context));
|
|
15878
|
+
return this._storage;
|
|
15879
|
+
}
|
|
15880
|
+
this._storage = new FileUserInfoStorage();
|
|
15881
|
+
return this._storage;
|
|
15882
|
+
}
|
|
15883
|
+
/**
|
|
15884
|
+
* Persist current entries to storage
|
|
15885
|
+
*/
|
|
15886
|
+
async persistToStorage(userId) {
|
|
15887
|
+
const storage = this.resolveStorage();
|
|
15888
|
+
if (this._entries.size === 0) {
|
|
15889
|
+
await storage.delete(userId);
|
|
15890
|
+
} else {
|
|
15891
|
+
await storage.save(userId, Array.from(this._entries.values()));
|
|
15892
|
+
}
|
|
15893
|
+
}
|
|
15894
|
+
// ============================================================================
|
|
15895
|
+
// Tool Factories
|
|
15896
|
+
// ============================================================================
|
|
15897
|
+
createUserInfoSetTool() {
|
|
15898
|
+
return {
|
|
15899
|
+
definition: userInfoSetDefinition,
|
|
15900
|
+
execute: async (args, context) => {
|
|
15901
|
+
this.assertNotDestroyed();
|
|
15902
|
+
await this.ensureInitialized();
|
|
15903
|
+
const userId = context?.userId ?? this.userId;
|
|
15904
|
+
const key = args.key;
|
|
15905
|
+
const value = args.value;
|
|
15906
|
+
const description = args.description;
|
|
15907
|
+
const keyError = validateKey2(key);
|
|
15908
|
+
if (keyError) {
|
|
15909
|
+
return { error: keyError };
|
|
15910
|
+
}
|
|
15911
|
+
const trimmedKey = key.trim();
|
|
15912
|
+
if (value === void 0) {
|
|
15913
|
+
return { error: "Value cannot be undefined. Use null for explicit null value." };
|
|
15914
|
+
}
|
|
15915
|
+
if (!this._entries.has(trimmedKey) && this._entries.size >= this.maxEntries) {
|
|
15916
|
+
return { error: `Maximum number of entries reached (${this.maxEntries})` };
|
|
15917
|
+
}
|
|
15918
|
+
const valueSize = calculateValueSize(value);
|
|
15919
|
+
let currentTotal = 0;
|
|
15920
|
+
for (const e of this._entries.values()) {
|
|
15921
|
+
currentTotal += calculateValueSize(e.value);
|
|
15922
|
+
}
|
|
15923
|
+
const existingSize = this._entries.has(trimmedKey) ? calculateValueSize(this._entries.get(trimmedKey).value) : 0;
|
|
15924
|
+
const newTotal = currentTotal - existingSize + valueSize;
|
|
15925
|
+
if (newTotal > this.maxTotalSize) {
|
|
15926
|
+
return { error: `Total size would exceed maximum (${this.maxTotalSize} bytes)` };
|
|
15927
|
+
}
|
|
15928
|
+
const now = Date.now();
|
|
15929
|
+
const existing = this._entries.get(trimmedKey);
|
|
15930
|
+
const entry = {
|
|
15931
|
+
id: trimmedKey,
|
|
15932
|
+
value,
|
|
15933
|
+
valueType: getValueType(value),
|
|
15934
|
+
description,
|
|
15935
|
+
createdAt: existing?.createdAt ?? now,
|
|
15936
|
+
updatedAt: now
|
|
15937
|
+
};
|
|
15938
|
+
this._entries.set(trimmedKey, entry);
|
|
15939
|
+
this._tokenCache = null;
|
|
15940
|
+
await this.persistToStorage(userId);
|
|
15941
|
+
return {
|
|
15942
|
+
success: true,
|
|
15943
|
+
message: existing ? `User info '${trimmedKey}' updated` : `User info '${trimmedKey}' added`,
|
|
15944
|
+
key: trimmedKey,
|
|
15945
|
+
valueType: entry.valueType,
|
|
15946
|
+
valueSize
|
|
15947
|
+
};
|
|
15948
|
+
},
|
|
15949
|
+
permission: { scope: "always", riskLevel: "low" },
|
|
15950
|
+
describeCall: (args) => `set user info '${args.key}'`
|
|
15951
|
+
};
|
|
15952
|
+
}
|
|
15953
|
+
createUserInfoGetTool() {
|
|
15954
|
+
return {
|
|
15955
|
+
definition: userInfoGetDefinition,
|
|
15956
|
+
execute: async (args, _context) => {
|
|
15957
|
+
this.assertNotDestroyed();
|
|
15958
|
+
await this.ensureInitialized();
|
|
15959
|
+
const key = args.key;
|
|
15960
|
+
if (this._entries.size === 0) {
|
|
15961
|
+
return { error: "User info not found" };
|
|
15962
|
+
}
|
|
15963
|
+
if (key !== void 0) {
|
|
15964
|
+
const trimmedKey = key.trim();
|
|
15965
|
+
const entry = this._entries.get(trimmedKey);
|
|
15966
|
+
if (!entry) {
|
|
15967
|
+
return { error: `User info '${trimmedKey}' not found` };
|
|
15968
|
+
}
|
|
15969
|
+
return {
|
|
15970
|
+
key: entry.id,
|
|
15971
|
+
value: entry.value,
|
|
15972
|
+
valueType: entry.valueType,
|
|
15973
|
+
description: entry.description,
|
|
15974
|
+
createdAt: entry.createdAt,
|
|
15975
|
+
updatedAt: entry.updatedAt
|
|
15976
|
+
};
|
|
15977
|
+
}
|
|
15978
|
+
const entries = Array.from(this._entries.values());
|
|
15979
|
+
return {
|
|
15980
|
+
count: entries.length,
|
|
15981
|
+
entries: entries.map((e) => ({
|
|
15982
|
+
key: e.id,
|
|
15983
|
+
value: e.value,
|
|
15984
|
+
valueType: e.valueType,
|
|
15985
|
+
description: e.description,
|
|
15986
|
+
createdAt: e.createdAt,
|
|
15987
|
+
updatedAt: e.updatedAt
|
|
15988
|
+
}))
|
|
15989
|
+
};
|
|
15990
|
+
},
|
|
15991
|
+
permission: { scope: "always", riskLevel: "low" },
|
|
15992
|
+
describeCall: (args) => args.key ? `get user info '${args.key}'` : "get all user info"
|
|
15993
|
+
};
|
|
15994
|
+
}
|
|
15995
|
+
createUserInfoRemoveTool() {
|
|
15996
|
+
return {
|
|
15997
|
+
definition: userInfoRemoveDefinition,
|
|
15998
|
+
execute: async (args, context) => {
|
|
15999
|
+
this.assertNotDestroyed();
|
|
16000
|
+
await this.ensureInitialized();
|
|
16001
|
+
const userId = context?.userId ?? this.userId;
|
|
16002
|
+
const key = args.key;
|
|
16003
|
+
if (!key || typeof key !== "string" || key.trim().length === 0) {
|
|
16004
|
+
return { error: "Key is required" };
|
|
16005
|
+
}
|
|
16006
|
+
const trimmedKey = key.trim();
|
|
16007
|
+
if (!this._entries.has(trimmedKey)) {
|
|
16008
|
+
return { error: `User info '${trimmedKey}' not found` };
|
|
16009
|
+
}
|
|
16010
|
+
this._entries.delete(trimmedKey);
|
|
16011
|
+
this._tokenCache = null;
|
|
16012
|
+
await this.persistToStorage(userId);
|
|
16013
|
+
return {
|
|
16014
|
+
success: true,
|
|
16015
|
+
message: `User info '${trimmedKey}' removed`,
|
|
16016
|
+
key: trimmedKey
|
|
16017
|
+
};
|
|
16018
|
+
},
|
|
16019
|
+
permission: { scope: "always", riskLevel: "low" },
|
|
16020
|
+
describeCall: (args) => `remove user info '${args.key}'`
|
|
16021
|
+
};
|
|
16022
|
+
}
|
|
16023
|
+
createUserInfoClearTool() {
|
|
16024
|
+
return {
|
|
16025
|
+
definition: userInfoClearDefinition,
|
|
16026
|
+
execute: async (args, context) => {
|
|
16027
|
+
this.assertNotDestroyed();
|
|
16028
|
+
const userId = context?.userId ?? this.userId;
|
|
16029
|
+
if (args.confirm !== true) {
|
|
16030
|
+
return { error: "Must pass confirm: true to clear user info" };
|
|
16031
|
+
}
|
|
16032
|
+
this._entries.clear();
|
|
16033
|
+
this._tokenCache = null;
|
|
16034
|
+
const storage = this.resolveStorage(context);
|
|
16035
|
+
await storage.delete(userId);
|
|
16036
|
+
return {
|
|
16037
|
+
success: true,
|
|
16038
|
+
message: "All user information cleared"
|
|
16039
|
+
};
|
|
16040
|
+
},
|
|
16041
|
+
permission: { scope: "once", riskLevel: "medium" },
|
|
16042
|
+
describeCall: () => "clear user info"
|
|
16043
|
+
};
|
|
16044
|
+
}
|
|
16045
|
+
};
|
|
15475
16046
|
|
|
15476
16047
|
// src/core/context-nextgen/AgentContextNextGen.ts
|
|
15477
16048
|
init_StorageRegistry();
|
|
@@ -16120,7 +16691,8 @@ var StrategyRegistry = class {
|
|
|
16120
16691
|
var DEFAULT_FEATURES = {
|
|
16121
16692
|
workingMemory: true,
|
|
16122
16693
|
inContextMemory: false,
|
|
16123
|
-
persistentInstructions: false
|
|
16694
|
+
persistentInstructions: false,
|
|
16695
|
+
userInfo: false
|
|
16124
16696
|
};
|
|
16125
16697
|
var DEFAULT_CONFIG2 = {
|
|
16126
16698
|
responseReserve: 4096,
|
|
@@ -16238,6 +16810,13 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
|
|
|
16238
16810
|
...piConfig
|
|
16239
16811
|
}));
|
|
16240
16812
|
}
|
|
16813
|
+
if (features.userInfo) {
|
|
16814
|
+
const uiConfig = configs.userInfo;
|
|
16815
|
+
this.registerPlugin(new UserInfoPluginNextGen({
|
|
16816
|
+
userId: this._userId,
|
|
16817
|
+
...uiConfig
|
|
16818
|
+
}));
|
|
16819
|
+
}
|
|
16241
16820
|
this.validateStrategyDependencies(this._compactionStrategy);
|
|
16242
16821
|
}
|
|
16243
16822
|
/**
|
|
@@ -16791,6 +17370,8 @@ ${content}`);
|
|
|
16791
17370
|
breakdown.pluginContents[plugin.name] = plugin.getTokenSize();
|
|
16792
17371
|
}
|
|
16793
17372
|
}
|
|
17373
|
+
const now = /* @__PURE__ */ new Date();
|
|
17374
|
+
parts.push(`CURRENT DATE AND TIME: ${now.toLocaleString("en-US", { dateStyle: "full", timeStyle: "long", timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone })}`);
|
|
16794
17375
|
const systemText = parts.join("\n\n---\n\n");
|
|
16795
17376
|
const systemTokens = this._estimator.estimateTokens(systemText);
|
|
16796
17377
|
const systemMessage = {
|
|
@@ -20249,6 +20830,7 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
|
|
|
20249
20830
|
_agentContext;
|
|
20250
20831
|
// SINGLE SOURCE OF TRUTH for tools and sessions
|
|
20251
20832
|
_permissionManager;
|
|
20833
|
+
_ownsContext = true;
|
|
20252
20834
|
_isDestroyed = false;
|
|
20253
20835
|
_cleanupCallbacks = [];
|
|
20254
20836
|
_logger;
|
|
@@ -20301,8 +20883,10 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
|
|
|
20301
20883
|
*/
|
|
20302
20884
|
initializeAgentContext(config) {
|
|
20303
20885
|
if (config.context instanceof AgentContextNextGen) {
|
|
20886
|
+
this._ownsContext = false;
|
|
20304
20887
|
return config.context;
|
|
20305
20888
|
}
|
|
20889
|
+
this._ownsContext = true;
|
|
20306
20890
|
const contextConfig = {
|
|
20307
20891
|
model: config.model,
|
|
20308
20892
|
agentId: config.name,
|
|
@@ -20814,7 +21398,9 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
|
|
|
20814
21398
|
clearInterval(this._autoSaveInterval);
|
|
20815
21399
|
this._autoSaveInterval = null;
|
|
20816
21400
|
}
|
|
20817
|
-
this.
|
|
21401
|
+
if (this._ownsContext) {
|
|
21402
|
+
this._agentContext.destroy();
|
|
21403
|
+
}
|
|
20818
21404
|
this._permissionManager.removeAllListeners();
|
|
20819
21405
|
this.removeAllListeners();
|
|
20820
21406
|
}
|
|
@@ -21155,6 +21741,18 @@ var HookManager = class {
|
|
|
21155
21741
|
}
|
|
21156
21742
|
existing.push(hook);
|
|
21157
21743
|
}
|
|
21744
|
+
/**
|
|
21745
|
+
* Unregister a specific hook function by reference.
|
|
21746
|
+
* Returns true if the hook was found and removed.
|
|
21747
|
+
*/
|
|
21748
|
+
unregister(name, hook) {
|
|
21749
|
+
const hooks = this.hooks.get(name);
|
|
21750
|
+
if (!hooks) return false;
|
|
21751
|
+
const index = hooks.indexOf(hook);
|
|
21752
|
+
if (index === -1) return false;
|
|
21753
|
+
hooks.splice(index, 1);
|
|
21754
|
+
return true;
|
|
21755
|
+
}
|
|
21158
21756
|
/**
|
|
21159
21757
|
* Execute hooks for a given name
|
|
21160
21758
|
*/
|
|
@@ -21177,7 +21775,7 @@ var HookManager = class {
|
|
|
21177
21775
|
const hook = hooks[i];
|
|
21178
21776
|
const hookKey = this.getHookKey(hook, i);
|
|
21179
21777
|
const hookResult = await this.executeHookSafely(hook, context, hookKey);
|
|
21180
|
-
if (hookResult
|
|
21778
|
+
if (hookResult == null) {
|
|
21181
21779
|
continue;
|
|
21182
21780
|
}
|
|
21183
21781
|
result = { ...result, ...hookResult };
|
|
@@ -21197,7 +21795,7 @@ var HookManager = class {
|
|
|
21197
21795
|
return this.executeHookSafely(hook, context, hookKey);
|
|
21198
21796
|
})
|
|
21199
21797
|
);
|
|
21200
|
-
const validResults = results.filter((r) => r
|
|
21798
|
+
const validResults = results.filter((r) => r != null);
|
|
21201
21799
|
return validResults.reduce(
|
|
21202
21800
|
(acc, hookResult) => ({ ...acc, ...hookResult }),
|
|
21203
21801
|
defaultResult
|
|
@@ -21873,7 +22471,6 @@ var Agent = class _Agent extends BaseAgent {
|
|
|
21873
22471
|
_cleanupExecution(streamState) {
|
|
21874
22472
|
streamState?.clear();
|
|
21875
22473
|
this.executionContext?.cleanup();
|
|
21876
|
-
this.hookManager.clear();
|
|
21877
22474
|
}
|
|
21878
22475
|
/**
|
|
21879
22476
|
* Emit iteration complete event (helper for run loop)
|
|
@@ -22758,6 +23355,27 @@ var Agent = class _Agent extends BaseAgent {
|
|
|
22758
23355
|
isCancelled() {
|
|
22759
23356
|
return this._cancelled;
|
|
22760
23357
|
}
|
|
23358
|
+
/**
|
|
23359
|
+
* Clear conversation history, resetting the context for a fresh interaction.
|
|
23360
|
+
* Plugins (working memory, in-context memory, etc.) are NOT affected.
|
|
23361
|
+
*/
|
|
23362
|
+
clearConversation(reason) {
|
|
23363
|
+
this._agentContext.clearConversation(reason);
|
|
23364
|
+
this._logger.info({ reason }, "Conversation cleared");
|
|
23365
|
+
}
|
|
23366
|
+
// ===== Hook Management =====
|
|
23367
|
+
/**
|
|
23368
|
+
* Register a hook on the agent. Can be called after creation.
|
|
23369
|
+
*/
|
|
23370
|
+
registerHook(name, hook) {
|
|
23371
|
+
this.hookManager.register(name, hook);
|
|
23372
|
+
}
|
|
23373
|
+
/**
|
|
23374
|
+
* Unregister a previously registered hook by reference.
|
|
23375
|
+
*/
|
|
23376
|
+
unregisterHook(name, hook) {
|
|
23377
|
+
return this.hookManager.unregister(name, hook);
|
|
23378
|
+
}
|
|
22761
23379
|
// ===== Cleanup =====
|
|
22762
23380
|
destroy() {
|
|
22763
23381
|
if (this._isDestroyed) {
|
|
@@ -22787,6 +23405,874 @@ var Agent = class _Agent extends BaseAgent {
|
|
|
22787
23405
|
}
|
|
22788
23406
|
};
|
|
22789
23407
|
|
|
23408
|
+
// src/domain/entities/Task.ts
|
|
23409
|
+
var TERMINAL_TASK_STATUSES = ["completed", "failed", "skipped", "cancelled"];
|
|
23410
|
+
function isTerminalStatus(status) {
|
|
23411
|
+
return TERMINAL_TASK_STATUSES.includes(status);
|
|
23412
|
+
}
|
|
23413
|
+
function createTask(input) {
|
|
23414
|
+
const now = Date.now();
|
|
23415
|
+
const id = input.id ?? `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
23416
|
+
return {
|
|
23417
|
+
id,
|
|
23418
|
+
name: input.name,
|
|
23419
|
+
description: input.description,
|
|
23420
|
+
status: "pending",
|
|
23421
|
+
dependsOn: input.dependsOn ?? [],
|
|
23422
|
+
externalDependency: input.externalDependency,
|
|
23423
|
+
condition: input.condition,
|
|
23424
|
+
execution: input.execution,
|
|
23425
|
+
suggestedTools: input.suggestedTools,
|
|
23426
|
+
validation: input.validation,
|
|
23427
|
+
expectedOutput: input.expectedOutput,
|
|
23428
|
+
attempts: 0,
|
|
23429
|
+
maxAttempts: input.maxAttempts ?? 3,
|
|
23430
|
+
createdAt: now,
|
|
23431
|
+
lastUpdatedAt: now,
|
|
23432
|
+
metadata: input.metadata
|
|
23433
|
+
};
|
|
23434
|
+
}
|
|
23435
|
+
function createPlan(input) {
|
|
23436
|
+
const now = Date.now();
|
|
23437
|
+
const id = `plan-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
23438
|
+
const tasks = input.tasks.map((taskInput) => createTask(taskInput));
|
|
23439
|
+
const nameToId = /* @__PURE__ */ new Map();
|
|
23440
|
+
for (const task of tasks) {
|
|
23441
|
+
nameToId.set(task.name, task.id);
|
|
23442
|
+
}
|
|
23443
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
23444
|
+
const taskInput = input.tasks[i];
|
|
23445
|
+
const task = tasks[i];
|
|
23446
|
+
if (taskInput.dependsOn && taskInput.dependsOn.length > 0) {
|
|
23447
|
+
task.dependsOn = taskInput.dependsOn.map((dep) => {
|
|
23448
|
+
if (dep.startsWith("task-")) {
|
|
23449
|
+
return dep;
|
|
23450
|
+
}
|
|
23451
|
+
const resolvedId = nameToId.get(dep);
|
|
23452
|
+
if (!resolvedId) {
|
|
23453
|
+
throw new Error(`Task dependency "${dep}" not found in plan`);
|
|
23454
|
+
}
|
|
23455
|
+
return resolvedId;
|
|
23456
|
+
});
|
|
23457
|
+
}
|
|
23458
|
+
}
|
|
23459
|
+
if (!input.skipCycleCheck) {
|
|
23460
|
+
const cycle = detectDependencyCycle(tasks);
|
|
23461
|
+
if (cycle) {
|
|
23462
|
+
const cycleNames = cycle.map((taskId) => {
|
|
23463
|
+
const task = tasks.find((t) => t.id === taskId);
|
|
23464
|
+
return task ? task.name : taskId;
|
|
23465
|
+
});
|
|
23466
|
+
throw new DependencyCycleError(cycleNames, id);
|
|
23467
|
+
}
|
|
23468
|
+
}
|
|
23469
|
+
return {
|
|
23470
|
+
id,
|
|
23471
|
+
goal: input.goal,
|
|
23472
|
+
context: input.context,
|
|
23473
|
+
tasks,
|
|
23474
|
+
concurrency: input.concurrency,
|
|
23475
|
+
allowDynamicTasks: input.allowDynamicTasks ?? true,
|
|
23476
|
+
status: "pending",
|
|
23477
|
+
createdAt: now,
|
|
23478
|
+
lastUpdatedAt: now,
|
|
23479
|
+
metadata: input.metadata
|
|
23480
|
+
};
|
|
23481
|
+
}
|
|
23482
|
+
function canTaskExecute(task, allTasks) {
|
|
23483
|
+
if (task.status !== "pending") {
|
|
23484
|
+
return false;
|
|
23485
|
+
}
|
|
23486
|
+
if (task.dependsOn.length > 0) {
|
|
23487
|
+
for (const depId of task.dependsOn) {
|
|
23488
|
+
const depTask = allTasks.find((t) => t.id === depId);
|
|
23489
|
+
if (!depTask || depTask.status !== "completed") {
|
|
23490
|
+
return false;
|
|
23491
|
+
}
|
|
23492
|
+
}
|
|
23493
|
+
}
|
|
23494
|
+
return true;
|
|
23495
|
+
}
|
|
23496
|
+
function getNextExecutableTasks(plan) {
|
|
23497
|
+
const executable = plan.tasks.filter((task) => canTaskExecute(task, plan.tasks));
|
|
23498
|
+
if (executable.length === 0) {
|
|
23499
|
+
return [];
|
|
23500
|
+
}
|
|
23501
|
+
if (!plan.concurrency) {
|
|
23502
|
+
return [executable[0]];
|
|
23503
|
+
}
|
|
23504
|
+
const runningCount = plan.tasks.filter((t) => t.status === "in_progress").length;
|
|
23505
|
+
const availableSlots = plan.concurrency.maxParallelTasks - runningCount;
|
|
23506
|
+
if (availableSlots <= 0) {
|
|
23507
|
+
return [];
|
|
23508
|
+
}
|
|
23509
|
+
const parallelTasks = executable.filter((task) => task.execution?.parallel === true);
|
|
23510
|
+
if (parallelTasks.length === 0) {
|
|
23511
|
+
return [executable[0]];
|
|
23512
|
+
}
|
|
23513
|
+
let sortedTasks = [...parallelTasks];
|
|
23514
|
+
if (plan.concurrency.strategy === "priority") {
|
|
23515
|
+
sortedTasks.sort((a, b) => (b.execution?.priority ?? 0) - (a.execution?.priority ?? 0));
|
|
23516
|
+
}
|
|
23517
|
+
return sortedTasks.slice(0, availableSlots);
|
|
23518
|
+
}
|
|
23519
|
+
async function evaluateCondition(condition, memory) {
|
|
23520
|
+
const value = await memory.get(condition.memoryKey);
|
|
23521
|
+
switch (condition.operator) {
|
|
23522
|
+
case "exists":
|
|
23523
|
+
return value !== void 0;
|
|
23524
|
+
case "not_exists":
|
|
23525
|
+
return value === void 0;
|
|
23526
|
+
case "equals":
|
|
23527
|
+
return value === condition.value;
|
|
23528
|
+
case "contains":
|
|
23529
|
+
if (Array.isArray(value)) {
|
|
23530
|
+
return value.includes(condition.value);
|
|
23531
|
+
}
|
|
23532
|
+
if (typeof value === "string" && typeof condition.value === "string") {
|
|
23533
|
+
return value.includes(condition.value);
|
|
23534
|
+
}
|
|
23535
|
+
return false;
|
|
23536
|
+
case "truthy":
|
|
23537
|
+
return !!value;
|
|
23538
|
+
case "greater_than":
|
|
23539
|
+
if (typeof value === "number" && typeof condition.value === "number") {
|
|
23540
|
+
return value > condition.value;
|
|
23541
|
+
}
|
|
23542
|
+
return false;
|
|
23543
|
+
case "less_than":
|
|
23544
|
+
if (typeof value === "number" && typeof condition.value === "number") {
|
|
23545
|
+
return value < condition.value;
|
|
23546
|
+
}
|
|
23547
|
+
return false;
|
|
23548
|
+
default:
|
|
23549
|
+
return false;
|
|
23550
|
+
}
|
|
23551
|
+
}
|
|
23552
|
+
function updateTaskStatus(task, status) {
|
|
23553
|
+
const now = Date.now();
|
|
23554
|
+
const updated = {
|
|
23555
|
+
...task,
|
|
23556
|
+
status,
|
|
23557
|
+
lastUpdatedAt: now
|
|
23558
|
+
};
|
|
23559
|
+
if (status === "in_progress") {
|
|
23560
|
+
if (!updated.startedAt) {
|
|
23561
|
+
updated.startedAt = now;
|
|
23562
|
+
}
|
|
23563
|
+
updated.attempts += 1;
|
|
23564
|
+
}
|
|
23565
|
+
if ((status === "completed" || status === "failed") && !updated.completedAt) {
|
|
23566
|
+
updated.completedAt = now;
|
|
23567
|
+
}
|
|
23568
|
+
return updated;
|
|
23569
|
+
}
|
|
23570
|
+
function isTaskBlocked(task, allTasks) {
|
|
23571
|
+
if (task.dependsOn.length === 0) {
|
|
23572
|
+
return false;
|
|
23573
|
+
}
|
|
23574
|
+
for (const depId of task.dependsOn) {
|
|
23575
|
+
const depTask = allTasks.find((t) => t.id === depId);
|
|
23576
|
+
if (!depTask) {
|
|
23577
|
+
return true;
|
|
23578
|
+
}
|
|
23579
|
+
if (depTask.status !== "completed") {
|
|
23580
|
+
return true;
|
|
23581
|
+
}
|
|
23582
|
+
}
|
|
23583
|
+
return false;
|
|
23584
|
+
}
|
|
23585
|
+
function getTaskDependencies(task, allTasks) {
|
|
23586
|
+
if (task.dependsOn.length === 0) {
|
|
23587
|
+
return [];
|
|
23588
|
+
}
|
|
23589
|
+
return task.dependsOn.map((depId) => allTasks.find((t) => t.id === depId)).filter((t) => t !== void 0);
|
|
23590
|
+
}
|
|
23591
|
+
function resolveDependencies(taskInputs, tasks) {
|
|
23592
|
+
const nameToId = /* @__PURE__ */ new Map();
|
|
23593
|
+
for (const task of tasks) {
|
|
23594
|
+
nameToId.set(task.name, task.id);
|
|
23595
|
+
}
|
|
23596
|
+
for (const input of taskInputs) {
|
|
23597
|
+
if (input.dependsOn && input.dependsOn.length > 0) {
|
|
23598
|
+
input.dependsOn = input.dependsOn.map((dep) => {
|
|
23599
|
+
if (dep.startsWith("task-")) {
|
|
23600
|
+
return dep;
|
|
23601
|
+
}
|
|
23602
|
+
const resolvedId = nameToId.get(dep);
|
|
23603
|
+
if (!resolvedId) {
|
|
23604
|
+
throw new Error(`Task dependency "${dep}" not found`);
|
|
23605
|
+
}
|
|
23606
|
+
return resolvedId;
|
|
23607
|
+
});
|
|
23608
|
+
}
|
|
23609
|
+
}
|
|
23610
|
+
}
|
|
23611
|
+
function detectDependencyCycle(tasks) {
|
|
23612
|
+
const visited = /* @__PURE__ */ new Set();
|
|
23613
|
+
const recStack = /* @__PURE__ */ new Set();
|
|
23614
|
+
const taskMap = new Map(tasks.map((t) => [t.id, t]));
|
|
23615
|
+
function dfs(taskId, path6) {
|
|
23616
|
+
if (recStack.has(taskId)) {
|
|
23617
|
+
const cycleStart = path6.indexOf(taskId);
|
|
23618
|
+
return [...path6.slice(cycleStart), taskId];
|
|
23619
|
+
}
|
|
23620
|
+
if (visited.has(taskId)) {
|
|
23621
|
+
return null;
|
|
23622
|
+
}
|
|
23623
|
+
visited.add(taskId);
|
|
23624
|
+
recStack.add(taskId);
|
|
23625
|
+
const task = taskMap.get(taskId);
|
|
23626
|
+
if (task) {
|
|
23627
|
+
for (const depId of task.dependsOn) {
|
|
23628
|
+
const cycle = dfs(depId, [...path6, taskId]);
|
|
23629
|
+
if (cycle) {
|
|
23630
|
+
return cycle;
|
|
23631
|
+
}
|
|
23632
|
+
}
|
|
23633
|
+
}
|
|
23634
|
+
recStack.delete(taskId);
|
|
23635
|
+
return null;
|
|
23636
|
+
}
|
|
23637
|
+
for (const task of tasks) {
|
|
23638
|
+
if (!visited.has(task.id)) {
|
|
23639
|
+
const cycle = dfs(task.id, []);
|
|
23640
|
+
if (cycle) {
|
|
23641
|
+
return cycle;
|
|
23642
|
+
}
|
|
23643
|
+
}
|
|
23644
|
+
}
|
|
23645
|
+
return null;
|
|
23646
|
+
}
|
|
23647
|
+
|
|
23648
|
+
// src/domain/entities/Routine.ts
|
|
23649
|
+
function createRoutineDefinition(input) {
|
|
23650
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23651
|
+
const id = input.id ?? `routine-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
23652
|
+
const taskNames = new Set(input.tasks.map((t) => t.name));
|
|
23653
|
+
const taskIds = new Set(input.tasks.filter((t) => t.id).map((t) => t.id));
|
|
23654
|
+
for (const task of input.tasks) {
|
|
23655
|
+
if (task.dependsOn) {
|
|
23656
|
+
for (const dep of task.dependsOn) {
|
|
23657
|
+
if (!taskNames.has(dep) && !taskIds.has(dep)) {
|
|
23658
|
+
throw new Error(
|
|
23659
|
+
`Routine "${input.name}": task "${task.name}" depends on unknown task "${dep}"`
|
|
23660
|
+
);
|
|
23661
|
+
}
|
|
23662
|
+
}
|
|
23663
|
+
}
|
|
23664
|
+
}
|
|
23665
|
+
createPlan({
|
|
23666
|
+
goal: input.name,
|
|
23667
|
+
tasks: input.tasks
|
|
23668
|
+
});
|
|
23669
|
+
return {
|
|
23670
|
+
id,
|
|
23671
|
+
name: input.name,
|
|
23672
|
+
description: input.description,
|
|
23673
|
+
version: input.version,
|
|
23674
|
+
tasks: input.tasks,
|
|
23675
|
+
requiredTools: input.requiredTools,
|
|
23676
|
+
requiredPlugins: input.requiredPlugins,
|
|
23677
|
+
instructions: input.instructions,
|
|
23678
|
+
concurrency: input.concurrency,
|
|
23679
|
+
allowDynamicTasks: input.allowDynamicTasks ?? false,
|
|
23680
|
+
tags: input.tags,
|
|
23681
|
+
author: input.author,
|
|
23682
|
+
createdAt: now,
|
|
23683
|
+
updatedAt: now,
|
|
23684
|
+
metadata: input.metadata
|
|
23685
|
+
};
|
|
23686
|
+
}
|
|
23687
|
+
function createRoutineExecution(definition) {
|
|
23688
|
+
const now = Date.now();
|
|
23689
|
+
const executionId = `rexec-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
23690
|
+
const plan = createPlan({
|
|
23691
|
+
goal: definition.name,
|
|
23692
|
+
context: definition.description,
|
|
23693
|
+
tasks: definition.tasks,
|
|
23694
|
+
concurrency: definition.concurrency,
|
|
23695
|
+
allowDynamicTasks: definition.allowDynamicTasks
|
|
23696
|
+
});
|
|
23697
|
+
return {
|
|
23698
|
+
id: executionId,
|
|
23699
|
+
routineId: definition.id,
|
|
23700
|
+
plan,
|
|
23701
|
+
status: "pending",
|
|
23702
|
+
progress: 0,
|
|
23703
|
+
lastUpdatedAt: now
|
|
23704
|
+
};
|
|
23705
|
+
}
|
|
23706
|
+
function getRoutineProgress(execution) {
|
|
23707
|
+
const { tasks } = execution.plan;
|
|
23708
|
+
if (tasks.length === 0) return 100;
|
|
23709
|
+
const completed = tasks.filter((t) => isTerminalStatus(t.status)).length;
|
|
23710
|
+
return Math.round(completed / tasks.length * 100);
|
|
23711
|
+
}
|
|
23712
|
+
|
|
23713
|
+
// src/utils/jsonExtractor.ts
|
|
23714
|
+
function extractJSON(text) {
|
|
23715
|
+
if (!text || typeof text !== "string") {
|
|
23716
|
+
return {
|
|
23717
|
+
success: false,
|
|
23718
|
+
error: "Input is empty or not a string"
|
|
23719
|
+
};
|
|
23720
|
+
}
|
|
23721
|
+
const trimmedText = text.trim();
|
|
23722
|
+
const codeBlockResult = extractFromCodeBlock(trimmedText);
|
|
23723
|
+
if (codeBlockResult.success) {
|
|
23724
|
+
return codeBlockResult;
|
|
23725
|
+
}
|
|
23726
|
+
const inlineResult = extractInlineJSON(trimmedText);
|
|
23727
|
+
if (inlineResult.success) {
|
|
23728
|
+
return inlineResult;
|
|
23729
|
+
}
|
|
23730
|
+
try {
|
|
23731
|
+
const data = JSON.parse(trimmedText);
|
|
23732
|
+
return {
|
|
23733
|
+
success: true,
|
|
23734
|
+
data,
|
|
23735
|
+
rawJson: trimmedText,
|
|
23736
|
+
method: "raw"
|
|
23737
|
+
};
|
|
23738
|
+
} catch (e) {
|
|
23739
|
+
return {
|
|
23740
|
+
success: false,
|
|
23741
|
+
error: `Could not extract JSON from text: ${e instanceof Error ? e.message : String(e)}`
|
|
23742
|
+
};
|
|
23743
|
+
}
|
|
23744
|
+
}
|
|
23745
|
+
function extractFromCodeBlock(text) {
|
|
23746
|
+
const codeBlockRegex = /```(?:json)?\s*([\s\S]*?)```/g;
|
|
23747
|
+
let match;
|
|
23748
|
+
while ((match = codeBlockRegex.exec(text)) !== null) {
|
|
23749
|
+
const content = match[1];
|
|
23750
|
+
if (content) {
|
|
23751
|
+
const trimmed = content.trim();
|
|
23752
|
+
try {
|
|
23753
|
+
const data = JSON.parse(trimmed);
|
|
23754
|
+
return {
|
|
23755
|
+
success: true,
|
|
23756
|
+
data,
|
|
23757
|
+
rawJson: trimmed,
|
|
23758
|
+
method: "code_block"
|
|
23759
|
+
};
|
|
23760
|
+
} catch {
|
|
23761
|
+
continue;
|
|
23762
|
+
}
|
|
23763
|
+
}
|
|
23764
|
+
}
|
|
23765
|
+
return { success: false };
|
|
23766
|
+
}
|
|
23767
|
+
function extractInlineJSON(text) {
|
|
23768
|
+
const objectMatch = findJSONObject(text);
|
|
23769
|
+
if (objectMatch) {
|
|
23770
|
+
try {
|
|
23771
|
+
const data = JSON.parse(objectMatch);
|
|
23772
|
+
return {
|
|
23773
|
+
success: true,
|
|
23774
|
+
data,
|
|
23775
|
+
rawJson: objectMatch,
|
|
23776
|
+
method: "inline"
|
|
23777
|
+
};
|
|
23778
|
+
} catch {
|
|
23779
|
+
}
|
|
23780
|
+
}
|
|
23781
|
+
const arrayMatch = findJSONArray(text);
|
|
23782
|
+
if (arrayMatch) {
|
|
23783
|
+
try {
|
|
23784
|
+
const data = JSON.parse(arrayMatch);
|
|
23785
|
+
return {
|
|
23786
|
+
success: true,
|
|
23787
|
+
data,
|
|
23788
|
+
rawJson: arrayMatch,
|
|
23789
|
+
method: "inline"
|
|
23790
|
+
};
|
|
23791
|
+
} catch {
|
|
23792
|
+
}
|
|
23793
|
+
}
|
|
23794
|
+
return { success: false };
|
|
23795
|
+
}
|
|
23796
|
+
function findJSONObject(text) {
|
|
23797
|
+
const startIndex = text.indexOf("{");
|
|
23798
|
+
if (startIndex === -1) return null;
|
|
23799
|
+
let depth = 0;
|
|
23800
|
+
let inString = false;
|
|
23801
|
+
let escaped = false;
|
|
23802
|
+
for (let i = startIndex; i < text.length; i++) {
|
|
23803
|
+
const char = text[i];
|
|
23804
|
+
if (escaped) {
|
|
23805
|
+
escaped = false;
|
|
23806
|
+
continue;
|
|
23807
|
+
}
|
|
23808
|
+
if (char === "\\" && inString) {
|
|
23809
|
+
escaped = true;
|
|
23810
|
+
continue;
|
|
23811
|
+
}
|
|
23812
|
+
if (char === '"') {
|
|
23813
|
+
inString = !inString;
|
|
23814
|
+
continue;
|
|
23815
|
+
}
|
|
23816
|
+
if (inString) continue;
|
|
23817
|
+
if (char === "{") {
|
|
23818
|
+
depth++;
|
|
23819
|
+
} else if (char === "}") {
|
|
23820
|
+
depth--;
|
|
23821
|
+
if (depth === 0) {
|
|
23822
|
+
return text.slice(startIndex, i + 1);
|
|
23823
|
+
}
|
|
23824
|
+
}
|
|
23825
|
+
}
|
|
23826
|
+
return null;
|
|
23827
|
+
}
|
|
23828
|
+
function findJSONArray(text) {
|
|
23829
|
+
const startIndex = text.indexOf("[");
|
|
23830
|
+
if (startIndex === -1) return null;
|
|
23831
|
+
let depth = 0;
|
|
23832
|
+
let inString = false;
|
|
23833
|
+
let escaped = false;
|
|
23834
|
+
for (let i = startIndex; i < text.length; i++) {
|
|
23835
|
+
const char = text[i];
|
|
23836
|
+
if (escaped) {
|
|
23837
|
+
escaped = false;
|
|
23838
|
+
continue;
|
|
23839
|
+
}
|
|
23840
|
+
if (char === "\\" && inString) {
|
|
23841
|
+
escaped = true;
|
|
23842
|
+
continue;
|
|
23843
|
+
}
|
|
23844
|
+
if (char === '"') {
|
|
23845
|
+
inString = !inString;
|
|
23846
|
+
continue;
|
|
23847
|
+
}
|
|
23848
|
+
if (inString) continue;
|
|
23849
|
+
if (char === "[") {
|
|
23850
|
+
depth++;
|
|
23851
|
+
} else if (char === "]") {
|
|
23852
|
+
depth--;
|
|
23853
|
+
if (depth === 0) {
|
|
23854
|
+
return text.slice(startIndex, i + 1);
|
|
23855
|
+
}
|
|
23856
|
+
}
|
|
23857
|
+
}
|
|
23858
|
+
return null;
|
|
23859
|
+
}
|
|
23860
|
+
function extractJSONField(text, field, defaultValue) {
|
|
23861
|
+
const result = extractJSON(text);
|
|
23862
|
+
if (result.success && result.data && field in result.data) {
|
|
23863
|
+
return result.data[field];
|
|
23864
|
+
}
|
|
23865
|
+
return defaultValue;
|
|
23866
|
+
}
|
|
23867
|
+
function extractNumber(text, patterns = [
|
|
23868
|
+
/(\d{1,3})%?\s*(?:complete|score|percent)/i,
|
|
23869
|
+
/(?:score|completion|rating)[:\s]+(\d{1,3})/i,
|
|
23870
|
+
/(\d{1,3})\s*(?:out of|\/)\s*100/i
|
|
23871
|
+
], defaultValue = 0) {
|
|
23872
|
+
const jsonResult = extractJSON(text);
|
|
23873
|
+
if (jsonResult.success && jsonResult.data) {
|
|
23874
|
+
const scoreFields = ["score", "completionScore", "completion_score", "rating", "percent", "value"];
|
|
23875
|
+
for (const field of scoreFields) {
|
|
23876
|
+
if (field in jsonResult.data && typeof jsonResult.data[field] === "number") {
|
|
23877
|
+
return jsonResult.data[field];
|
|
23878
|
+
}
|
|
23879
|
+
}
|
|
23880
|
+
}
|
|
23881
|
+
for (const pattern of patterns) {
|
|
23882
|
+
const match = text.match(pattern);
|
|
23883
|
+
if (match && match[1]) {
|
|
23884
|
+
const num = parseInt(match[1], 10);
|
|
23885
|
+
if (!isNaN(num)) {
|
|
23886
|
+
return num;
|
|
23887
|
+
}
|
|
23888
|
+
}
|
|
23889
|
+
}
|
|
23890
|
+
return defaultValue;
|
|
23891
|
+
}
|
|
23892
|
+
|
|
23893
|
+
// src/core/routineRunner.ts
|
|
23894
|
+
init_Logger();
|
|
23895
|
+
function defaultSystemPrompt(definition) {
|
|
23896
|
+
const parts = [];
|
|
23897
|
+
if (definition.instructions) {
|
|
23898
|
+
parts.push(definition.instructions);
|
|
23899
|
+
}
|
|
23900
|
+
parts.push(
|
|
23901
|
+
`You are executing a routine called "${definition.name}".`,
|
|
23902
|
+
"",
|
|
23903
|
+
"Between tasks, your conversation history is cleared but your memory persists.",
|
|
23904
|
+
"Use these strategies to pass information between tasks:",
|
|
23905
|
+
"- Use context_set for small key results that subsequent tasks need immediately (visible in context, no retrieval needed).",
|
|
23906
|
+
'- Use memory_store with tier="findings" for larger data that may be needed later.',
|
|
23907
|
+
"- Use memory_retrieve to access data stored by previous tasks.",
|
|
23908
|
+
"",
|
|
23909
|
+
"IMPORTANT: When you have completed the current task, you MUST stop immediately.",
|
|
23910
|
+
"Do NOT repeat work you have already done. Do NOT re-fetch data you already have.",
|
|
23911
|
+
"Store key results in memory once, then produce a final text response (no more tool calls) to signal completion."
|
|
23912
|
+
);
|
|
23913
|
+
return parts.join("\n");
|
|
23914
|
+
}
|
|
23915
|
+
function defaultTaskPrompt(task) {
|
|
23916
|
+
const parts = [];
|
|
23917
|
+
parts.push(`## Current Task: ${task.name}`, "");
|
|
23918
|
+
parts.push(task.description, "");
|
|
23919
|
+
if (task.expectedOutput) {
|
|
23920
|
+
parts.push(`**Expected output:** ${task.expectedOutput}`, "");
|
|
23921
|
+
}
|
|
23922
|
+
if (task.suggestedTools && task.suggestedTools.length > 0) {
|
|
23923
|
+
parts.push(`**Suggested tools:** ${task.suggestedTools.join(", ")}`, "");
|
|
23924
|
+
}
|
|
23925
|
+
const criteria = task.validation?.completionCriteria;
|
|
23926
|
+
if (criteria && criteria.length > 0) {
|
|
23927
|
+
parts.push("### Completion Criteria");
|
|
23928
|
+
parts.push("When you are done, ensure the following are met:");
|
|
23929
|
+
for (const c of criteria) {
|
|
23930
|
+
parts.push(`- ${c}`);
|
|
23931
|
+
}
|
|
23932
|
+
parts.push("");
|
|
23933
|
+
}
|
|
23934
|
+
parts.push("After completing the work, store key results in memory once, then respond with a text summary (no more tool calls).");
|
|
23935
|
+
return parts.join("\n");
|
|
23936
|
+
}
|
|
23937
|
+
function defaultValidationPrompt(task, context) {
|
|
23938
|
+
const criteria = task.validation?.completionCriteria ?? [];
|
|
23939
|
+
const criteriaList = criteria.length > 0 ? criteria.map((c, i) => `${i + 1}. ${c}`).join("\n") : "The task was completed as described.";
|
|
23940
|
+
const parts = [
|
|
23941
|
+
`Evaluate if the task "${task.name}" was completed successfully.`,
|
|
23942
|
+
"",
|
|
23943
|
+
`Task description: ${task.description}`,
|
|
23944
|
+
"",
|
|
23945
|
+
"Completion criteria:",
|
|
23946
|
+
criteriaList,
|
|
23947
|
+
"",
|
|
23948
|
+
"--- EVIDENCE ---",
|
|
23949
|
+
"",
|
|
23950
|
+
"Agent response (final text output):",
|
|
23951
|
+
context.responseText || "(no text output)",
|
|
23952
|
+
"",
|
|
23953
|
+
"Tool calls made during this task:",
|
|
23954
|
+
context.toolCallLog
|
|
23955
|
+
];
|
|
23956
|
+
if (context.inContextMemory) {
|
|
23957
|
+
parts.push("", "In-context memory (current state):", context.inContextMemory);
|
|
23958
|
+
}
|
|
23959
|
+
if (context.workingMemoryIndex) {
|
|
23960
|
+
parts.push("", "Working memory index (stored data):", context.workingMemoryIndex);
|
|
23961
|
+
}
|
|
23962
|
+
parts.push(
|
|
23963
|
+
"",
|
|
23964
|
+
"--- END EVIDENCE ---",
|
|
23965
|
+
"",
|
|
23966
|
+
"Use the evidence above to verify each criterion. Check tool call results, not just the agent's claims.",
|
|
23967
|
+
"",
|
|
23968
|
+
"Return a JSON object with the following structure:",
|
|
23969
|
+
"```json",
|
|
23970
|
+
'{ "isComplete": boolean, "completionScore": number (0-100), "explanation": "..." }',
|
|
23971
|
+
"```",
|
|
23972
|
+
"",
|
|
23973
|
+
"Be strict: only mark isComplete=true if all criteria are clearly met based on the evidence."
|
|
23974
|
+
);
|
|
23975
|
+
return parts.join("\n");
|
|
23976
|
+
}
|
|
23977
|
+
function formatToolCallLog(conversation) {
|
|
23978
|
+
const calls = [];
|
|
23979
|
+
for (const item of conversation) {
|
|
23980
|
+
if (!("content" in item) || !Array.isArray(item.content)) continue;
|
|
23981
|
+
const msg = item;
|
|
23982
|
+
for (const c of msg.content) {
|
|
23983
|
+
if (c.type === "tool_use" /* TOOL_USE */) {
|
|
23984
|
+
let argsStr;
|
|
23985
|
+
try {
|
|
23986
|
+
const parsed = JSON.parse(c.arguments);
|
|
23987
|
+
argsStr = JSON.stringify(parsed, null, 2);
|
|
23988
|
+
if (argsStr.length > 500) argsStr = argsStr.slice(0, 500) + "... (truncated)";
|
|
23989
|
+
} catch {
|
|
23990
|
+
argsStr = c.arguments;
|
|
23991
|
+
}
|
|
23992
|
+
calls.push(`CALL: ${c.name}(${argsStr})`);
|
|
23993
|
+
} else if (c.type === "tool_result" /* TOOL_RESULT */) {
|
|
23994
|
+
let resultStr = typeof c.content === "string" ? c.content : JSON.stringify(c.content);
|
|
23995
|
+
if (resultStr.length > 500) resultStr = resultStr.slice(0, 500) + "... (truncated)";
|
|
23996
|
+
const prefix = c.error ? "ERROR" : "RESULT";
|
|
23997
|
+
calls.push(` ${prefix}: ${resultStr}`);
|
|
23998
|
+
}
|
|
23999
|
+
}
|
|
24000
|
+
}
|
|
24001
|
+
return calls.length > 0 ? calls.join("\n") : "(no tool calls)";
|
|
24002
|
+
}
|
|
24003
|
+
async function collectValidationContext(agent, responseText) {
|
|
24004
|
+
const icmPlugin = agent.context.getPlugin("in_context_memory");
|
|
24005
|
+
const inContextMemory = icmPlugin ? await icmPlugin.getContent() : null;
|
|
24006
|
+
const wmPlugin = agent.context.memory;
|
|
24007
|
+
const workingMemoryIndex = wmPlugin ? await wmPlugin.getContent() : null;
|
|
24008
|
+
const conversation = agent.context.getConversation();
|
|
24009
|
+
const toolCallLog = formatToolCallLog(conversation);
|
|
24010
|
+
return {
|
|
24011
|
+
responseText,
|
|
24012
|
+
inContextMemory,
|
|
24013
|
+
workingMemoryIndex,
|
|
24014
|
+
toolCallLog
|
|
24015
|
+
};
|
|
24016
|
+
}
|
|
24017
|
+
async function validateTaskCompletion(agent, task, responseText, validationPromptBuilder) {
|
|
24018
|
+
const hasExplicitValidation = task.validation?.skipReflection === false && task.validation?.completionCriteria && task.validation.completionCriteria.length > 0;
|
|
24019
|
+
if (!hasExplicitValidation) {
|
|
24020
|
+
return {
|
|
24021
|
+
isComplete: true,
|
|
24022
|
+
completionScore: 100,
|
|
24023
|
+
explanation: "Auto-passed (LLM validation not enabled)",
|
|
24024
|
+
requiresUserApproval: false
|
|
24025
|
+
};
|
|
24026
|
+
}
|
|
24027
|
+
const validationContext = await collectValidationContext(agent, responseText);
|
|
24028
|
+
const prompt = validationPromptBuilder(task, validationContext);
|
|
24029
|
+
const response = await agent.runDirect(prompt, {
|
|
24030
|
+
instructions: "You are a task completion evaluator. Return only JSON.",
|
|
24031
|
+
temperature: 0.1
|
|
24032
|
+
});
|
|
24033
|
+
const text = response.output_text ?? "";
|
|
24034
|
+
const extracted = extractJSON(text);
|
|
24035
|
+
if (!extracted.success || !extracted.data) {
|
|
24036
|
+
return {
|
|
24037
|
+
isComplete: false,
|
|
24038
|
+
completionScore: 0,
|
|
24039
|
+
explanation: `Failed to parse validation response: ${extracted.error ?? "unknown error"}`,
|
|
24040
|
+
requiresUserApproval: false
|
|
24041
|
+
};
|
|
24042
|
+
}
|
|
24043
|
+
const { isComplete, completionScore, explanation } = extracted.data;
|
|
24044
|
+
const minScore = task.validation?.minCompletionScore ?? 80;
|
|
24045
|
+
return {
|
|
24046
|
+
isComplete: isComplete && completionScore >= minScore,
|
|
24047
|
+
completionScore,
|
|
24048
|
+
explanation,
|
|
24049
|
+
requiresUserApproval: false
|
|
24050
|
+
};
|
|
24051
|
+
}
|
|
24052
|
+
async function executeRoutine(options) {
|
|
24053
|
+
const {
|
|
24054
|
+
definition,
|
|
24055
|
+
agent: existingAgent,
|
|
24056
|
+
connector,
|
|
24057
|
+
model,
|
|
24058
|
+
tools: extraTools,
|
|
24059
|
+
onTaskStarted,
|
|
24060
|
+
onTaskComplete,
|
|
24061
|
+
onTaskFailed,
|
|
24062
|
+
onTaskValidation,
|
|
24063
|
+
hooks,
|
|
24064
|
+
prompts
|
|
24065
|
+
} = options;
|
|
24066
|
+
if (!existingAgent && (!connector || !model)) {
|
|
24067
|
+
throw new Error("executeRoutine requires either `agent` or both `connector` and `model`");
|
|
24068
|
+
}
|
|
24069
|
+
const ownsAgent = !existingAgent;
|
|
24070
|
+
const log = exports.logger.child({ routine: definition.name });
|
|
24071
|
+
const execution = createRoutineExecution(definition);
|
|
24072
|
+
execution.status = "running";
|
|
24073
|
+
execution.startedAt = Date.now();
|
|
24074
|
+
execution.lastUpdatedAt = Date.now();
|
|
24075
|
+
const buildSystemPrompt = prompts?.system ?? defaultSystemPrompt;
|
|
24076
|
+
const buildTaskPrompt = prompts?.task ?? defaultTaskPrompt;
|
|
24077
|
+
const buildValidationPrompt = prompts?.validation ?? defaultValidationPrompt;
|
|
24078
|
+
let agent;
|
|
24079
|
+
const registeredHooks = [];
|
|
24080
|
+
if (existingAgent) {
|
|
24081
|
+
agent = existingAgent;
|
|
24082
|
+
if (hooks) {
|
|
24083
|
+
const hookNames = [
|
|
24084
|
+
"before:execution",
|
|
24085
|
+
"after:execution",
|
|
24086
|
+
"before:llm",
|
|
24087
|
+
"after:llm",
|
|
24088
|
+
"before:tool",
|
|
24089
|
+
"after:tool",
|
|
24090
|
+
"approve:tool",
|
|
24091
|
+
"pause:check"
|
|
24092
|
+
];
|
|
24093
|
+
for (const name of hookNames) {
|
|
24094
|
+
const hook = hooks[name];
|
|
24095
|
+
if (hook) {
|
|
24096
|
+
agent.registerHook(name, hook);
|
|
24097
|
+
registeredHooks.push({ name, hook });
|
|
24098
|
+
}
|
|
24099
|
+
}
|
|
24100
|
+
}
|
|
24101
|
+
} else {
|
|
24102
|
+
const allTools = [...extraTools ?? []];
|
|
24103
|
+
if (definition.requiredTools && definition.requiredTools.length > 0) {
|
|
24104
|
+
const availableToolNames = new Set(allTools.map((t) => t.definition.function.name));
|
|
24105
|
+
const missing = definition.requiredTools.filter((name) => !availableToolNames.has(name));
|
|
24106
|
+
if (missing.length > 0) {
|
|
24107
|
+
execution.status = "failed";
|
|
24108
|
+
execution.error = `Missing required tools: ${missing.join(", ")}`;
|
|
24109
|
+
execution.completedAt = Date.now();
|
|
24110
|
+
execution.lastUpdatedAt = Date.now();
|
|
24111
|
+
return execution;
|
|
24112
|
+
}
|
|
24113
|
+
}
|
|
24114
|
+
agent = Agent.create({
|
|
24115
|
+
connector,
|
|
24116
|
+
model,
|
|
24117
|
+
tools: allTools,
|
|
24118
|
+
instructions: buildSystemPrompt(definition),
|
|
24119
|
+
hooks,
|
|
24120
|
+
context: {
|
|
24121
|
+
model,
|
|
24122
|
+
features: {
|
|
24123
|
+
workingMemory: true,
|
|
24124
|
+
inContextMemory: true
|
|
24125
|
+
}
|
|
24126
|
+
}
|
|
24127
|
+
});
|
|
24128
|
+
}
|
|
24129
|
+
if (definition.requiredPlugins && definition.requiredPlugins.length > 0) {
|
|
24130
|
+
const missing = definition.requiredPlugins.filter(
|
|
24131
|
+
(name) => !agent.context.hasPlugin(name)
|
|
24132
|
+
);
|
|
24133
|
+
if (missing.length > 0) {
|
|
24134
|
+
if (ownsAgent) agent.destroy();
|
|
24135
|
+
execution.status = "failed";
|
|
24136
|
+
execution.error = `Missing required plugins: ${missing.join(", ")}`;
|
|
24137
|
+
execution.completedAt = Date.now();
|
|
24138
|
+
execution.lastUpdatedAt = Date.now();
|
|
24139
|
+
return execution;
|
|
24140
|
+
}
|
|
24141
|
+
}
|
|
24142
|
+
const failureMode = definition.concurrency?.failureMode ?? "fail-fast";
|
|
24143
|
+
try {
|
|
24144
|
+
let nextTasks = getNextExecutableTasks(execution.plan);
|
|
24145
|
+
while (nextTasks.length > 0) {
|
|
24146
|
+
const task = nextTasks[0];
|
|
24147
|
+
const taskIndex = execution.plan.tasks.findIndex((t) => t.id === task.id);
|
|
24148
|
+
log.info({ taskName: task.name, taskId: task.id }, "Starting task");
|
|
24149
|
+
execution.plan.tasks[taskIndex] = updateTaskStatus(task, "in_progress");
|
|
24150
|
+
execution.lastUpdatedAt = Date.now();
|
|
24151
|
+
onTaskStarted?.(execution.plan.tasks[taskIndex], execution);
|
|
24152
|
+
let taskCompleted = false;
|
|
24153
|
+
const maxTaskIterations = task.execution?.maxIterations ?? 15;
|
|
24154
|
+
const iterationLimiter = async (ctx) => {
|
|
24155
|
+
if (ctx.iteration >= maxTaskIterations) {
|
|
24156
|
+
agent.cancel(`Task "${task.name}" exceeded max iterations (${maxTaskIterations})`);
|
|
24157
|
+
}
|
|
24158
|
+
return { shouldPause: false };
|
|
24159
|
+
};
|
|
24160
|
+
agent.registerHook("pause:check", iterationLimiter);
|
|
24161
|
+
const getTask = () => execution.plan.tasks[taskIndex];
|
|
24162
|
+
while (!taskCompleted) {
|
|
24163
|
+
try {
|
|
24164
|
+
const taskPrompt = buildTaskPrompt(getTask());
|
|
24165
|
+
const response = await agent.run(taskPrompt);
|
|
24166
|
+
const responseText = response.output_text ?? "";
|
|
24167
|
+
const validationResult = await validateTaskCompletion(
|
|
24168
|
+
agent,
|
|
24169
|
+
getTask(),
|
|
24170
|
+
responseText,
|
|
24171
|
+
buildValidationPrompt
|
|
24172
|
+
);
|
|
24173
|
+
onTaskValidation?.(getTask(), validationResult, execution);
|
|
24174
|
+
if (validationResult.isComplete) {
|
|
24175
|
+
execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "completed");
|
|
24176
|
+
execution.plan.tasks[taskIndex].result = {
|
|
24177
|
+
success: true,
|
|
24178
|
+
output: responseText,
|
|
24179
|
+
validationScore: validationResult.completionScore,
|
|
24180
|
+
validationExplanation: validationResult.explanation
|
|
24181
|
+
};
|
|
24182
|
+
taskCompleted = true;
|
|
24183
|
+
log.info(
|
|
24184
|
+
{ taskName: getTask().name, score: validationResult.completionScore },
|
|
24185
|
+
"Task completed"
|
|
24186
|
+
);
|
|
24187
|
+
execution.progress = getRoutineProgress(execution);
|
|
24188
|
+
execution.lastUpdatedAt = Date.now();
|
|
24189
|
+
onTaskComplete?.(execution.plan.tasks[taskIndex], execution);
|
|
24190
|
+
} else {
|
|
24191
|
+
log.warn(
|
|
24192
|
+
{
|
|
24193
|
+
taskName: getTask().name,
|
|
24194
|
+
score: validationResult.completionScore,
|
|
24195
|
+
attempt: getTask().attempts,
|
|
24196
|
+
maxAttempts: getTask().maxAttempts
|
|
24197
|
+
},
|
|
24198
|
+
"Task validation failed"
|
|
24199
|
+
);
|
|
24200
|
+
if (getTask().attempts >= getTask().maxAttempts) {
|
|
24201
|
+
execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "failed");
|
|
24202
|
+
execution.plan.tasks[taskIndex].result = {
|
|
24203
|
+
success: false,
|
|
24204
|
+
error: validationResult.explanation,
|
|
24205
|
+
validationScore: validationResult.completionScore,
|
|
24206
|
+
validationExplanation: validationResult.explanation
|
|
24207
|
+
};
|
|
24208
|
+
break;
|
|
24209
|
+
}
|
|
24210
|
+
execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "in_progress");
|
|
24211
|
+
}
|
|
24212
|
+
} catch (error) {
|
|
24213
|
+
const errorMessage = error.message;
|
|
24214
|
+
log.error({ taskName: getTask().name, error: errorMessage }, "Task execution error");
|
|
24215
|
+
if (getTask().attempts >= getTask().maxAttempts) {
|
|
24216
|
+
execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "failed");
|
|
24217
|
+
execution.plan.tasks[taskIndex].result = {
|
|
24218
|
+
success: false,
|
|
24219
|
+
error: errorMessage
|
|
24220
|
+
};
|
|
24221
|
+
break;
|
|
24222
|
+
}
|
|
24223
|
+
execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "in_progress");
|
|
24224
|
+
}
|
|
24225
|
+
}
|
|
24226
|
+
if (!taskCompleted) {
|
|
24227
|
+
execution.progress = getRoutineProgress(execution);
|
|
24228
|
+
execution.lastUpdatedAt = Date.now();
|
|
24229
|
+
onTaskFailed?.(execution.plan.tasks[taskIndex], execution);
|
|
24230
|
+
if (failureMode === "fail-fast") {
|
|
24231
|
+
execution.status = "failed";
|
|
24232
|
+
execution.error = `Task "${getTask().name}" failed after ${getTask().attempts} attempt(s)`;
|
|
24233
|
+
execution.completedAt = Date.now();
|
|
24234
|
+
execution.lastUpdatedAt = Date.now();
|
|
24235
|
+
break;
|
|
24236
|
+
}
|
|
24237
|
+
}
|
|
24238
|
+
agent.unregisterHook("pause:check", iterationLimiter);
|
|
24239
|
+
agent.clearConversation("task-boundary");
|
|
24240
|
+
nextTasks = getNextExecutableTasks(execution.plan);
|
|
24241
|
+
}
|
|
24242
|
+
if (execution.status === "running") {
|
|
24243
|
+
const allTerminal = execution.plan.tasks.every((t) => isTerminalStatus(t.status));
|
|
24244
|
+
const allCompleted = execution.plan.tasks.every((t) => t.status === "completed");
|
|
24245
|
+
if (allCompleted) {
|
|
24246
|
+
execution.status = "completed";
|
|
24247
|
+
} else if (allTerminal) {
|
|
24248
|
+
execution.status = "failed";
|
|
24249
|
+
execution.error = "Not all tasks completed successfully";
|
|
24250
|
+
} else {
|
|
24251
|
+
execution.status = "failed";
|
|
24252
|
+
execution.error = "Execution stalled: remaining tasks are blocked by incomplete dependencies";
|
|
24253
|
+
}
|
|
24254
|
+
execution.completedAt = Date.now();
|
|
24255
|
+
execution.lastUpdatedAt = Date.now();
|
|
24256
|
+
execution.progress = getRoutineProgress(execution);
|
|
24257
|
+
}
|
|
24258
|
+
log.info(
|
|
24259
|
+
{ status: execution.status, progress: execution.progress },
|
|
24260
|
+
"Routine execution finished"
|
|
24261
|
+
);
|
|
24262
|
+
return execution;
|
|
24263
|
+
} finally {
|
|
24264
|
+
for (const { name, hook } of registeredHooks) {
|
|
24265
|
+
try {
|
|
24266
|
+
agent.unregisterHook(name, hook);
|
|
24267
|
+
} catch {
|
|
24268
|
+
}
|
|
24269
|
+
}
|
|
24270
|
+
if (ownsAgent) {
|
|
24271
|
+
agent.destroy();
|
|
24272
|
+
}
|
|
24273
|
+
}
|
|
24274
|
+
}
|
|
24275
|
+
|
|
22790
24276
|
// src/core/index.ts
|
|
22791
24277
|
init_constants();
|
|
22792
24278
|
(class {
|
|
@@ -22803,7 +24289,7 @@ init_constants();
|
|
|
22803
24289
|
throw new Error("Configuration file not found. Searched: " + this.DEFAULT_PATHS.join(", "));
|
|
22804
24290
|
}
|
|
22805
24291
|
try {
|
|
22806
|
-
const content = await
|
|
24292
|
+
const content = await fs19.promises.readFile(configPath, "utf-8");
|
|
22807
24293
|
let config = JSON.parse(content);
|
|
22808
24294
|
config = this.interpolateEnvVars(config);
|
|
22809
24295
|
this.validate(config);
|
|
@@ -22824,8 +24310,8 @@ init_constants();
|
|
|
22824
24310
|
throw new Error("Configuration file not found. Searched: " + this.DEFAULT_PATHS.join(", "));
|
|
22825
24311
|
}
|
|
22826
24312
|
try {
|
|
22827
|
-
const
|
|
22828
|
-
const content =
|
|
24313
|
+
const fs20 = __require("fs");
|
|
24314
|
+
const content = fs20.readFileSync(configPath, "utf-8");
|
|
22829
24315
|
let config = JSON.parse(content);
|
|
22830
24316
|
config = this.interpolateEnvVars(config);
|
|
22831
24317
|
this.validate(config);
|
|
@@ -22843,7 +24329,7 @@ init_constants();
|
|
|
22843
24329
|
static async findConfig() {
|
|
22844
24330
|
for (const path6 of this.DEFAULT_PATHS) {
|
|
22845
24331
|
try {
|
|
22846
|
-
await
|
|
24332
|
+
await fs19.promises.access(path2.resolve(path6));
|
|
22847
24333
|
return path2.resolve(path6);
|
|
22848
24334
|
} catch {
|
|
22849
24335
|
}
|
|
@@ -22854,10 +24340,10 @@ init_constants();
|
|
|
22854
24340
|
* Find configuration file synchronously
|
|
22855
24341
|
*/
|
|
22856
24342
|
static findConfigSync() {
|
|
22857
|
-
const
|
|
24343
|
+
const fs20 = __require("fs");
|
|
22858
24344
|
for (const path6 of this.DEFAULT_PATHS) {
|
|
22859
24345
|
try {
|
|
22860
|
-
|
|
24346
|
+
fs20.accessSync(path2.resolve(path6));
|
|
22861
24347
|
return path2.resolve(path6);
|
|
22862
24348
|
} catch {
|
|
22863
24349
|
}
|
|
@@ -28431,7 +29917,7 @@ var MCPRegistry = class {
|
|
|
28431
29917
|
static async loadFromConfigFile(path6) {
|
|
28432
29918
|
try {
|
|
28433
29919
|
const configPath = path2.resolve(path6);
|
|
28434
|
-
const content = await
|
|
29920
|
+
const content = await fs19.promises.readFile(configPath, "utf-8");
|
|
28435
29921
|
const config = JSON.parse(content);
|
|
28436
29922
|
if (!config.mcp) {
|
|
28437
29923
|
throw new MCPError("Configuration file does not contain MCP section");
|
|
@@ -29036,7 +30522,7 @@ var OpenAISTTProvider = class extends BaseMediaProvider {
|
|
|
29036
30522
|
if (Buffer.isBuffer(audio)) {
|
|
29037
30523
|
return new File([new Uint8Array(audio)], "audio.wav", { type: "audio/wav" });
|
|
29038
30524
|
} else if (typeof audio === "string") {
|
|
29039
|
-
return
|
|
30525
|
+
return fs19__namespace.createReadStream(audio);
|
|
29040
30526
|
} else {
|
|
29041
30527
|
throw new Error("Invalid audio input: must be Buffer or file path");
|
|
29042
30528
|
}
|
|
@@ -29589,7 +31075,7 @@ var TextToSpeech = class _TextToSpeech {
|
|
|
29589
31075
|
*/
|
|
29590
31076
|
async toFile(text, filePath, options) {
|
|
29591
31077
|
const response = await this.synthesize(text, options);
|
|
29592
|
-
await
|
|
31078
|
+
await fs18__namespace.writeFile(filePath, response.audio);
|
|
29593
31079
|
}
|
|
29594
31080
|
// ======================== Introspection Methods ========================
|
|
29595
31081
|
/**
|
|
@@ -29937,7 +31423,7 @@ var SpeechToText = class _SpeechToText {
|
|
|
29937
31423
|
* @param options - Optional transcription parameters
|
|
29938
31424
|
*/
|
|
29939
31425
|
async transcribeFile(filePath, options) {
|
|
29940
|
-
const audio = await
|
|
31426
|
+
const audio = await fs18__namespace.readFile(filePath);
|
|
29941
31427
|
return this.transcribe(audio, options);
|
|
29942
31428
|
}
|
|
29943
31429
|
/**
|
|
@@ -30263,7 +31749,7 @@ var OpenAIImageProvider = class extends BaseMediaProvider {
|
|
|
30263
31749
|
if (Buffer.isBuffer(image)) {
|
|
30264
31750
|
return new File([new Uint8Array(image)], "image.png", { type: "image/png" });
|
|
30265
31751
|
}
|
|
30266
|
-
return
|
|
31752
|
+
return fs19__namespace.createReadStream(image);
|
|
30267
31753
|
}
|
|
30268
31754
|
/**
|
|
30269
31755
|
* Handle OpenAI API errors
|
|
@@ -30410,8 +31896,8 @@ var GoogleImageProvider = class extends BaseMediaProvider {
|
|
|
30410
31896
|
if (Buffer.isBuffer(image)) {
|
|
30411
31897
|
imageBytes = image.toString("base64");
|
|
30412
31898
|
} else {
|
|
30413
|
-
const
|
|
30414
|
-
const buffer =
|
|
31899
|
+
const fs20 = await import('fs');
|
|
31900
|
+
const buffer = fs20.readFileSync(image);
|
|
30415
31901
|
imageBytes = buffer.toString("base64");
|
|
30416
31902
|
}
|
|
30417
31903
|
return {
|
|
@@ -30572,7 +32058,7 @@ var GrokImageProvider = class extends BaseMediaProvider {
|
|
|
30572
32058
|
if (Buffer.isBuffer(image)) {
|
|
30573
32059
|
return new File([new Uint8Array(image)], "image.png", { type: "image/png" });
|
|
30574
32060
|
}
|
|
30575
|
-
return
|
|
32061
|
+
return fs19__namespace.createReadStream(image);
|
|
30576
32062
|
}
|
|
30577
32063
|
/**
|
|
30578
32064
|
* Handle API errors
|
|
@@ -32022,8 +33508,8 @@ var OpenAISoraProvider = class extends BaseMediaProvider {
|
|
|
32022
33508
|
return new File([new Uint8Array(image)], "input.png", { type: "image/png" });
|
|
32023
33509
|
}
|
|
32024
33510
|
if (!image.startsWith("http")) {
|
|
32025
|
-
const
|
|
32026
|
-
const data =
|
|
33511
|
+
const fs20 = await import('fs');
|
|
33512
|
+
const data = fs20.readFileSync(image);
|
|
32027
33513
|
return new File([new Uint8Array(data)], "input.png", { type: "image/png" });
|
|
32028
33514
|
}
|
|
32029
33515
|
const response = await fetch(image);
|
|
@@ -32201,7 +33687,7 @@ var GoogleVeoProvider = class extends BaseMediaProvider {
|
|
|
32201
33687
|
if (video.videoBytes) {
|
|
32202
33688
|
buffer = Buffer.from(video.videoBytes, "base64");
|
|
32203
33689
|
} else if (video.uri) {
|
|
32204
|
-
const
|
|
33690
|
+
const fs20 = await import('fs/promises');
|
|
32205
33691
|
const os3 = await import('os');
|
|
32206
33692
|
const path6 = await import('path');
|
|
32207
33693
|
const tempDir = os3.tmpdir();
|
|
@@ -32212,11 +33698,11 @@ var GoogleVeoProvider = class extends BaseMediaProvider {
|
|
|
32212
33698
|
// Pass as GeneratedVideo
|
|
32213
33699
|
downloadPath: tempFile
|
|
32214
33700
|
});
|
|
32215
|
-
buffer = await
|
|
32216
|
-
await
|
|
33701
|
+
buffer = await fs20.readFile(tempFile);
|
|
33702
|
+
await fs20.unlink(tempFile).catch(() => {
|
|
32217
33703
|
});
|
|
32218
33704
|
} catch (downloadError) {
|
|
32219
|
-
await
|
|
33705
|
+
await fs20.unlink(tempFile).catch(() => {
|
|
32220
33706
|
});
|
|
32221
33707
|
throw new ProviderError(
|
|
32222
33708
|
"google",
|
|
@@ -32338,8 +33824,8 @@ var GoogleVeoProvider = class extends BaseMediaProvider {
|
|
|
32338
33824
|
if (image.startsWith("http://") || image.startsWith("https://")) {
|
|
32339
33825
|
return { imageUri: image };
|
|
32340
33826
|
}
|
|
32341
|
-
const
|
|
32342
|
-
const data = await
|
|
33827
|
+
const fs20 = await import('fs/promises');
|
|
33828
|
+
const data = await fs20.readFile(image);
|
|
32343
33829
|
return {
|
|
32344
33830
|
imageBytes: data.toString("base64")
|
|
32345
33831
|
};
|
|
@@ -32646,8 +34132,8 @@ var GrokImagineProvider = class extends BaseMediaProvider {
|
|
|
32646
34132
|
if (image.startsWith("http") || image.startsWith("data:")) {
|
|
32647
34133
|
return image;
|
|
32648
34134
|
}
|
|
32649
|
-
const
|
|
32650
|
-
const data =
|
|
34135
|
+
const fs20 = await import('fs');
|
|
34136
|
+
const data = fs20.readFileSync(image);
|
|
32651
34137
|
const base64 = data.toString("base64");
|
|
32652
34138
|
const ext = image.split(".").pop()?.toLowerCase() || "png";
|
|
32653
34139
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext}`;
|
|
@@ -34083,7 +35569,7 @@ var DocumentReader = class _DocumentReader {
|
|
|
34083
35569
|
async resolveSource(source) {
|
|
34084
35570
|
switch (source.type) {
|
|
34085
35571
|
case "file": {
|
|
34086
|
-
const buffer = await
|
|
35572
|
+
const buffer = await fs18.readFile(source.path);
|
|
34087
35573
|
const filename = source.path.split("/").pop() || source.path;
|
|
34088
35574
|
return { buffer, filename };
|
|
34089
35575
|
}
|
|
@@ -35316,245 +36802,6 @@ var CheckpointManager = class {
|
|
|
35316
36802
|
}
|
|
35317
36803
|
};
|
|
35318
36804
|
|
|
35319
|
-
// src/domain/entities/Task.ts
|
|
35320
|
-
var TERMINAL_TASK_STATUSES = ["completed", "failed", "skipped", "cancelled"];
|
|
35321
|
-
function isTerminalStatus(status) {
|
|
35322
|
-
return TERMINAL_TASK_STATUSES.includes(status);
|
|
35323
|
-
}
|
|
35324
|
-
function createTask(input) {
|
|
35325
|
-
const now = Date.now();
|
|
35326
|
-
const id = input.id ?? `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
35327
|
-
return {
|
|
35328
|
-
id,
|
|
35329
|
-
name: input.name,
|
|
35330
|
-
description: input.description,
|
|
35331
|
-
status: "pending",
|
|
35332
|
-
dependsOn: input.dependsOn ?? [],
|
|
35333
|
-
externalDependency: input.externalDependency,
|
|
35334
|
-
condition: input.condition,
|
|
35335
|
-
execution: input.execution,
|
|
35336
|
-
validation: input.validation,
|
|
35337
|
-
expectedOutput: input.expectedOutput,
|
|
35338
|
-
attempts: 0,
|
|
35339
|
-
maxAttempts: input.maxAttempts ?? 3,
|
|
35340
|
-
createdAt: now,
|
|
35341
|
-
lastUpdatedAt: now,
|
|
35342
|
-
metadata: input.metadata
|
|
35343
|
-
};
|
|
35344
|
-
}
|
|
35345
|
-
function createPlan(input) {
|
|
35346
|
-
const now = Date.now();
|
|
35347
|
-
const id = `plan-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
35348
|
-
const tasks = input.tasks.map((taskInput) => createTask(taskInput));
|
|
35349
|
-
const nameToId = /* @__PURE__ */ new Map();
|
|
35350
|
-
for (const task of tasks) {
|
|
35351
|
-
nameToId.set(task.name, task.id);
|
|
35352
|
-
}
|
|
35353
|
-
for (let i = 0; i < tasks.length; i++) {
|
|
35354
|
-
const taskInput = input.tasks[i];
|
|
35355
|
-
const task = tasks[i];
|
|
35356
|
-
if (taskInput.dependsOn && taskInput.dependsOn.length > 0) {
|
|
35357
|
-
task.dependsOn = taskInput.dependsOn.map((dep) => {
|
|
35358
|
-
if (dep.startsWith("task-")) {
|
|
35359
|
-
return dep;
|
|
35360
|
-
}
|
|
35361
|
-
const resolvedId = nameToId.get(dep);
|
|
35362
|
-
if (!resolvedId) {
|
|
35363
|
-
throw new Error(`Task dependency "${dep}" not found in plan`);
|
|
35364
|
-
}
|
|
35365
|
-
return resolvedId;
|
|
35366
|
-
});
|
|
35367
|
-
}
|
|
35368
|
-
}
|
|
35369
|
-
if (!input.skipCycleCheck) {
|
|
35370
|
-
const cycle = detectDependencyCycle(tasks);
|
|
35371
|
-
if (cycle) {
|
|
35372
|
-
const cycleNames = cycle.map((taskId) => {
|
|
35373
|
-
const task = tasks.find((t) => t.id === taskId);
|
|
35374
|
-
return task ? task.name : taskId;
|
|
35375
|
-
});
|
|
35376
|
-
throw new DependencyCycleError(cycleNames, id);
|
|
35377
|
-
}
|
|
35378
|
-
}
|
|
35379
|
-
return {
|
|
35380
|
-
id,
|
|
35381
|
-
goal: input.goal,
|
|
35382
|
-
context: input.context,
|
|
35383
|
-
tasks,
|
|
35384
|
-
concurrency: input.concurrency,
|
|
35385
|
-
allowDynamicTasks: input.allowDynamicTasks ?? true,
|
|
35386
|
-
status: "pending",
|
|
35387
|
-
createdAt: now,
|
|
35388
|
-
lastUpdatedAt: now,
|
|
35389
|
-
metadata: input.metadata
|
|
35390
|
-
};
|
|
35391
|
-
}
|
|
35392
|
-
function canTaskExecute(task, allTasks) {
|
|
35393
|
-
if (task.status !== "pending") {
|
|
35394
|
-
return false;
|
|
35395
|
-
}
|
|
35396
|
-
if (task.dependsOn.length > 0) {
|
|
35397
|
-
for (const depId of task.dependsOn) {
|
|
35398
|
-
const depTask = allTasks.find((t) => t.id === depId);
|
|
35399
|
-
if (!depTask || depTask.status !== "completed") {
|
|
35400
|
-
return false;
|
|
35401
|
-
}
|
|
35402
|
-
}
|
|
35403
|
-
}
|
|
35404
|
-
return true;
|
|
35405
|
-
}
|
|
35406
|
-
function getNextExecutableTasks(plan) {
|
|
35407
|
-
const executable = plan.tasks.filter((task) => canTaskExecute(task, plan.tasks));
|
|
35408
|
-
if (executable.length === 0) {
|
|
35409
|
-
return [];
|
|
35410
|
-
}
|
|
35411
|
-
if (!plan.concurrency) {
|
|
35412
|
-
return [executable[0]];
|
|
35413
|
-
}
|
|
35414
|
-
const runningCount = plan.tasks.filter((t) => t.status === "in_progress").length;
|
|
35415
|
-
const availableSlots = plan.concurrency.maxParallelTasks - runningCount;
|
|
35416
|
-
if (availableSlots <= 0) {
|
|
35417
|
-
return [];
|
|
35418
|
-
}
|
|
35419
|
-
const parallelTasks = executable.filter((task) => task.execution?.parallel === true);
|
|
35420
|
-
if (parallelTasks.length === 0) {
|
|
35421
|
-
return [executable[0]];
|
|
35422
|
-
}
|
|
35423
|
-
let sortedTasks = [...parallelTasks];
|
|
35424
|
-
if (plan.concurrency.strategy === "priority") {
|
|
35425
|
-
sortedTasks.sort((a, b) => (b.execution?.priority ?? 0) - (a.execution?.priority ?? 0));
|
|
35426
|
-
}
|
|
35427
|
-
return sortedTasks.slice(0, availableSlots);
|
|
35428
|
-
}
|
|
35429
|
-
async function evaluateCondition(condition, memory) {
|
|
35430
|
-
const value = await memory.get(condition.memoryKey);
|
|
35431
|
-
switch (condition.operator) {
|
|
35432
|
-
case "exists":
|
|
35433
|
-
return value !== void 0;
|
|
35434
|
-
case "not_exists":
|
|
35435
|
-
return value === void 0;
|
|
35436
|
-
case "equals":
|
|
35437
|
-
return value === condition.value;
|
|
35438
|
-
case "contains":
|
|
35439
|
-
if (Array.isArray(value)) {
|
|
35440
|
-
return value.includes(condition.value);
|
|
35441
|
-
}
|
|
35442
|
-
if (typeof value === "string" && typeof condition.value === "string") {
|
|
35443
|
-
return value.includes(condition.value);
|
|
35444
|
-
}
|
|
35445
|
-
return false;
|
|
35446
|
-
case "truthy":
|
|
35447
|
-
return !!value;
|
|
35448
|
-
case "greater_than":
|
|
35449
|
-
if (typeof value === "number" && typeof condition.value === "number") {
|
|
35450
|
-
return value > condition.value;
|
|
35451
|
-
}
|
|
35452
|
-
return false;
|
|
35453
|
-
case "less_than":
|
|
35454
|
-
if (typeof value === "number" && typeof condition.value === "number") {
|
|
35455
|
-
return value < condition.value;
|
|
35456
|
-
}
|
|
35457
|
-
return false;
|
|
35458
|
-
default:
|
|
35459
|
-
return false;
|
|
35460
|
-
}
|
|
35461
|
-
}
|
|
35462
|
-
function updateTaskStatus(task, status) {
|
|
35463
|
-
const now = Date.now();
|
|
35464
|
-
const updated = {
|
|
35465
|
-
...task,
|
|
35466
|
-
status,
|
|
35467
|
-
lastUpdatedAt: now
|
|
35468
|
-
};
|
|
35469
|
-
if (status === "in_progress") {
|
|
35470
|
-
if (!updated.startedAt) {
|
|
35471
|
-
updated.startedAt = now;
|
|
35472
|
-
}
|
|
35473
|
-
updated.attempts += 1;
|
|
35474
|
-
}
|
|
35475
|
-
if ((status === "completed" || status === "failed") && !updated.completedAt) {
|
|
35476
|
-
updated.completedAt = now;
|
|
35477
|
-
}
|
|
35478
|
-
return updated;
|
|
35479
|
-
}
|
|
35480
|
-
function isTaskBlocked(task, allTasks) {
|
|
35481
|
-
if (task.dependsOn.length === 0) {
|
|
35482
|
-
return false;
|
|
35483
|
-
}
|
|
35484
|
-
for (const depId of task.dependsOn) {
|
|
35485
|
-
const depTask = allTasks.find((t) => t.id === depId);
|
|
35486
|
-
if (!depTask) {
|
|
35487
|
-
return true;
|
|
35488
|
-
}
|
|
35489
|
-
if (depTask.status !== "completed") {
|
|
35490
|
-
return true;
|
|
35491
|
-
}
|
|
35492
|
-
}
|
|
35493
|
-
return false;
|
|
35494
|
-
}
|
|
35495
|
-
function getTaskDependencies(task, allTasks) {
|
|
35496
|
-
if (task.dependsOn.length === 0) {
|
|
35497
|
-
return [];
|
|
35498
|
-
}
|
|
35499
|
-
return task.dependsOn.map((depId) => allTasks.find((t) => t.id === depId)).filter((t) => t !== void 0);
|
|
35500
|
-
}
|
|
35501
|
-
function resolveDependencies(taskInputs, tasks) {
|
|
35502
|
-
const nameToId = /* @__PURE__ */ new Map();
|
|
35503
|
-
for (const task of tasks) {
|
|
35504
|
-
nameToId.set(task.name, task.id);
|
|
35505
|
-
}
|
|
35506
|
-
for (const input of taskInputs) {
|
|
35507
|
-
if (input.dependsOn && input.dependsOn.length > 0) {
|
|
35508
|
-
input.dependsOn = input.dependsOn.map((dep) => {
|
|
35509
|
-
if (dep.startsWith("task-")) {
|
|
35510
|
-
return dep;
|
|
35511
|
-
}
|
|
35512
|
-
const resolvedId = nameToId.get(dep);
|
|
35513
|
-
if (!resolvedId) {
|
|
35514
|
-
throw new Error(`Task dependency "${dep}" not found`);
|
|
35515
|
-
}
|
|
35516
|
-
return resolvedId;
|
|
35517
|
-
});
|
|
35518
|
-
}
|
|
35519
|
-
}
|
|
35520
|
-
}
|
|
35521
|
-
function detectDependencyCycle(tasks) {
|
|
35522
|
-
const visited = /* @__PURE__ */ new Set();
|
|
35523
|
-
const recStack = /* @__PURE__ */ new Set();
|
|
35524
|
-
const taskMap = new Map(tasks.map((t) => [t.id, t]));
|
|
35525
|
-
function dfs(taskId, path6) {
|
|
35526
|
-
if (recStack.has(taskId)) {
|
|
35527
|
-
const cycleStart = path6.indexOf(taskId);
|
|
35528
|
-
return [...path6.slice(cycleStart), taskId];
|
|
35529
|
-
}
|
|
35530
|
-
if (visited.has(taskId)) {
|
|
35531
|
-
return null;
|
|
35532
|
-
}
|
|
35533
|
-
visited.add(taskId);
|
|
35534
|
-
recStack.add(taskId);
|
|
35535
|
-
const task = taskMap.get(taskId);
|
|
35536
|
-
if (task) {
|
|
35537
|
-
for (const depId of task.dependsOn) {
|
|
35538
|
-
const cycle = dfs(depId, [...path6, taskId]);
|
|
35539
|
-
if (cycle) {
|
|
35540
|
-
return cycle;
|
|
35541
|
-
}
|
|
35542
|
-
}
|
|
35543
|
-
}
|
|
35544
|
-
recStack.delete(taskId);
|
|
35545
|
-
return null;
|
|
35546
|
-
}
|
|
35547
|
-
for (const task of tasks) {
|
|
35548
|
-
if (!visited.has(task.id)) {
|
|
35549
|
-
const cycle = dfs(task.id, []);
|
|
35550
|
-
if (cycle) {
|
|
35551
|
-
return cycle;
|
|
35552
|
-
}
|
|
35553
|
-
}
|
|
35554
|
-
}
|
|
35555
|
-
return null;
|
|
35556
|
-
}
|
|
35557
|
-
|
|
35558
36805
|
// src/capabilities/taskAgent/PlanningAgent.ts
|
|
35559
36806
|
var PLANNING_SYSTEM_PROMPT = `You are an AI planning agent. Your job is to analyze goals and break them down into structured, executable task plans.
|
|
35560
36807
|
|
|
@@ -36350,7 +37597,7 @@ var InMemoryHistoryStorage = class {
|
|
|
36350
37597
|
this.summaries = state.summaries ? [...state.summaries] : [];
|
|
36351
37598
|
}
|
|
36352
37599
|
};
|
|
36353
|
-
function
|
|
37600
|
+
function getDefaultBaseDirectory3() {
|
|
36354
37601
|
const platform2 = process.platform;
|
|
36355
37602
|
if (platform2 === "win32") {
|
|
36356
37603
|
const appData = process.env.APPDATA || process.env.LOCALAPPDATA;
|
|
@@ -36373,7 +37620,7 @@ var FileContextStorage = class {
|
|
|
36373
37620
|
constructor(config) {
|
|
36374
37621
|
this.agentId = config.agentId;
|
|
36375
37622
|
const sanitizedAgentId = sanitizeId(config.agentId);
|
|
36376
|
-
const baseDir = config.baseDirectory ??
|
|
37623
|
+
const baseDir = config.baseDirectory ?? getDefaultBaseDirectory3();
|
|
36377
37624
|
this.prettyPrint = config.prettyPrint ?? true;
|
|
36378
37625
|
this.sessionsDirectory = path2.join(baseDir, sanitizedAgentId, "sessions");
|
|
36379
37626
|
this.indexPath = path2.join(this.sessionsDirectory, "_index.json");
|
|
@@ -36402,11 +37649,11 @@ var FileContextStorage = class {
|
|
|
36402
37649
|
const data = this.prettyPrint ? JSON.stringify(storedSession, null, 2) : JSON.stringify(storedSession);
|
|
36403
37650
|
const tempPath = `${filePath}.tmp`;
|
|
36404
37651
|
try {
|
|
36405
|
-
await
|
|
36406
|
-
await
|
|
37652
|
+
await fs19.promises.writeFile(tempPath, data, "utf-8");
|
|
37653
|
+
await fs19.promises.rename(tempPath, filePath);
|
|
36407
37654
|
} catch (error) {
|
|
36408
37655
|
try {
|
|
36409
|
-
await
|
|
37656
|
+
await fs19.promises.unlink(tempPath);
|
|
36410
37657
|
} catch {
|
|
36411
37658
|
}
|
|
36412
37659
|
throw error;
|
|
@@ -36427,7 +37674,7 @@ var FileContextStorage = class {
|
|
|
36427
37674
|
const sanitizedSessionId = sanitizeId(sessionId);
|
|
36428
37675
|
const filePath = this.getFilePath(sanitizedSessionId);
|
|
36429
37676
|
try {
|
|
36430
|
-
await
|
|
37677
|
+
await fs19.promises.unlink(filePath);
|
|
36431
37678
|
} catch (error) {
|
|
36432
37679
|
if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
|
|
36433
37680
|
throw error;
|
|
@@ -36442,7 +37689,7 @@ var FileContextStorage = class {
|
|
|
36442
37689
|
const sanitizedSessionId = sanitizeId(sessionId);
|
|
36443
37690
|
const filePath = this.getFilePath(sanitizedSessionId);
|
|
36444
37691
|
try {
|
|
36445
|
-
await
|
|
37692
|
+
await fs19.promises.access(filePath);
|
|
36446
37693
|
return true;
|
|
36447
37694
|
} catch {
|
|
36448
37695
|
return false;
|
|
@@ -36507,7 +37754,7 @@ var FileContextStorage = class {
|
|
|
36507
37754
|
const sanitizedSessionId = sanitizeId(sessionId);
|
|
36508
37755
|
const filePath = this.getFilePath(sanitizedSessionId);
|
|
36509
37756
|
const data = this.prettyPrint ? JSON.stringify(stored, null, 2) : JSON.stringify(stored);
|
|
36510
|
-
await
|
|
37757
|
+
await fs19.promises.writeFile(filePath, data, "utf-8");
|
|
36511
37758
|
await this.updateIndex(stored);
|
|
36512
37759
|
}
|
|
36513
37760
|
/**
|
|
@@ -36535,13 +37782,13 @@ var FileContextStorage = class {
|
|
|
36535
37782
|
*/
|
|
36536
37783
|
async rebuildIndex() {
|
|
36537
37784
|
await this.ensureDirectory();
|
|
36538
|
-
const files = await
|
|
37785
|
+
const files = await fs19.promises.readdir(this.sessionsDirectory);
|
|
36539
37786
|
const sessionFiles = files.filter((f) => f.endsWith(".json") && !f.startsWith("_"));
|
|
36540
37787
|
const entries = [];
|
|
36541
37788
|
for (const file of sessionFiles) {
|
|
36542
37789
|
try {
|
|
36543
37790
|
const filePath = path2.join(this.sessionsDirectory, file);
|
|
36544
|
-
const data = await
|
|
37791
|
+
const data = await fs19.promises.readFile(filePath, "utf-8");
|
|
36545
37792
|
const stored = JSON.parse(data);
|
|
36546
37793
|
entries.push(this.storedToIndexEntry(stored));
|
|
36547
37794
|
} catch {
|
|
@@ -36563,7 +37810,7 @@ var FileContextStorage = class {
|
|
|
36563
37810
|
}
|
|
36564
37811
|
async ensureDirectory() {
|
|
36565
37812
|
try {
|
|
36566
|
-
await
|
|
37813
|
+
await fs19.promises.mkdir(this.sessionsDirectory, { recursive: true });
|
|
36567
37814
|
} catch (error) {
|
|
36568
37815
|
if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
|
|
36569
37816
|
throw error;
|
|
@@ -36573,7 +37820,7 @@ var FileContextStorage = class {
|
|
|
36573
37820
|
async loadRaw(sanitizedSessionId) {
|
|
36574
37821
|
const filePath = this.getFilePath(sanitizedSessionId);
|
|
36575
37822
|
try {
|
|
36576
|
-
const data = await
|
|
37823
|
+
const data = await fs19.promises.readFile(filePath, "utf-8");
|
|
36577
37824
|
return JSON.parse(data);
|
|
36578
37825
|
} catch (error) {
|
|
36579
37826
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
@@ -36591,7 +37838,7 @@ var FileContextStorage = class {
|
|
|
36591
37838
|
return this.index;
|
|
36592
37839
|
}
|
|
36593
37840
|
try {
|
|
36594
|
-
const data = await
|
|
37841
|
+
const data = await fs19.promises.readFile(this.indexPath, "utf-8");
|
|
36595
37842
|
this.index = JSON.parse(data);
|
|
36596
37843
|
return this.index;
|
|
36597
37844
|
} catch (error) {
|
|
@@ -36612,7 +37859,7 @@ var FileContextStorage = class {
|
|
|
36612
37859
|
await this.ensureDirectory();
|
|
36613
37860
|
this.index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
36614
37861
|
const data = this.prettyPrint ? JSON.stringify(this.index, null, 2) : JSON.stringify(this.index);
|
|
36615
|
-
await
|
|
37862
|
+
await fs19.promises.writeFile(this.indexPath, data, "utf-8");
|
|
36616
37863
|
}
|
|
36617
37864
|
async updateIndex(stored) {
|
|
36618
37865
|
const index = await this.loadIndex();
|
|
@@ -36645,7 +37892,7 @@ var FileContextStorage = class {
|
|
|
36645
37892
|
function createFileContextStorage(agentId, options) {
|
|
36646
37893
|
return new FileContextStorage({ agentId, ...options });
|
|
36647
37894
|
}
|
|
36648
|
-
function
|
|
37895
|
+
function getDefaultBaseDirectory4() {
|
|
36649
37896
|
const platform2 = process.platform;
|
|
36650
37897
|
if (platform2 === "win32") {
|
|
36651
37898
|
const appData = process.env.APPDATA || process.env.LOCALAPPDATA;
|
|
@@ -36664,7 +37911,7 @@ var FileAgentDefinitionStorage = class {
|
|
|
36664
37911
|
prettyPrint;
|
|
36665
37912
|
index = null;
|
|
36666
37913
|
constructor(config = {}) {
|
|
36667
|
-
this.baseDirectory = config.baseDirectory ??
|
|
37914
|
+
this.baseDirectory = config.baseDirectory ?? getDefaultBaseDirectory4();
|
|
36668
37915
|
this.prettyPrint = config.prettyPrint ?? true;
|
|
36669
37916
|
this.indexPath = path2.join(this.baseDirectory, "_agents_index.json");
|
|
36670
37917
|
}
|
|
@@ -36686,11 +37933,11 @@ var FileAgentDefinitionStorage = class {
|
|
|
36686
37933
|
const data = this.prettyPrint ? JSON.stringify(definition, null, 2) : JSON.stringify(definition);
|
|
36687
37934
|
const tempPath = `${filePath}.tmp`;
|
|
36688
37935
|
try {
|
|
36689
|
-
await
|
|
36690
|
-
await
|
|
37936
|
+
await fs19.promises.writeFile(tempPath, data, "utf-8");
|
|
37937
|
+
await fs19.promises.rename(tempPath, filePath);
|
|
36691
37938
|
} catch (error) {
|
|
36692
37939
|
try {
|
|
36693
|
-
await
|
|
37940
|
+
await fs19.promises.unlink(tempPath);
|
|
36694
37941
|
} catch {
|
|
36695
37942
|
}
|
|
36696
37943
|
throw error;
|
|
@@ -36712,7 +37959,7 @@ var FileAgentDefinitionStorage = class {
|
|
|
36712
37959
|
const agentDir = path2.join(this.baseDirectory, sanitizedId);
|
|
36713
37960
|
const filePath = path2.join(agentDir, "definition.json");
|
|
36714
37961
|
try {
|
|
36715
|
-
await
|
|
37962
|
+
await fs19.promises.unlink(filePath);
|
|
36716
37963
|
} catch (error) {
|
|
36717
37964
|
if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
|
|
36718
37965
|
throw error;
|
|
@@ -36727,7 +37974,7 @@ var FileAgentDefinitionStorage = class {
|
|
|
36727
37974
|
const sanitizedId = sanitizeAgentId2(agentId);
|
|
36728
37975
|
const filePath = path2.join(this.baseDirectory, sanitizedId, "definition.json");
|
|
36729
37976
|
try {
|
|
36730
|
-
await
|
|
37977
|
+
await fs19.promises.access(filePath);
|
|
36731
37978
|
return true;
|
|
36732
37979
|
} catch {
|
|
36733
37980
|
return false;
|
|
@@ -36789,13 +38036,13 @@ var FileAgentDefinitionStorage = class {
|
|
|
36789
38036
|
*/
|
|
36790
38037
|
async rebuildIndex() {
|
|
36791
38038
|
await this.ensureDirectory(this.baseDirectory);
|
|
36792
|
-
const entries = await
|
|
38039
|
+
const entries = await fs19.promises.readdir(this.baseDirectory, { withFileTypes: true });
|
|
36793
38040
|
const agentDirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith("_"));
|
|
36794
38041
|
const indexEntries = [];
|
|
36795
38042
|
for (const dir of agentDirs) {
|
|
36796
38043
|
try {
|
|
36797
38044
|
const filePath = path2.join(this.baseDirectory, dir.name, "definition.json");
|
|
36798
|
-
const data = await
|
|
38045
|
+
const data = await fs19.promises.readFile(filePath, "utf-8");
|
|
36799
38046
|
const definition = JSON.parse(data);
|
|
36800
38047
|
indexEntries.push(this.definitionToIndexEntry(definition));
|
|
36801
38048
|
} catch {
|
|
@@ -36813,7 +38060,7 @@ var FileAgentDefinitionStorage = class {
|
|
|
36813
38060
|
// ==========================================================================
|
|
36814
38061
|
async ensureDirectory(dir) {
|
|
36815
38062
|
try {
|
|
36816
|
-
await
|
|
38063
|
+
await fs19.promises.mkdir(dir, { recursive: true });
|
|
36817
38064
|
} catch (error) {
|
|
36818
38065
|
if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
|
|
36819
38066
|
throw error;
|
|
@@ -36823,7 +38070,7 @@ var FileAgentDefinitionStorage = class {
|
|
|
36823
38070
|
async loadRaw(sanitizedId) {
|
|
36824
38071
|
const filePath = path2.join(this.baseDirectory, sanitizedId, "definition.json");
|
|
36825
38072
|
try {
|
|
36826
|
-
const data = await
|
|
38073
|
+
const data = await fs19.promises.readFile(filePath, "utf-8");
|
|
36827
38074
|
return JSON.parse(data);
|
|
36828
38075
|
} catch (error) {
|
|
36829
38076
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
@@ -36841,7 +38088,7 @@ var FileAgentDefinitionStorage = class {
|
|
|
36841
38088
|
return this.index;
|
|
36842
38089
|
}
|
|
36843
38090
|
try {
|
|
36844
|
-
const data = await
|
|
38091
|
+
const data = await fs19.promises.readFile(this.indexPath, "utf-8");
|
|
36845
38092
|
this.index = JSON.parse(data);
|
|
36846
38093
|
return this.index;
|
|
36847
38094
|
} catch (error) {
|
|
@@ -36861,7 +38108,7 @@ var FileAgentDefinitionStorage = class {
|
|
|
36861
38108
|
await this.ensureDirectory(this.baseDirectory);
|
|
36862
38109
|
this.index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
36863
38110
|
const data = this.prettyPrint ? JSON.stringify(this.index, null, 2) : JSON.stringify(this.index);
|
|
36864
|
-
await
|
|
38111
|
+
await fs19.promises.writeFile(this.indexPath, data, "utf-8");
|
|
36865
38112
|
}
|
|
36866
38113
|
async updateIndex(definition) {
|
|
36867
38114
|
const index = await this.loadIndex();
|
|
@@ -36919,10 +38166,10 @@ var FileMediaStorage = class {
|
|
|
36919
38166
|
}
|
|
36920
38167
|
async save(data, metadata) {
|
|
36921
38168
|
const dir = metadata.userId ? path2__namespace.join(this.outputDir, metadata.userId) : this.outputDir;
|
|
36922
|
-
await
|
|
38169
|
+
await fs18__namespace.mkdir(dir, { recursive: true });
|
|
36923
38170
|
const filename = metadata.suggestedFilename ?? this.generateFilename(metadata);
|
|
36924
38171
|
const filePath = path2__namespace.join(dir, filename);
|
|
36925
|
-
await
|
|
38172
|
+
await fs18__namespace.writeFile(filePath, data);
|
|
36926
38173
|
const format = metadata.format.toLowerCase();
|
|
36927
38174
|
const mimeType = MIME_TYPES2[format] ?? "application/octet-stream";
|
|
36928
38175
|
return {
|
|
@@ -36933,7 +38180,7 @@ var FileMediaStorage = class {
|
|
|
36933
38180
|
}
|
|
36934
38181
|
async read(location) {
|
|
36935
38182
|
try {
|
|
36936
|
-
return await
|
|
38183
|
+
return await fs18__namespace.readFile(location);
|
|
36937
38184
|
} catch (err) {
|
|
36938
38185
|
if (err.code === "ENOENT") {
|
|
36939
38186
|
return null;
|
|
@@ -36943,7 +38190,7 @@ var FileMediaStorage = class {
|
|
|
36943
38190
|
}
|
|
36944
38191
|
async delete(location) {
|
|
36945
38192
|
try {
|
|
36946
|
-
await
|
|
38193
|
+
await fs18__namespace.unlink(location);
|
|
36947
38194
|
} catch (err) {
|
|
36948
38195
|
if (err.code === "ENOENT") {
|
|
36949
38196
|
return;
|
|
@@ -36953,7 +38200,7 @@ var FileMediaStorage = class {
|
|
|
36953
38200
|
}
|
|
36954
38201
|
async exists(location) {
|
|
36955
38202
|
try {
|
|
36956
|
-
await
|
|
38203
|
+
await fs18__namespace.access(location);
|
|
36957
38204
|
return true;
|
|
36958
38205
|
} catch {
|
|
36959
38206
|
return false;
|
|
@@ -36962,11 +38209,11 @@ var FileMediaStorage = class {
|
|
|
36962
38209
|
async list(options) {
|
|
36963
38210
|
await this.ensureDir();
|
|
36964
38211
|
let entries = [];
|
|
36965
|
-
const files = await
|
|
38212
|
+
const files = await fs18__namespace.readdir(this.outputDir);
|
|
36966
38213
|
for (const file of files) {
|
|
36967
38214
|
const filePath = path2__namespace.join(this.outputDir, file);
|
|
36968
38215
|
try {
|
|
36969
|
-
const stat6 = await
|
|
38216
|
+
const stat6 = await fs18__namespace.stat(filePath);
|
|
36970
38217
|
if (!stat6.isFile()) continue;
|
|
36971
38218
|
const ext = path2__namespace.extname(file).slice(1).toLowerCase();
|
|
36972
38219
|
const mimeType = MIME_TYPES2[ext] ?? "application/octet-stream";
|
|
@@ -37006,7 +38253,7 @@ var FileMediaStorage = class {
|
|
|
37006
38253
|
}
|
|
37007
38254
|
async ensureDir() {
|
|
37008
38255
|
if (!this.initialized) {
|
|
37009
|
-
await
|
|
38256
|
+
await fs18__namespace.mkdir(this.outputDir, { recursive: true });
|
|
37010
38257
|
this.initialized = true;
|
|
37011
38258
|
}
|
|
37012
38259
|
}
|
|
@@ -37014,58 +38261,82 @@ var FileMediaStorage = class {
|
|
|
37014
38261
|
function createFileMediaStorage(config) {
|
|
37015
38262
|
return new FileMediaStorage(config);
|
|
37016
38263
|
}
|
|
37017
|
-
function
|
|
38264
|
+
function getDefaultBaseDirectory5() {
|
|
37018
38265
|
const platform2 = process.platform;
|
|
37019
38266
|
if (platform2 === "win32") {
|
|
37020
38267
|
const appData = process.env.APPDATA || process.env.LOCALAPPDATA;
|
|
37021
38268
|
if (appData) {
|
|
37022
|
-
return path2.join(appData, "oneringai", "
|
|
38269
|
+
return path2.join(appData, "oneringai", "users");
|
|
37023
38270
|
}
|
|
37024
38271
|
}
|
|
37025
|
-
return path2.join(os2.homedir(), ".oneringai", "
|
|
38272
|
+
return path2.join(os2.homedir(), ".oneringai", "users");
|
|
38273
|
+
}
|
|
38274
|
+
var DEFAULT_USER_ID2 = "default";
|
|
38275
|
+
function sanitizeUserId2(userId) {
|
|
38276
|
+
if (!userId) {
|
|
38277
|
+
return DEFAULT_USER_ID2;
|
|
38278
|
+
}
|
|
38279
|
+
return userId.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase() || DEFAULT_USER_ID2;
|
|
37026
38280
|
}
|
|
37027
38281
|
function sanitizeName(name) {
|
|
37028
38282
|
return name.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase() || "default";
|
|
37029
38283
|
}
|
|
37030
38284
|
var FileCustomToolStorage = class {
|
|
37031
38285
|
baseDirectory;
|
|
37032
|
-
indexPath;
|
|
37033
38286
|
prettyPrint;
|
|
37034
|
-
index = null;
|
|
37035
38287
|
constructor(config = {}) {
|
|
37036
|
-
this.baseDirectory = config.baseDirectory ??
|
|
38288
|
+
this.baseDirectory = config.baseDirectory ?? getDefaultBaseDirectory5();
|
|
37037
38289
|
this.prettyPrint = config.prettyPrint ?? true;
|
|
37038
|
-
|
|
38290
|
+
}
|
|
38291
|
+
/**
|
|
38292
|
+
* Get the directory path for a specific user's custom tools
|
|
38293
|
+
*/
|
|
38294
|
+
getUserDirectory(userId) {
|
|
38295
|
+
const sanitizedId = sanitizeUserId2(userId);
|
|
38296
|
+
return path2.join(this.baseDirectory, sanitizedId, "custom-tools");
|
|
38297
|
+
}
|
|
38298
|
+
/**
|
|
38299
|
+
* Get the index file path for a specific user
|
|
38300
|
+
*/
|
|
38301
|
+
getUserIndexPath(userId) {
|
|
38302
|
+
return path2.join(this.getUserDirectory(userId), "_index.json");
|
|
38303
|
+
}
|
|
38304
|
+
/**
|
|
38305
|
+
* Get the tool file path for a specific user
|
|
38306
|
+
*/
|
|
38307
|
+
getToolPath(userId, sanitizedName) {
|
|
38308
|
+
return path2.join(this.getUserDirectory(userId), `${sanitizedName}.json`);
|
|
37039
38309
|
}
|
|
37040
38310
|
/**
|
|
37041
38311
|
* Save a custom tool definition
|
|
37042
38312
|
*/
|
|
37043
|
-
async save(definition) {
|
|
38313
|
+
async save(userId, definition) {
|
|
38314
|
+
const directory = this.getUserDirectory(userId);
|
|
37044
38315
|
const sanitized = sanitizeName(definition.name);
|
|
37045
|
-
const filePath =
|
|
37046
|
-
await this.ensureDirectory(
|
|
38316
|
+
const filePath = this.getToolPath(userId, sanitized);
|
|
38317
|
+
await this.ensureDirectory(directory);
|
|
37047
38318
|
const data = this.prettyPrint ? JSON.stringify(definition, null, 2) : JSON.stringify(definition);
|
|
37048
38319
|
const tempPath = `${filePath}.tmp`;
|
|
37049
38320
|
try {
|
|
37050
|
-
await
|
|
37051
|
-
await
|
|
38321
|
+
await fs19.promises.writeFile(tempPath, data, "utf-8");
|
|
38322
|
+
await fs19.promises.rename(tempPath, filePath);
|
|
37052
38323
|
} catch (error) {
|
|
37053
38324
|
try {
|
|
37054
|
-
await
|
|
38325
|
+
await fs19.promises.unlink(tempPath);
|
|
37055
38326
|
} catch {
|
|
37056
38327
|
}
|
|
37057
38328
|
throw error;
|
|
37058
38329
|
}
|
|
37059
|
-
await this.updateIndex(definition);
|
|
38330
|
+
await this.updateIndex(userId, definition);
|
|
37060
38331
|
}
|
|
37061
38332
|
/**
|
|
37062
38333
|
* Load a custom tool definition by name
|
|
37063
38334
|
*/
|
|
37064
|
-
async load(name) {
|
|
38335
|
+
async load(userId, name) {
|
|
37065
38336
|
const sanitized = sanitizeName(name);
|
|
37066
|
-
const filePath =
|
|
38337
|
+
const filePath = this.getToolPath(userId, sanitized);
|
|
37067
38338
|
try {
|
|
37068
|
-
const data = await
|
|
38339
|
+
const data = await fs19.promises.readFile(filePath, "utf-8");
|
|
37069
38340
|
return JSON.parse(data);
|
|
37070
38341
|
} catch (error) {
|
|
37071
38342
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
@@ -37080,26 +38351,26 @@ var FileCustomToolStorage = class {
|
|
|
37080
38351
|
/**
|
|
37081
38352
|
* Delete a custom tool definition
|
|
37082
38353
|
*/
|
|
37083
|
-
async delete(name) {
|
|
38354
|
+
async delete(userId, name) {
|
|
37084
38355
|
const sanitized = sanitizeName(name);
|
|
37085
|
-
const filePath =
|
|
38356
|
+
const filePath = this.getToolPath(userId, sanitized);
|
|
37086
38357
|
try {
|
|
37087
|
-
await
|
|
38358
|
+
await fs19.promises.unlink(filePath);
|
|
37088
38359
|
} catch (error) {
|
|
37089
38360
|
if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
|
|
37090
38361
|
throw error;
|
|
37091
38362
|
}
|
|
37092
38363
|
}
|
|
37093
|
-
await this.removeFromIndex(name);
|
|
38364
|
+
await this.removeFromIndex(userId, name);
|
|
37094
38365
|
}
|
|
37095
38366
|
/**
|
|
37096
38367
|
* Check if a custom tool exists
|
|
37097
38368
|
*/
|
|
37098
|
-
async exists(name) {
|
|
38369
|
+
async exists(userId, name) {
|
|
37099
38370
|
const sanitized = sanitizeName(name);
|
|
37100
|
-
const filePath =
|
|
38371
|
+
const filePath = this.getToolPath(userId, sanitized);
|
|
37101
38372
|
try {
|
|
37102
|
-
await
|
|
38373
|
+
await fs19.promises.access(filePath);
|
|
37103
38374
|
return true;
|
|
37104
38375
|
} catch {
|
|
37105
38376
|
return false;
|
|
@@ -37108,8 +38379,8 @@ var FileCustomToolStorage = class {
|
|
|
37108
38379
|
/**
|
|
37109
38380
|
* List custom tools (summaries only)
|
|
37110
38381
|
*/
|
|
37111
|
-
async list(options) {
|
|
37112
|
-
const index = await this.loadIndex();
|
|
38382
|
+
async list(userId, options) {
|
|
38383
|
+
const index = await this.loadIndex(userId);
|
|
37113
38384
|
let entries = [...index.tools];
|
|
37114
38385
|
if (options?.tags && options.tags.length > 0) {
|
|
37115
38386
|
entries = entries.filter((e) => {
|
|
@@ -37150,62 +38421,59 @@ var FileCustomToolStorage = class {
|
|
|
37150
38421
|
/**
|
|
37151
38422
|
* Update metadata without loading full definition
|
|
37152
38423
|
*/
|
|
37153
|
-
async updateMetadata(name, metadata) {
|
|
37154
|
-
const definition = await this.load(name);
|
|
38424
|
+
async updateMetadata(userId, name, metadata) {
|
|
38425
|
+
const definition = await this.load(userId, name);
|
|
37155
38426
|
if (!definition) {
|
|
37156
38427
|
throw new Error(`Custom tool '${name}' not found`);
|
|
37157
38428
|
}
|
|
37158
38429
|
definition.metadata = { ...definition.metadata, ...metadata };
|
|
37159
38430
|
definition.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
37160
|
-
await this.save(definition);
|
|
38431
|
+
await this.save(userId, definition);
|
|
37161
38432
|
}
|
|
37162
38433
|
/**
|
|
37163
|
-
* Get storage path
|
|
38434
|
+
* Get storage path for a specific user
|
|
37164
38435
|
*/
|
|
37165
|
-
getPath() {
|
|
37166
|
-
return this.
|
|
38436
|
+
getPath(userId) {
|
|
38437
|
+
return this.getUserDirectory(userId);
|
|
37167
38438
|
}
|
|
37168
38439
|
// ==========================================================================
|
|
37169
38440
|
// Private Helpers
|
|
37170
38441
|
// ==========================================================================
|
|
37171
38442
|
async ensureDirectory(dir) {
|
|
37172
38443
|
try {
|
|
37173
|
-
await
|
|
38444
|
+
await fs19.promises.mkdir(dir, { recursive: true });
|
|
37174
38445
|
} catch (error) {
|
|
37175
38446
|
if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
|
|
37176
38447
|
throw error;
|
|
37177
38448
|
}
|
|
37178
38449
|
}
|
|
37179
38450
|
}
|
|
37180
|
-
async loadIndex() {
|
|
37181
|
-
|
|
37182
|
-
return this.index;
|
|
37183
|
-
}
|
|
38451
|
+
async loadIndex(userId) {
|
|
38452
|
+
const indexPath = this.getUserIndexPath(userId);
|
|
37184
38453
|
try {
|
|
37185
|
-
const data = await
|
|
37186
|
-
|
|
37187
|
-
return this.index;
|
|
38454
|
+
const data = await fs19.promises.readFile(indexPath, "utf-8");
|
|
38455
|
+
return JSON.parse(data);
|
|
37188
38456
|
} catch (error) {
|
|
37189
38457
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
37190
|
-
|
|
38458
|
+
return {
|
|
37191
38459
|
version: 1,
|
|
37192
38460
|
tools: [],
|
|
37193
38461
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
37194
38462
|
};
|
|
37195
|
-
return this.index;
|
|
37196
38463
|
}
|
|
37197
38464
|
throw error;
|
|
37198
38465
|
}
|
|
37199
38466
|
}
|
|
37200
|
-
async saveIndex() {
|
|
37201
|
-
|
|
37202
|
-
|
|
37203
|
-
this.
|
|
37204
|
-
|
|
37205
|
-
|
|
38467
|
+
async saveIndex(userId, index) {
|
|
38468
|
+
const directory = this.getUserDirectory(userId);
|
|
38469
|
+
const indexPath = this.getUserIndexPath(userId);
|
|
38470
|
+
await this.ensureDirectory(directory);
|
|
38471
|
+
index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
38472
|
+
const data = this.prettyPrint ? JSON.stringify(index, null, 2) : JSON.stringify(index);
|
|
38473
|
+
await fs19.promises.writeFile(indexPath, data, "utf-8");
|
|
37206
38474
|
}
|
|
37207
|
-
async updateIndex(definition) {
|
|
37208
|
-
const index = await this.loadIndex();
|
|
38475
|
+
async updateIndex(userId, definition) {
|
|
38476
|
+
const index = await this.loadIndex(userId);
|
|
37209
38477
|
const entry = this.definitionToIndexEntry(definition);
|
|
37210
38478
|
const existingIdx = index.tools.findIndex((e) => e.name === definition.name);
|
|
37211
38479
|
if (existingIdx >= 0) {
|
|
@@ -37213,12 +38481,12 @@ var FileCustomToolStorage = class {
|
|
|
37213
38481
|
} else {
|
|
37214
38482
|
index.tools.push(entry);
|
|
37215
38483
|
}
|
|
37216
|
-
await this.saveIndex();
|
|
38484
|
+
await this.saveIndex(userId, index);
|
|
37217
38485
|
}
|
|
37218
|
-
async removeFromIndex(name) {
|
|
37219
|
-
const index = await this.loadIndex();
|
|
38486
|
+
async removeFromIndex(userId, name) {
|
|
38487
|
+
const index = await this.loadIndex(userId);
|
|
37220
38488
|
index.tools = index.tools.filter((e) => e.name !== name);
|
|
37221
|
-
await this.saveIndex();
|
|
38489
|
+
await this.saveIndex(userId, index);
|
|
37222
38490
|
}
|
|
37223
38491
|
definitionToIndexEntry(definition) {
|
|
37224
38492
|
return {
|
|
@@ -37235,6 +38503,234 @@ var FileCustomToolStorage = class {
|
|
|
37235
38503
|
function createFileCustomToolStorage(config) {
|
|
37236
38504
|
return new FileCustomToolStorage(config);
|
|
37237
38505
|
}
|
|
38506
|
+
var STORAGE_VERSION = 1;
|
|
38507
|
+
var DEFAULT_USER_ID3 = "default";
|
|
38508
|
+
function getDefaultBaseDirectory6() {
|
|
38509
|
+
const platform2 = process.platform;
|
|
38510
|
+
if (platform2 === "win32") {
|
|
38511
|
+
const appData = process.env.APPDATA || process.env.LOCALAPPDATA;
|
|
38512
|
+
if (appData) {
|
|
38513
|
+
return path2.join(appData, "oneringai", "users");
|
|
38514
|
+
}
|
|
38515
|
+
}
|
|
38516
|
+
return path2.join(os2.homedir(), ".oneringai", "users");
|
|
38517
|
+
}
|
|
38518
|
+
function sanitizeUserId3(userId) {
|
|
38519
|
+
if (!userId) {
|
|
38520
|
+
return DEFAULT_USER_ID3;
|
|
38521
|
+
}
|
|
38522
|
+
return userId.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase() || DEFAULT_USER_ID3;
|
|
38523
|
+
}
|
|
38524
|
+
function sanitizeId2(id) {
|
|
38525
|
+
return id.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase() || "default";
|
|
38526
|
+
}
|
|
38527
|
+
var FileRoutineDefinitionStorage = class {
|
|
38528
|
+
baseDirectory;
|
|
38529
|
+
prettyPrint;
|
|
38530
|
+
constructor(config = {}) {
|
|
38531
|
+
this.baseDirectory = config.baseDirectory ?? getDefaultBaseDirectory6();
|
|
38532
|
+
this.prettyPrint = config.prettyPrint ?? true;
|
|
38533
|
+
}
|
|
38534
|
+
getUserDirectory(userId) {
|
|
38535
|
+
const sanitizedId = sanitizeUserId3(userId);
|
|
38536
|
+
return path2.join(this.baseDirectory, sanitizedId, "routines");
|
|
38537
|
+
}
|
|
38538
|
+
getIndexPath(userId) {
|
|
38539
|
+
return path2.join(this.getUserDirectory(userId), "_index.json");
|
|
38540
|
+
}
|
|
38541
|
+
getRoutinePath(userId, sanitizedId) {
|
|
38542
|
+
return path2.join(this.getUserDirectory(userId), `${sanitizedId}.json`);
|
|
38543
|
+
}
|
|
38544
|
+
async save(userId, definition) {
|
|
38545
|
+
const directory = this.getUserDirectory(userId);
|
|
38546
|
+
const sanitized = sanitizeId2(definition.id);
|
|
38547
|
+
const filePath = this.getRoutinePath(userId, sanitized);
|
|
38548
|
+
await this.ensureDirectory(directory);
|
|
38549
|
+
const stored = { version: STORAGE_VERSION, definition };
|
|
38550
|
+
const data = this.prettyPrint ? JSON.stringify(stored, null, 2) : JSON.stringify(stored);
|
|
38551
|
+
const tempPath = `${filePath}.tmp`;
|
|
38552
|
+
try {
|
|
38553
|
+
await fs19.promises.writeFile(tempPath, data, "utf-8");
|
|
38554
|
+
await fs19.promises.rename(tempPath, filePath);
|
|
38555
|
+
} catch (error) {
|
|
38556
|
+
try {
|
|
38557
|
+
await fs19.promises.unlink(tempPath);
|
|
38558
|
+
} catch {
|
|
38559
|
+
}
|
|
38560
|
+
throw error;
|
|
38561
|
+
}
|
|
38562
|
+
await this.updateIndex(userId, definition);
|
|
38563
|
+
}
|
|
38564
|
+
async load(userId, id) {
|
|
38565
|
+
const sanitized = sanitizeId2(id);
|
|
38566
|
+
const filePath = this.getRoutinePath(userId, sanitized);
|
|
38567
|
+
try {
|
|
38568
|
+
const data = await fs19.promises.readFile(filePath, "utf-8");
|
|
38569
|
+
const stored = JSON.parse(data);
|
|
38570
|
+
return stored.definition;
|
|
38571
|
+
} catch (error) {
|
|
38572
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
38573
|
+
return null;
|
|
38574
|
+
}
|
|
38575
|
+
if (error instanceof SyntaxError) {
|
|
38576
|
+
return null;
|
|
38577
|
+
}
|
|
38578
|
+
throw error;
|
|
38579
|
+
}
|
|
38580
|
+
}
|
|
38581
|
+
async delete(userId, id) {
|
|
38582
|
+
const sanitized = sanitizeId2(id);
|
|
38583
|
+
const filePath = this.getRoutinePath(userId, sanitized);
|
|
38584
|
+
try {
|
|
38585
|
+
await fs19.promises.unlink(filePath);
|
|
38586
|
+
} catch (error) {
|
|
38587
|
+
if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
|
|
38588
|
+
throw error;
|
|
38589
|
+
}
|
|
38590
|
+
}
|
|
38591
|
+
await this.removeFromIndex(userId, id);
|
|
38592
|
+
}
|
|
38593
|
+
async exists(userId, id) {
|
|
38594
|
+
const sanitized = sanitizeId2(id);
|
|
38595
|
+
const filePath = this.getRoutinePath(userId, sanitized);
|
|
38596
|
+
try {
|
|
38597
|
+
await fs19.promises.access(filePath);
|
|
38598
|
+
return true;
|
|
38599
|
+
} catch {
|
|
38600
|
+
return false;
|
|
38601
|
+
}
|
|
38602
|
+
}
|
|
38603
|
+
async list(userId, options) {
|
|
38604
|
+
const index = await this.loadIndex(userId);
|
|
38605
|
+
let entries = [...index.routines];
|
|
38606
|
+
if (options?.tags && options.tags.length > 0) {
|
|
38607
|
+
entries = entries.filter((e) => {
|
|
38608
|
+
const entryTags = e.tags ?? [];
|
|
38609
|
+
return options.tags.some((t) => entryTags.includes(t));
|
|
38610
|
+
});
|
|
38611
|
+
}
|
|
38612
|
+
if (options?.search) {
|
|
38613
|
+
const searchLower = options.search.toLowerCase();
|
|
38614
|
+
entries = entries.filter(
|
|
38615
|
+
(e) => e.name.toLowerCase().includes(searchLower) || e.description.toLowerCase().includes(searchLower)
|
|
38616
|
+
);
|
|
38617
|
+
}
|
|
38618
|
+
entries.sort(
|
|
38619
|
+
(a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
|
|
38620
|
+
);
|
|
38621
|
+
if (options?.offset) {
|
|
38622
|
+
entries = entries.slice(options.offset);
|
|
38623
|
+
}
|
|
38624
|
+
if (options?.limit) {
|
|
38625
|
+
entries = entries.slice(0, options.limit);
|
|
38626
|
+
}
|
|
38627
|
+
const results = [];
|
|
38628
|
+
for (const entry of entries) {
|
|
38629
|
+
const def = await this.load(userId, entry.id);
|
|
38630
|
+
if (def) {
|
|
38631
|
+
results.push(def);
|
|
38632
|
+
}
|
|
38633
|
+
}
|
|
38634
|
+
return results;
|
|
38635
|
+
}
|
|
38636
|
+
getPath(userId) {
|
|
38637
|
+
return this.getUserDirectory(userId);
|
|
38638
|
+
}
|
|
38639
|
+
// ==========================================================================
|
|
38640
|
+
// Private Helpers
|
|
38641
|
+
// ==========================================================================
|
|
38642
|
+
async ensureDirectory(dir) {
|
|
38643
|
+
try {
|
|
38644
|
+
await fs19.promises.mkdir(dir, { recursive: true });
|
|
38645
|
+
} catch (error) {
|
|
38646
|
+
if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
|
|
38647
|
+
throw error;
|
|
38648
|
+
}
|
|
38649
|
+
}
|
|
38650
|
+
}
|
|
38651
|
+
async loadIndex(userId) {
|
|
38652
|
+
const indexPath = this.getIndexPath(userId);
|
|
38653
|
+
try {
|
|
38654
|
+
const data = await fs19.promises.readFile(indexPath, "utf-8");
|
|
38655
|
+
return JSON.parse(data);
|
|
38656
|
+
} catch (error) {
|
|
38657
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
38658
|
+
return await this.rebuildIndex(userId);
|
|
38659
|
+
}
|
|
38660
|
+
throw error;
|
|
38661
|
+
}
|
|
38662
|
+
}
|
|
38663
|
+
async saveIndex(userId, index) {
|
|
38664
|
+
const directory = this.getUserDirectory(userId);
|
|
38665
|
+
const indexPath = this.getIndexPath(userId);
|
|
38666
|
+
await this.ensureDirectory(directory);
|
|
38667
|
+
index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
38668
|
+
const data = this.prettyPrint ? JSON.stringify(index, null, 2) : JSON.stringify(index);
|
|
38669
|
+
await fs19.promises.writeFile(indexPath, data, "utf-8");
|
|
38670
|
+
}
|
|
38671
|
+
async updateIndex(userId, definition) {
|
|
38672
|
+
const index = await this.loadIndex(userId);
|
|
38673
|
+
const entry = this.definitionToIndexEntry(definition);
|
|
38674
|
+
const existingIdx = index.routines.findIndex((e) => e.id === definition.id);
|
|
38675
|
+
if (existingIdx >= 0) {
|
|
38676
|
+
index.routines[existingIdx] = entry;
|
|
38677
|
+
} else {
|
|
38678
|
+
index.routines.push(entry);
|
|
38679
|
+
}
|
|
38680
|
+
await this.saveIndex(userId, index);
|
|
38681
|
+
}
|
|
38682
|
+
async removeFromIndex(userId, id) {
|
|
38683
|
+
const index = await this.loadIndex(userId);
|
|
38684
|
+
index.routines = index.routines.filter((e) => e.id !== id);
|
|
38685
|
+
await this.saveIndex(userId, index);
|
|
38686
|
+
}
|
|
38687
|
+
definitionToIndexEntry(definition) {
|
|
38688
|
+
return {
|
|
38689
|
+
id: definition.id,
|
|
38690
|
+
name: definition.name,
|
|
38691
|
+
description: definition.description,
|
|
38692
|
+
tags: definition.tags,
|
|
38693
|
+
author: definition.author,
|
|
38694
|
+
updatedAt: definition.updatedAt
|
|
38695
|
+
};
|
|
38696
|
+
}
|
|
38697
|
+
/**
|
|
38698
|
+
* Rebuild index by scanning directory for .json files (excluding _index.json).
|
|
38699
|
+
* Returns empty index if directory doesn't exist.
|
|
38700
|
+
*/
|
|
38701
|
+
async rebuildIndex(userId) {
|
|
38702
|
+
const directory = this.getUserDirectory(userId);
|
|
38703
|
+
const index = {
|
|
38704
|
+
version: 1,
|
|
38705
|
+
routines: [],
|
|
38706
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
38707
|
+
};
|
|
38708
|
+
let files;
|
|
38709
|
+
try {
|
|
38710
|
+
files = await fs19.promises.readdir(directory);
|
|
38711
|
+
} catch {
|
|
38712
|
+
return index;
|
|
38713
|
+
}
|
|
38714
|
+
for (const file of files) {
|
|
38715
|
+
if (!file.endsWith(".json") || file === "_index.json") continue;
|
|
38716
|
+
try {
|
|
38717
|
+
const data = await fs19.promises.readFile(path2.join(directory, file), "utf-8");
|
|
38718
|
+
const stored = JSON.parse(data);
|
|
38719
|
+
if (stored.definition) {
|
|
38720
|
+
index.routines.push(this.definitionToIndexEntry(stored.definition));
|
|
38721
|
+
}
|
|
38722
|
+
} catch {
|
|
38723
|
+
}
|
|
38724
|
+
}
|
|
38725
|
+
if (index.routines.length > 0) {
|
|
38726
|
+
await this.saveIndex(userId, index);
|
|
38727
|
+
}
|
|
38728
|
+
return index;
|
|
38729
|
+
}
|
|
38730
|
+
};
|
|
38731
|
+
function createFileRoutineDefinitionStorage(config) {
|
|
38732
|
+
return new FileRoutineDefinitionStorage(config);
|
|
38733
|
+
}
|
|
37238
38734
|
|
|
37239
38735
|
// src/domain/entities/CustomToolDefinition.ts
|
|
37240
38736
|
var CUSTOM_TOOL_DEFINITION_VERSION = 1;
|
|
@@ -38335,8 +39831,8 @@ var FileStorage = class {
|
|
|
38335
39831
|
}
|
|
38336
39832
|
async ensureDirectory() {
|
|
38337
39833
|
try {
|
|
38338
|
-
await
|
|
38339
|
-
await
|
|
39834
|
+
await fs18__namespace.mkdir(this.directory, { recursive: true });
|
|
39835
|
+
await fs18__namespace.chmod(this.directory, 448);
|
|
38340
39836
|
} catch (error) {
|
|
38341
39837
|
}
|
|
38342
39838
|
}
|
|
@@ -38352,13 +39848,13 @@ var FileStorage = class {
|
|
|
38352
39848
|
const filePath = this.getFilePath(key);
|
|
38353
39849
|
const plaintext = JSON.stringify(token);
|
|
38354
39850
|
const encrypted = encrypt(plaintext, this.encryptionKey);
|
|
38355
|
-
await
|
|
38356
|
-
await
|
|
39851
|
+
await fs18__namespace.writeFile(filePath, encrypted, "utf8");
|
|
39852
|
+
await fs18__namespace.chmod(filePath, 384);
|
|
38357
39853
|
}
|
|
38358
39854
|
async getToken(key) {
|
|
38359
39855
|
const filePath = this.getFilePath(key);
|
|
38360
39856
|
try {
|
|
38361
|
-
const encrypted = await
|
|
39857
|
+
const encrypted = await fs18__namespace.readFile(filePath, "utf8");
|
|
38362
39858
|
const decrypted = decrypt(encrypted, this.encryptionKey);
|
|
38363
39859
|
return JSON.parse(decrypted);
|
|
38364
39860
|
} catch (error) {
|
|
@@ -38367,7 +39863,7 @@ var FileStorage = class {
|
|
|
38367
39863
|
}
|
|
38368
39864
|
console.error("Failed to read/decrypt token file:", error);
|
|
38369
39865
|
try {
|
|
38370
|
-
await
|
|
39866
|
+
await fs18__namespace.unlink(filePath);
|
|
38371
39867
|
} catch {
|
|
38372
39868
|
}
|
|
38373
39869
|
return null;
|
|
@@ -38376,7 +39872,7 @@ var FileStorage = class {
|
|
|
38376
39872
|
async deleteToken(key) {
|
|
38377
39873
|
const filePath = this.getFilePath(key);
|
|
38378
39874
|
try {
|
|
38379
|
-
await
|
|
39875
|
+
await fs18__namespace.unlink(filePath);
|
|
38380
39876
|
} catch (error) {
|
|
38381
39877
|
if (error.code !== "ENOENT") {
|
|
38382
39878
|
throw error;
|
|
@@ -38386,7 +39882,7 @@ var FileStorage = class {
|
|
|
38386
39882
|
async hasToken(key) {
|
|
38387
39883
|
const filePath = this.getFilePath(key);
|
|
38388
39884
|
try {
|
|
38389
|
-
await
|
|
39885
|
+
await fs18__namespace.access(filePath);
|
|
38390
39886
|
return true;
|
|
38391
39887
|
} catch {
|
|
38392
39888
|
return false;
|
|
@@ -38397,7 +39893,7 @@ var FileStorage = class {
|
|
|
38397
39893
|
*/
|
|
38398
39894
|
async listTokens() {
|
|
38399
39895
|
try {
|
|
38400
|
-
const files = await
|
|
39896
|
+
const files = await fs18__namespace.readdir(this.directory);
|
|
38401
39897
|
return files.filter((f) => f.endsWith(".token")).map((f) => f.replace(".token", ""));
|
|
38402
39898
|
} catch {
|
|
38403
39899
|
return [];
|
|
@@ -38408,10 +39904,10 @@ var FileStorage = class {
|
|
|
38408
39904
|
*/
|
|
38409
39905
|
async clearAll() {
|
|
38410
39906
|
try {
|
|
38411
|
-
const files = await
|
|
39907
|
+
const files = await fs18__namespace.readdir(this.directory);
|
|
38412
39908
|
const tokenFiles = files.filter((f) => f.endsWith(".token"));
|
|
38413
39909
|
await Promise.all(
|
|
38414
|
-
tokenFiles.map((f) =>
|
|
39910
|
+
tokenFiles.map((f) => fs18__namespace.unlink(path2__namespace.join(this.directory, f)).catch(() => {
|
|
38415
39911
|
}))
|
|
38416
39912
|
);
|
|
38417
39913
|
} catch {
|
|
@@ -38859,14 +40355,14 @@ var FileConnectorStorage = class {
|
|
|
38859
40355
|
await this.ensureDirectory();
|
|
38860
40356
|
const filePath = this.getFilePath(name);
|
|
38861
40357
|
const json = JSON.stringify(stored, null, 2);
|
|
38862
|
-
await
|
|
38863
|
-
await
|
|
40358
|
+
await fs18__namespace.writeFile(filePath, json, "utf8");
|
|
40359
|
+
await fs18__namespace.chmod(filePath, 384);
|
|
38864
40360
|
await this.updateIndex(name, "add");
|
|
38865
40361
|
}
|
|
38866
40362
|
async get(name) {
|
|
38867
40363
|
const filePath = this.getFilePath(name);
|
|
38868
40364
|
try {
|
|
38869
|
-
const json = await
|
|
40365
|
+
const json = await fs18__namespace.readFile(filePath, "utf8");
|
|
38870
40366
|
return JSON.parse(json);
|
|
38871
40367
|
} catch (error) {
|
|
38872
40368
|
const err = error;
|
|
@@ -38879,7 +40375,7 @@ var FileConnectorStorage = class {
|
|
|
38879
40375
|
async delete(name) {
|
|
38880
40376
|
const filePath = this.getFilePath(name);
|
|
38881
40377
|
try {
|
|
38882
|
-
await
|
|
40378
|
+
await fs18__namespace.unlink(filePath);
|
|
38883
40379
|
await this.updateIndex(name, "remove");
|
|
38884
40380
|
return true;
|
|
38885
40381
|
} catch (error) {
|
|
@@ -38893,7 +40389,7 @@ var FileConnectorStorage = class {
|
|
|
38893
40389
|
async has(name) {
|
|
38894
40390
|
const filePath = this.getFilePath(name);
|
|
38895
40391
|
try {
|
|
38896
|
-
await
|
|
40392
|
+
await fs18__namespace.access(filePath);
|
|
38897
40393
|
return true;
|
|
38898
40394
|
} catch {
|
|
38899
40395
|
return false;
|
|
@@ -38919,13 +40415,13 @@ var FileConnectorStorage = class {
|
|
|
38919
40415
|
*/
|
|
38920
40416
|
async clear() {
|
|
38921
40417
|
try {
|
|
38922
|
-
const files = await
|
|
40418
|
+
const files = await fs18__namespace.readdir(this.directory);
|
|
38923
40419
|
const connectorFiles = files.filter(
|
|
38924
40420
|
(f) => f.endsWith(".connector.json") || f === "_index.json"
|
|
38925
40421
|
);
|
|
38926
40422
|
await Promise.all(
|
|
38927
40423
|
connectorFiles.map(
|
|
38928
|
-
(f) =>
|
|
40424
|
+
(f) => fs18__namespace.unlink(path2__namespace.join(this.directory, f)).catch(() => {
|
|
38929
40425
|
})
|
|
38930
40426
|
)
|
|
38931
40427
|
);
|
|
@@ -38952,8 +40448,8 @@ var FileConnectorStorage = class {
|
|
|
38952
40448
|
async ensureDirectory() {
|
|
38953
40449
|
if (this.initialized) return;
|
|
38954
40450
|
try {
|
|
38955
|
-
await
|
|
38956
|
-
await
|
|
40451
|
+
await fs18__namespace.mkdir(this.directory, { recursive: true });
|
|
40452
|
+
await fs18__namespace.chmod(this.directory, 448);
|
|
38957
40453
|
this.initialized = true;
|
|
38958
40454
|
} catch {
|
|
38959
40455
|
this.initialized = true;
|
|
@@ -38964,7 +40460,7 @@ var FileConnectorStorage = class {
|
|
|
38964
40460
|
*/
|
|
38965
40461
|
async loadIndex() {
|
|
38966
40462
|
try {
|
|
38967
|
-
const json = await
|
|
40463
|
+
const json = await fs18__namespace.readFile(this.indexPath, "utf8");
|
|
38968
40464
|
return JSON.parse(json);
|
|
38969
40465
|
} catch {
|
|
38970
40466
|
return { connectors: {} };
|
|
@@ -38982,8 +40478,8 @@ var FileConnectorStorage = class {
|
|
|
38982
40478
|
delete index.connectors[hash];
|
|
38983
40479
|
}
|
|
38984
40480
|
const json = JSON.stringify(index, null, 2);
|
|
38985
|
-
await
|
|
38986
|
-
await
|
|
40481
|
+
await fs18__namespace.writeFile(this.indexPath, json, "utf8");
|
|
40482
|
+
await fs18__namespace.chmod(this.indexPath, 384);
|
|
38987
40483
|
}
|
|
38988
40484
|
};
|
|
38989
40485
|
|
|
@@ -41438,8 +42934,8 @@ function createMessageWithImages(text, imageUrls, role = "user" /* USER */) {
|
|
|
41438
42934
|
var execAsync = util.promisify(child_process.exec);
|
|
41439
42935
|
function cleanupTempFile(filePath) {
|
|
41440
42936
|
try {
|
|
41441
|
-
if (
|
|
41442
|
-
|
|
42937
|
+
if (fs19__namespace.existsSync(filePath)) {
|
|
42938
|
+
fs19__namespace.unlinkSync(filePath);
|
|
41443
42939
|
}
|
|
41444
42940
|
} catch {
|
|
41445
42941
|
}
|
|
@@ -41490,7 +42986,7 @@ async function readClipboardImageMac() {
|
|
|
41490
42986
|
end try
|
|
41491
42987
|
`;
|
|
41492
42988
|
const { stdout } = await execAsync(`osascript -e '${script}'`);
|
|
41493
|
-
if (stdout.includes("success") ||
|
|
42989
|
+
if (stdout.includes("success") || fs19__namespace.existsSync(tempFile)) {
|
|
41494
42990
|
return await convertFileToDataUri(tempFile);
|
|
41495
42991
|
}
|
|
41496
42992
|
return {
|
|
@@ -41507,14 +43003,14 @@ async function readClipboardImageLinux() {
|
|
|
41507
43003
|
try {
|
|
41508
43004
|
try {
|
|
41509
43005
|
await execAsync(`xclip -selection clipboard -t image/png -o > "${tempFile}"`);
|
|
41510
|
-
if (
|
|
43006
|
+
if (fs19__namespace.existsSync(tempFile) && fs19__namespace.statSync(tempFile).size > 0) {
|
|
41511
43007
|
return await convertFileToDataUri(tempFile);
|
|
41512
43008
|
}
|
|
41513
43009
|
} catch {
|
|
41514
43010
|
}
|
|
41515
43011
|
try {
|
|
41516
43012
|
await execAsync(`wl-paste -t image/png > "${tempFile}"`);
|
|
41517
|
-
if (
|
|
43013
|
+
if (fs19__namespace.existsSync(tempFile) && fs19__namespace.statSync(tempFile).size > 0) {
|
|
41518
43014
|
return await convertFileToDataUri(tempFile);
|
|
41519
43015
|
}
|
|
41520
43016
|
} catch {
|
|
@@ -41541,7 +43037,7 @@ async function readClipboardImageWindows() {
|
|
|
41541
43037
|
}
|
|
41542
43038
|
`;
|
|
41543
43039
|
await execAsync(`powershell -Command "${psScript}"`);
|
|
41544
|
-
if (
|
|
43040
|
+
if (fs19__namespace.existsSync(tempFile) && fs19__namespace.statSync(tempFile).size > 0) {
|
|
41545
43041
|
return await convertFileToDataUri(tempFile);
|
|
41546
43042
|
}
|
|
41547
43043
|
return {
|
|
@@ -41554,7 +43050,7 @@ async function readClipboardImageWindows() {
|
|
|
41554
43050
|
}
|
|
41555
43051
|
async function convertFileToDataUri(filePath) {
|
|
41556
43052
|
try {
|
|
41557
|
-
const imageBuffer =
|
|
43053
|
+
const imageBuffer = fs19__namespace.readFileSync(filePath);
|
|
41558
43054
|
const base64Image = imageBuffer.toString("base64");
|
|
41559
43055
|
const magic = imageBuffer.slice(0, 4).toString("hex");
|
|
41560
43056
|
let mimeType = "image/png";
|
|
@@ -41613,186 +43109,6 @@ async function hasClipboardImage() {
|
|
|
41613
43109
|
}
|
|
41614
43110
|
}
|
|
41615
43111
|
|
|
41616
|
-
// src/utils/jsonExtractor.ts
|
|
41617
|
-
function extractJSON(text) {
|
|
41618
|
-
if (!text || typeof text !== "string") {
|
|
41619
|
-
return {
|
|
41620
|
-
success: false,
|
|
41621
|
-
error: "Input is empty or not a string"
|
|
41622
|
-
};
|
|
41623
|
-
}
|
|
41624
|
-
const trimmedText = text.trim();
|
|
41625
|
-
const codeBlockResult = extractFromCodeBlock(trimmedText);
|
|
41626
|
-
if (codeBlockResult.success) {
|
|
41627
|
-
return codeBlockResult;
|
|
41628
|
-
}
|
|
41629
|
-
const inlineResult = extractInlineJSON(trimmedText);
|
|
41630
|
-
if (inlineResult.success) {
|
|
41631
|
-
return inlineResult;
|
|
41632
|
-
}
|
|
41633
|
-
try {
|
|
41634
|
-
const data = JSON.parse(trimmedText);
|
|
41635
|
-
return {
|
|
41636
|
-
success: true,
|
|
41637
|
-
data,
|
|
41638
|
-
rawJson: trimmedText,
|
|
41639
|
-
method: "raw"
|
|
41640
|
-
};
|
|
41641
|
-
} catch (e) {
|
|
41642
|
-
return {
|
|
41643
|
-
success: false,
|
|
41644
|
-
error: `Could not extract JSON from text: ${e instanceof Error ? e.message : String(e)}`
|
|
41645
|
-
};
|
|
41646
|
-
}
|
|
41647
|
-
}
|
|
41648
|
-
function extractFromCodeBlock(text) {
|
|
41649
|
-
const codeBlockRegex = /```(?:json)?\s*([\s\S]*?)```/g;
|
|
41650
|
-
let match;
|
|
41651
|
-
while ((match = codeBlockRegex.exec(text)) !== null) {
|
|
41652
|
-
const content = match[1];
|
|
41653
|
-
if (content) {
|
|
41654
|
-
const trimmed = content.trim();
|
|
41655
|
-
try {
|
|
41656
|
-
const data = JSON.parse(trimmed);
|
|
41657
|
-
return {
|
|
41658
|
-
success: true,
|
|
41659
|
-
data,
|
|
41660
|
-
rawJson: trimmed,
|
|
41661
|
-
method: "code_block"
|
|
41662
|
-
};
|
|
41663
|
-
} catch {
|
|
41664
|
-
continue;
|
|
41665
|
-
}
|
|
41666
|
-
}
|
|
41667
|
-
}
|
|
41668
|
-
return { success: false };
|
|
41669
|
-
}
|
|
41670
|
-
function extractInlineJSON(text) {
|
|
41671
|
-
const objectMatch = findJSONObject(text);
|
|
41672
|
-
if (objectMatch) {
|
|
41673
|
-
try {
|
|
41674
|
-
const data = JSON.parse(objectMatch);
|
|
41675
|
-
return {
|
|
41676
|
-
success: true,
|
|
41677
|
-
data,
|
|
41678
|
-
rawJson: objectMatch,
|
|
41679
|
-
method: "inline"
|
|
41680
|
-
};
|
|
41681
|
-
} catch {
|
|
41682
|
-
}
|
|
41683
|
-
}
|
|
41684
|
-
const arrayMatch = findJSONArray(text);
|
|
41685
|
-
if (arrayMatch) {
|
|
41686
|
-
try {
|
|
41687
|
-
const data = JSON.parse(arrayMatch);
|
|
41688
|
-
return {
|
|
41689
|
-
success: true,
|
|
41690
|
-
data,
|
|
41691
|
-
rawJson: arrayMatch,
|
|
41692
|
-
method: "inline"
|
|
41693
|
-
};
|
|
41694
|
-
} catch {
|
|
41695
|
-
}
|
|
41696
|
-
}
|
|
41697
|
-
return { success: false };
|
|
41698
|
-
}
|
|
41699
|
-
function findJSONObject(text) {
|
|
41700
|
-
const startIndex = text.indexOf("{");
|
|
41701
|
-
if (startIndex === -1) return null;
|
|
41702
|
-
let depth = 0;
|
|
41703
|
-
let inString = false;
|
|
41704
|
-
let escaped = false;
|
|
41705
|
-
for (let i = startIndex; i < text.length; i++) {
|
|
41706
|
-
const char = text[i];
|
|
41707
|
-
if (escaped) {
|
|
41708
|
-
escaped = false;
|
|
41709
|
-
continue;
|
|
41710
|
-
}
|
|
41711
|
-
if (char === "\\" && inString) {
|
|
41712
|
-
escaped = true;
|
|
41713
|
-
continue;
|
|
41714
|
-
}
|
|
41715
|
-
if (char === '"') {
|
|
41716
|
-
inString = !inString;
|
|
41717
|
-
continue;
|
|
41718
|
-
}
|
|
41719
|
-
if (inString) continue;
|
|
41720
|
-
if (char === "{") {
|
|
41721
|
-
depth++;
|
|
41722
|
-
} else if (char === "}") {
|
|
41723
|
-
depth--;
|
|
41724
|
-
if (depth === 0) {
|
|
41725
|
-
return text.slice(startIndex, i + 1);
|
|
41726
|
-
}
|
|
41727
|
-
}
|
|
41728
|
-
}
|
|
41729
|
-
return null;
|
|
41730
|
-
}
|
|
41731
|
-
function findJSONArray(text) {
|
|
41732
|
-
const startIndex = text.indexOf("[");
|
|
41733
|
-
if (startIndex === -1) return null;
|
|
41734
|
-
let depth = 0;
|
|
41735
|
-
let inString = false;
|
|
41736
|
-
let escaped = false;
|
|
41737
|
-
for (let i = startIndex; i < text.length; i++) {
|
|
41738
|
-
const char = text[i];
|
|
41739
|
-
if (escaped) {
|
|
41740
|
-
escaped = false;
|
|
41741
|
-
continue;
|
|
41742
|
-
}
|
|
41743
|
-
if (char === "\\" && inString) {
|
|
41744
|
-
escaped = true;
|
|
41745
|
-
continue;
|
|
41746
|
-
}
|
|
41747
|
-
if (char === '"') {
|
|
41748
|
-
inString = !inString;
|
|
41749
|
-
continue;
|
|
41750
|
-
}
|
|
41751
|
-
if (inString) continue;
|
|
41752
|
-
if (char === "[") {
|
|
41753
|
-
depth++;
|
|
41754
|
-
} else if (char === "]") {
|
|
41755
|
-
depth--;
|
|
41756
|
-
if (depth === 0) {
|
|
41757
|
-
return text.slice(startIndex, i + 1);
|
|
41758
|
-
}
|
|
41759
|
-
}
|
|
41760
|
-
}
|
|
41761
|
-
return null;
|
|
41762
|
-
}
|
|
41763
|
-
function extractJSONField(text, field, defaultValue) {
|
|
41764
|
-
const result = extractJSON(text);
|
|
41765
|
-
if (result.success && result.data && field in result.data) {
|
|
41766
|
-
return result.data[field];
|
|
41767
|
-
}
|
|
41768
|
-
return defaultValue;
|
|
41769
|
-
}
|
|
41770
|
-
function extractNumber(text, patterns = [
|
|
41771
|
-
/(\d{1,3})%?\s*(?:complete|score|percent)/i,
|
|
41772
|
-
/(?:score|completion|rating)[:\s]+(\d{1,3})/i,
|
|
41773
|
-
/(\d{1,3})\s*(?:out of|\/)\s*100/i
|
|
41774
|
-
], defaultValue = 0) {
|
|
41775
|
-
const jsonResult = extractJSON(text);
|
|
41776
|
-
if (jsonResult.success && jsonResult.data) {
|
|
41777
|
-
const scoreFields = ["score", "completionScore", "completion_score", "rating", "percent", "value"];
|
|
41778
|
-
for (const field of scoreFields) {
|
|
41779
|
-
if (field in jsonResult.data && typeof jsonResult.data[field] === "number") {
|
|
41780
|
-
return jsonResult.data[field];
|
|
41781
|
-
}
|
|
41782
|
-
}
|
|
41783
|
-
}
|
|
41784
|
-
for (const pattern of patterns) {
|
|
41785
|
-
const match = text.match(pattern);
|
|
41786
|
-
if (match && match[1]) {
|
|
41787
|
-
const num = parseInt(match[1], 10);
|
|
41788
|
-
if (!isNaN(num)) {
|
|
41789
|
-
return num;
|
|
41790
|
-
}
|
|
41791
|
-
}
|
|
41792
|
-
}
|
|
41793
|
-
return defaultValue;
|
|
41794
|
-
}
|
|
41795
|
-
|
|
41796
43112
|
// src/tools/index.ts
|
|
41797
43113
|
var tools_exports = {};
|
|
41798
43114
|
__export(tools_exports, {
|
|
@@ -41828,19 +43144,25 @@ __export(tools_exports, {
|
|
|
41828
43144
|
createDesktopScreenshotTool: () => createDesktopScreenshotTool,
|
|
41829
43145
|
createDesktopWindowFocusTool: () => createDesktopWindowFocusTool,
|
|
41830
43146
|
createDesktopWindowListTool: () => createDesktopWindowListTool,
|
|
43147
|
+
createDraftEmailTool: () => createDraftEmailTool,
|
|
41831
43148
|
createEditFileTool: () => createEditFileTool,
|
|
43149
|
+
createEditMeetingTool: () => createEditMeetingTool,
|
|
41832
43150
|
createExecuteJavaScriptTool: () => createExecuteJavaScriptTool,
|
|
43151
|
+
createFindMeetingSlotsTool: () => createFindMeetingSlotsTool,
|
|
43152
|
+
createGetMeetingTranscriptTool: () => createGetMeetingTranscriptTool,
|
|
41833
43153
|
createGetPRTool: () => createGetPRTool,
|
|
41834
43154
|
createGitHubReadFileTool: () => createGitHubReadFileTool,
|
|
41835
43155
|
createGlobTool: () => createGlobTool,
|
|
41836
43156
|
createGrepTool: () => createGrepTool,
|
|
41837
43157
|
createImageGenerationTool: () => createImageGenerationTool,
|
|
41838
43158
|
createListDirectoryTool: () => createListDirectoryTool,
|
|
43159
|
+
createMeetingTool: () => createMeetingTool,
|
|
41839
43160
|
createPRCommentsTool: () => createPRCommentsTool,
|
|
41840
43161
|
createPRFilesTool: () => createPRFilesTool,
|
|
41841
43162
|
createReadFileTool: () => createReadFileTool,
|
|
41842
43163
|
createSearchCodeTool: () => createSearchCodeTool,
|
|
41843
43164
|
createSearchFilesTool: () => createSearchFilesTool,
|
|
43165
|
+
createSendEmailTool: () => createSendEmailTool,
|
|
41844
43166
|
createSpeechToTextTool: () => createSpeechToTextTool,
|
|
41845
43167
|
createTextToSpeechTool: () => createTextToSpeechTool,
|
|
41846
43168
|
createVideoTools: () => createVideoTools,
|
|
@@ -41870,6 +43192,8 @@ __export(tools_exports, {
|
|
|
41870
43192
|
executeInVM: () => executeInVM,
|
|
41871
43193
|
executeJavaScript: () => executeJavaScript,
|
|
41872
43194
|
expandTilde: () => expandTilde,
|
|
43195
|
+
formatAttendees: () => formatAttendees,
|
|
43196
|
+
formatRecipients: () => formatRecipients,
|
|
41873
43197
|
getAllBuiltInTools: () => getAllBuiltInTools,
|
|
41874
43198
|
getBackgroundOutput: () => getBackgroundOutput,
|
|
41875
43199
|
getDesktopDriver: () => getDesktopDriver,
|
|
@@ -41880,19 +43204,24 @@ __export(tools_exports, {
|
|
|
41880
43204
|
getToolRegistry: () => getToolRegistry,
|
|
41881
43205
|
getToolsByCategory: () => getToolsByCategory,
|
|
41882
43206
|
getToolsRequiringConnector: () => getToolsRequiringConnector,
|
|
43207
|
+
getUserPathPrefix: () => getUserPathPrefix,
|
|
41883
43208
|
glob: () => glob,
|
|
41884
43209
|
grep: () => grep,
|
|
41885
43210
|
hydrateCustomTool: () => hydrateCustomTool,
|
|
41886
43211
|
isBlockedCommand: () => isBlockedCommand,
|
|
41887
43212
|
isExcludedExtension: () => isExcludedExtension,
|
|
43213
|
+
isTeamsMeetingUrl: () => isTeamsMeetingUrl,
|
|
41888
43214
|
jsonManipulator: () => jsonManipulator,
|
|
41889
43215
|
killBackgroundProcess: () => killBackgroundProcess,
|
|
41890
43216
|
listDirectory: () => listDirectory,
|
|
41891
43217
|
mergeTextPieces: () => mergeTextPieces,
|
|
43218
|
+
microsoftFetch: () => microsoftFetch,
|
|
43219
|
+
normalizeEmails: () => normalizeEmails,
|
|
41892
43220
|
parseKeyCombo: () => parseKeyCombo,
|
|
41893
43221
|
parseRepository: () => parseRepository,
|
|
41894
43222
|
readFile: () => readFile5,
|
|
41895
43223
|
resetDefaultDriver: () => resetDefaultDriver,
|
|
43224
|
+
resolveMeetingId: () => resolveMeetingId,
|
|
41896
43225
|
resolveRepository: () => resolveRepository,
|
|
41897
43226
|
setMediaOutputHandler: () => setMediaOutputHandler,
|
|
41898
43227
|
setMediaStorage: () => setMediaStorage,
|
|
@@ -42103,7 +43432,7 @@ EXAMPLES:
|
|
|
42103
43432
|
};
|
|
42104
43433
|
}
|
|
42105
43434
|
const resolvedPath = validation.resolvedPath;
|
|
42106
|
-
if (!
|
|
43435
|
+
if (!fs19.existsSync(resolvedPath)) {
|
|
42107
43436
|
return {
|
|
42108
43437
|
success: false,
|
|
42109
43438
|
error: `File not found: ${file_path}`,
|
|
@@ -42111,7 +43440,7 @@ EXAMPLES:
|
|
|
42111
43440
|
};
|
|
42112
43441
|
}
|
|
42113
43442
|
try {
|
|
42114
|
-
const stats = await
|
|
43443
|
+
const stats = await fs18.stat(resolvedPath);
|
|
42115
43444
|
if (!stats.isFile()) {
|
|
42116
43445
|
return {
|
|
42117
43446
|
success: false,
|
|
@@ -42153,7 +43482,7 @@ EXAMPLES:
|
|
|
42153
43482
|
} catch {
|
|
42154
43483
|
}
|
|
42155
43484
|
}
|
|
42156
|
-
const content = await
|
|
43485
|
+
const content = await fs18.readFile(resolvedPath, "utf-8");
|
|
42157
43486
|
const allLines = content.split("\n");
|
|
42158
43487
|
const totalLines = allLines.length;
|
|
42159
43488
|
const startIndex = Math.max(0, offset - 1);
|
|
@@ -42258,13 +43587,13 @@ EXAMPLES:
|
|
|
42258
43587
|
};
|
|
42259
43588
|
}
|
|
42260
43589
|
const resolvedPath = validation.resolvedPath;
|
|
42261
|
-
const fileExists =
|
|
43590
|
+
const fileExists = fs19.existsSync(resolvedPath);
|
|
42262
43591
|
try {
|
|
42263
43592
|
const parentDir = path2.dirname(resolvedPath);
|
|
42264
|
-
if (!
|
|
42265
|
-
await
|
|
43593
|
+
if (!fs19.existsSync(parentDir)) {
|
|
43594
|
+
await fs18.mkdir(parentDir, { recursive: true });
|
|
42266
43595
|
}
|
|
42267
|
-
await
|
|
43596
|
+
await fs18.writeFile(resolvedPath, content, "utf-8");
|
|
42268
43597
|
return {
|
|
42269
43598
|
success: true,
|
|
42270
43599
|
path: file_path,
|
|
@@ -42367,7 +43696,7 @@ EXAMPLES:
|
|
|
42367
43696
|
};
|
|
42368
43697
|
}
|
|
42369
43698
|
const resolvedPath = validation.resolvedPath;
|
|
42370
|
-
if (!
|
|
43699
|
+
if (!fs19.existsSync(resolvedPath)) {
|
|
42371
43700
|
return {
|
|
42372
43701
|
success: false,
|
|
42373
43702
|
error: `File not found: ${file_path}`,
|
|
@@ -42375,7 +43704,7 @@ EXAMPLES:
|
|
|
42375
43704
|
};
|
|
42376
43705
|
}
|
|
42377
43706
|
try {
|
|
42378
|
-
const content = await
|
|
43707
|
+
const content = await fs18.readFile(resolvedPath, "utf-8");
|
|
42379
43708
|
let occurrences = 0;
|
|
42380
43709
|
let searchIndex = 0;
|
|
42381
43710
|
while (true) {
|
|
@@ -42414,7 +43743,7 @@ EXAMPLES:
|
|
|
42414
43743
|
} else {
|
|
42415
43744
|
newContent = content.replace(old_string, new_string);
|
|
42416
43745
|
}
|
|
42417
|
-
await
|
|
43746
|
+
await fs18.writeFile(resolvedPath, newContent, "utf-8");
|
|
42418
43747
|
const diffPreview = generateDiffPreview(old_string, new_string);
|
|
42419
43748
|
return {
|
|
42420
43749
|
success: true,
|
|
@@ -42470,7 +43799,7 @@ async function findFiles(dir, pattern, baseDir, config, results = [], depth = 0)
|
|
|
42470
43799
|
return results;
|
|
42471
43800
|
}
|
|
42472
43801
|
try {
|
|
42473
|
-
const entries = await
|
|
43802
|
+
const entries = await fs18.readdir(dir, { withFileTypes: true });
|
|
42474
43803
|
for (const entry of entries) {
|
|
42475
43804
|
if (results.length >= config.maxResults) break;
|
|
42476
43805
|
const fullPath = path2.join(dir, entry.name);
|
|
@@ -42484,7 +43813,7 @@ async function findFiles(dir, pattern, baseDir, config, results = [], depth = 0)
|
|
|
42484
43813
|
} else if (entry.isFile()) {
|
|
42485
43814
|
if (matchGlobPattern(pattern, relativePath)) {
|
|
42486
43815
|
try {
|
|
42487
|
-
const stats = await
|
|
43816
|
+
const stats = await fs18.stat(fullPath);
|
|
42488
43817
|
results.push({
|
|
42489
43818
|
path: relativePath,
|
|
42490
43819
|
mtime: stats.mtimeMs
|
|
@@ -42566,7 +43895,7 @@ WHEN TO USE:
|
|
|
42566
43895
|
};
|
|
42567
43896
|
}
|
|
42568
43897
|
const resolvedDir = validation.resolvedPath;
|
|
42569
|
-
if (!
|
|
43898
|
+
if (!fs19.existsSync(resolvedDir)) {
|
|
42570
43899
|
return {
|
|
42571
43900
|
success: false,
|
|
42572
43901
|
error: `Directory not found: ${searchDir}`
|
|
@@ -42621,7 +43950,7 @@ async function findFilesToSearch(dir, baseDir, config, globPattern, fileType, fi
|
|
|
42621
43950
|
return files;
|
|
42622
43951
|
}
|
|
42623
43952
|
try {
|
|
42624
|
-
const entries = await
|
|
43953
|
+
const entries = await fs18.readdir(dir, { withFileTypes: true });
|
|
42625
43954
|
for (const entry of entries) {
|
|
42626
43955
|
const fullPath = path2.join(dir, entry.name);
|
|
42627
43956
|
if (entry.isDirectory()) {
|
|
@@ -42654,7 +43983,7 @@ async function findFilesToSearch(dir, baseDir, config, globPattern, fileType, fi
|
|
|
42654
43983
|
async function searchFile(filePath, regex, contextBefore, contextAfter) {
|
|
42655
43984
|
const matches = [];
|
|
42656
43985
|
try {
|
|
42657
|
-
const content = await
|
|
43986
|
+
const content = await fs18.readFile(filePath, "utf-8");
|
|
42658
43987
|
const lines = content.split("\n");
|
|
42659
43988
|
for (let i = 0; i < lines.length; i++) {
|
|
42660
43989
|
const line = lines[i] ?? "";
|
|
@@ -42795,7 +44124,7 @@ WHEN TO USE:
|
|
|
42795
44124
|
};
|
|
42796
44125
|
}
|
|
42797
44126
|
const resolvedPath = validation.resolvedPath;
|
|
42798
|
-
if (!
|
|
44127
|
+
if (!fs19.existsSync(resolvedPath)) {
|
|
42799
44128
|
return {
|
|
42800
44129
|
success: false,
|
|
42801
44130
|
error: `Path not found: ${searchPath}`
|
|
@@ -42811,7 +44140,7 @@ WHEN TO USE:
|
|
|
42811
44140
|
};
|
|
42812
44141
|
}
|
|
42813
44142
|
try {
|
|
42814
|
-
const stats = await
|
|
44143
|
+
const stats = await fs18.stat(resolvedPath);
|
|
42815
44144
|
let filesToSearch;
|
|
42816
44145
|
if (stats.isFile()) {
|
|
42817
44146
|
filesToSearch = [resolvedPath];
|
|
@@ -42899,7 +44228,7 @@ async function listDir(dir, baseDir, config, recursive, filter, maxDepth = 3, cu
|
|
|
42899
44228
|
return entries;
|
|
42900
44229
|
}
|
|
42901
44230
|
try {
|
|
42902
|
-
const dirEntries = await
|
|
44231
|
+
const dirEntries = await fs18.readdir(dir, { withFileTypes: true });
|
|
42903
44232
|
for (const entry of dirEntries) {
|
|
42904
44233
|
if (entries.length >= config.maxResults) break;
|
|
42905
44234
|
const fullPath = path2.join(dir, entry.name);
|
|
@@ -42917,7 +44246,7 @@ async function listDir(dir, baseDir, config, recursive, filter, maxDepth = 3, cu
|
|
|
42917
44246
|
}
|
|
42918
44247
|
if (filter === "directories" && !isDir) continue;
|
|
42919
44248
|
try {
|
|
42920
|
-
const stats = await
|
|
44249
|
+
const stats = await fs18.stat(fullPath);
|
|
42921
44250
|
const dirEntry = {
|
|
42922
44251
|
name: entry.name,
|
|
42923
44252
|
path: relativePath,
|
|
@@ -43013,14 +44342,14 @@ EXAMPLES:
|
|
|
43013
44342
|
};
|
|
43014
44343
|
}
|
|
43015
44344
|
const resolvedPath = validation.resolvedPath;
|
|
43016
|
-
if (!
|
|
44345
|
+
if (!fs19.existsSync(resolvedPath)) {
|
|
43017
44346
|
return {
|
|
43018
44347
|
success: false,
|
|
43019
44348
|
error: `Directory not found: ${path6}`
|
|
43020
44349
|
};
|
|
43021
44350
|
}
|
|
43022
44351
|
try {
|
|
43023
|
-
const stats = await
|
|
44352
|
+
const stats = await fs18.stat(resolvedPath);
|
|
43024
44353
|
if (!stats.isDirectory()) {
|
|
43025
44354
|
return {
|
|
43026
44355
|
success: false,
|
|
@@ -45974,6 +47303,840 @@ function registerGitHubTools() {
|
|
|
45974
47303
|
// src/tools/github/index.ts
|
|
45975
47304
|
registerGitHubTools();
|
|
45976
47305
|
|
|
47306
|
+
// src/tools/microsoft/types.ts
|
|
47307
|
+
function getUserPathPrefix(connector, targetUser) {
|
|
47308
|
+
const auth2 = connector.config.auth;
|
|
47309
|
+
if (auth2.type === "oauth" && auth2.flow === "client_credentials") {
|
|
47310
|
+
if (!targetUser) {
|
|
47311
|
+
throw new Error(
|
|
47312
|
+
'targetUser is required when using client_credentials (application) flow. Provide a user ID or UPN (e.g., "user@domain.com").'
|
|
47313
|
+
);
|
|
47314
|
+
}
|
|
47315
|
+
return `/users/${targetUser}`;
|
|
47316
|
+
}
|
|
47317
|
+
return "/me";
|
|
47318
|
+
}
|
|
47319
|
+
var MicrosoftAPIError = class extends Error {
|
|
47320
|
+
constructor(status, statusText, body) {
|
|
47321
|
+
const msg = typeof body === "object" && body !== null && "error" in body ? body.error?.message ?? statusText : statusText;
|
|
47322
|
+
super(`Microsoft Graph API error ${status}: ${msg}`);
|
|
47323
|
+
this.status = status;
|
|
47324
|
+
this.statusText = statusText;
|
|
47325
|
+
this.body = body;
|
|
47326
|
+
this.name = "MicrosoftAPIError";
|
|
47327
|
+
}
|
|
47328
|
+
};
|
|
47329
|
+
async function microsoftFetch(connector, endpoint, options) {
|
|
47330
|
+
let url2 = endpoint;
|
|
47331
|
+
if (options?.queryParams && Object.keys(options.queryParams).length > 0) {
|
|
47332
|
+
const params = new URLSearchParams();
|
|
47333
|
+
for (const [key, value] of Object.entries(options.queryParams)) {
|
|
47334
|
+
params.append(key, String(value));
|
|
47335
|
+
}
|
|
47336
|
+
url2 += (url2.includes("?") ? "&" : "?") + params.toString();
|
|
47337
|
+
}
|
|
47338
|
+
const headers = {
|
|
47339
|
+
"Accept": options?.accept ?? "application/json"
|
|
47340
|
+
};
|
|
47341
|
+
if (options?.body) {
|
|
47342
|
+
headers["Content-Type"] = "application/json";
|
|
47343
|
+
}
|
|
47344
|
+
const response = await connector.fetch(
|
|
47345
|
+
url2,
|
|
47346
|
+
{
|
|
47347
|
+
method: options?.method ?? "GET",
|
|
47348
|
+
headers,
|
|
47349
|
+
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
47350
|
+
},
|
|
47351
|
+
options?.userId
|
|
47352
|
+
);
|
|
47353
|
+
const text = await response.text();
|
|
47354
|
+
if (!response.ok) {
|
|
47355
|
+
let data;
|
|
47356
|
+
try {
|
|
47357
|
+
data = JSON.parse(text);
|
|
47358
|
+
} catch {
|
|
47359
|
+
data = text;
|
|
47360
|
+
}
|
|
47361
|
+
throw new MicrosoftAPIError(response.status, response.statusText, data);
|
|
47362
|
+
}
|
|
47363
|
+
if (!text || text.trim().length === 0) {
|
|
47364
|
+
return void 0;
|
|
47365
|
+
}
|
|
47366
|
+
try {
|
|
47367
|
+
return JSON.parse(text);
|
|
47368
|
+
} catch {
|
|
47369
|
+
return text;
|
|
47370
|
+
}
|
|
47371
|
+
}
|
|
47372
|
+
function normalizeEmails(input) {
|
|
47373
|
+
return input.map((item) => {
|
|
47374
|
+
if (typeof item === "string") return item;
|
|
47375
|
+
if (typeof item === "object" && item !== null) {
|
|
47376
|
+
const obj = item;
|
|
47377
|
+
if (obj.emailAddress && typeof obj.emailAddress === "object") {
|
|
47378
|
+
const ea = obj.emailAddress;
|
|
47379
|
+
if (typeof ea.address === "string") return ea.address;
|
|
47380
|
+
}
|
|
47381
|
+
if (typeof obj.address === "string") return obj.address;
|
|
47382
|
+
if (typeof obj.email === "string") return obj.email;
|
|
47383
|
+
}
|
|
47384
|
+
return String(item);
|
|
47385
|
+
});
|
|
47386
|
+
}
|
|
47387
|
+
function formatRecipients(emails) {
|
|
47388
|
+
return normalizeEmails(emails).map((address) => ({ emailAddress: { address } }));
|
|
47389
|
+
}
|
|
47390
|
+
function formatAttendees(emails) {
|
|
47391
|
+
return normalizeEmails(emails).map((address) => ({
|
|
47392
|
+
emailAddress: { address },
|
|
47393
|
+
type: "required"
|
|
47394
|
+
}));
|
|
47395
|
+
}
|
|
47396
|
+
function isTeamsMeetingUrl(input) {
|
|
47397
|
+
try {
|
|
47398
|
+
const url2 = new URL(input.trim());
|
|
47399
|
+
return (url2.hostname === "teams.microsoft.com" || url2.hostname === "teams.live.com") && url2.pathname.includes("meetup-join");
|
|
47400
|
+
} catch {
|
|
47401
|
+
return false;
|
|
47402
|
+
}
|
|
47403
|
+
}
|
|
47404
|
+
async function resolveMeetingId(connector, input, prefix, effectiveUserId) {
|
|
47405
|
+
if (!input || input.trim().length === 0) {
|
|
47406
|
+
throw new Error("Meeting ID cannot be empty");
|
|
47407
|
+
}
|
|
47408
|
+
const trimmed = input.trim();
|
|
47409
|
+
if (!isTeamsMeetingUrl(trimmed)) {
|
|
47410
|
+
return { meetingId: trimmed };
|
|
47411
|
+
}
|
|
47412
|
+
const meetings = await microsoftFetch(
|
|
47413
|
+
connector,
|
|
47414
|
+
`${prefix}/onlineMeetings`,
|
|
47415
|
+
{
|
|
47416
|
+
userId: effectiveUserId,
|
|
47417
|
+
queryParams: { "$filter": `JoinWebUrl eq '${trimmed}'` }
|
|
47418
|
+
}
|
|
47419
|
+
);
|
|
47420
|
+
if (!meetings.value || meetings.value.length === 0) {
|
|
47421
|
+
throw new Error(
|
|
47422
|
+
`Could not find an online meeting matching the provided Teams URL. Make sure the URL is correct and you have access to this meeting.`
|
|
47423
|
+
);
|
|
47424
|
+
}
|
|
47425
|
+
return {
|
|
47426
|
+
meetingId: meetings.value[0].id,
|
|
47427
|
+
subject: meetings.value[0].subject
|
|
47428
|
+
};
|
|
47429
|
+
}
|
|
47430
|
+
|
|
47431
|
+
// src/tools/microsoft/createDraftEmail.ts
|
|
47432
|
+
function createDraftEmailTool(connector, userId) {
|
|
47433
|
+
return {
|
|
47434
|
+
definition: {
|
|
47435
|
+
type: "function",
|
|
47436
|
+
function: {
|
|
47437
|
+
name: "create_draft_email",
|
|
47438
|
+
description: `Create a draft email or draft reply in the user's Outlook mailbox via Microsoft Graph. The draft is saved but NOT sent \u2014 use send_email to send immediately instead.
|
|
47439
|
+
|
|
47440
|
+
PARAMETER FORMATS:
|
|
47441
|
+
- to/cc: plain string array of email addresses. Example: ["alice@contoso.com", "bob@contoso.com"]. Do NOT use objects.
|
|
47442
|
+
- subject: plain string. Example: "Project update" or "Re: Project update" for replies.
|
|
47443
|
+
- body: HTML string. Example: "<p>Hi Alice,</p><p>Here is the update.</p>". Use <p>, <br>, <b>, <ul> tags for formatting.
|
|
47444
|
+
- replyToMessageId: Graph message ID string (starts with "AAMk..."). Only set when replying to an existing email.
|
|
47445
|
+
|
|
47446
|
+
EXAMPLES:
|
|
47447
|
+
- New draft: { "to": ["alice@contoso.com"], "subject": "Project update", "body": "<p>Hi Alice,</p><p>Here is the update.</p>" }
|
|
47448
|
+
- Reply draft: { "to": ["alice@contoso.com"], "subject": "Re: Project update", "body": "<p>Thanks!</p>", "replyToMessageId": "AAMkADI1..." }
|
|
47449
|
+
- With CC: { "to": ["alice@contoso.com"], "subject": "Notes", "body": "<p>See attached.</p>", "cc": ["bob@contoso.com"] }`,
|
|
47450
|
+
parameters: {
|
|
47451
|
+
type: "object",
|
|
47452
|
+
properties: {
|
|
47453
|
+
to: {
|
|
47454
|
+
type: "array",
|
|
47455
|
+
items: { type: "string" },
|
|
47456
|
+
description: 'Recipient email addresses as plain strings. Example: ["alice@contoso.com", "bob@contoso.com"]'
|
|
47457
|
+
},
|
|
47458
|
+
subject: {
|
|
47459
|
+
type: "string",
|
|
47460
|
+
description: 'Email subject as plain string. Example: "Project update" or "Re: Original subject" for replies.'
|
|
47461
|
+
},
|
|
47462
|
+
body: {
|
|
47463
|
+
type: "string",
|
|
47464
|
+
description: 'Email body as an HTML string. Example: "<p>Hello!</p><p>See you tomorrow.</p>"'
|
|
47465
|
+
},
|
|
47466
|
+
cc: {
|
|
47467
|
+
type: "array",
|
|
47468
|
+
items: { type: "string" },
|
|
47469
|
+
description: 'CC email addresses as plain strings. Example: ["bob@contoso.com"]. Optional.'
|
|
47470
|
+
},
|
|
47471
|
+
replyToMessageId: {
|
|
47472
|
+
type: "string",
|
|
47473
|
+
description: 'Graph message ID of the email to reply to. Example: "AAMkADI1M2I3YzgtODg...". When set, creates a threaded reply draft.'
|
|
47474
|
+
},
|
|
47475
|
+
targetUser: {
|
|
47476
|
+
type: "string",
|
|
47477
|
+
description: 'User ID or email (UPN) for app-only auth. Example: "alice@contoso.com". Ignored in delegated auth.'
|
|
47478
|
+
}
|
|
47479
|
+
},
|
|
47480
|
+
required: ["to", "subject", "body"]
|
|
47481
|
+
}
|
|
47482
|
+
}
|
|
47483
|
+
},
|
|
47484
|
+
describeCall: (args) => {
|
|
47485
|
+
const action = args.replyToMessageId ? "Reply draft" : "Draft";
|
|
47486
|
+
return `${action} to ${args.to.join(", ")}: ${args.subject}`;
|
|
47487
|
+
},
|
|
47488
|
+
permission: {
|
|
47489
|
+
scope: "session",
|
|
47490
|
+
riskLevel: "medium",
|
|
47491
|
+
approvalMessage: `Create a draft email via ${connector.displayName}`
|
|
47492
|
+
},
|
|
47493
|
+
execute: async (args, context) => {
|
|
47494
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
47495
|
+
try {
|
|
47496
|
+
const prefix = getUserPathPrefix(connector, args.targetUser);
|
|
47497
|
+
if (args.replyToMessageId) {
|
|
47498
|
+
const replyDraft = await microsoftFetch(
|
|
47499
|
+
connector,
|
|
47500
|
+
`${prefix}/messages/${args.replyToMessageId}/createReply`,
|
|
47501
|
+
{ method: "POST", userId: effectiveUserId, body: {} }
|
|
47502
|
+
);
|
|
47503
|
+
const updated = await microsoftFetch(
|
|
47504
|
+
connector,
|
|
47505
|
+
`${prefix}/messages/${replyDraft.id}`,
|
|
47506
|
+
{
|
|
47507
|
+
method: "PATCH",
|
|
47508
|
+
userId: effectiveUserId,
|
|
47509
|
+
body: {
|
|
47510
|
+
subject: args.subject,
|
|
47511
|
+
body: { contentType: "HTML", content: args.body },
|
|
47512
|
+
toRecipients: formatRecipients(args.to),
|
|
47513
|
+
...args.cc && { ccRecipients: formatRecipients(args.cc) }
|
|
47514
|
+
}
|
|
47515
|
+
}
|
|
47516
|
+
);
|
|
47517
|
+
return {
|
|
47518
|
+
success: true,
|
|
47519
|
+
draftId: updated.id,
|
|
47520
|
+
webLink: updated.webLink
|
|
47521
|
+
};
|
|
47522
|
+
}
|
|
47523
|
+
const draft = await microsoftFetch(
|
|
47524
|
+
connector,
|
|
47525
|
+
`${prefix}/messages`,
|
|
47526
|
+
{
|
|
47527
|
+
method: "POST",
|
|
47528
|
+
userId: effectiveUserId,
|
|
47529
|
+
body: {
|
|
47530
|
+
isDraft: true,
|
|
47531
|
+
subject: args.subject,
|
|
47532
|
+
body: { contentType: "HTML", content: args.body },
|
|
47533
|
+
toRecipients: formatRecipients(args.to),
|
|
47534
|
+
...args.cc && { ccRecipients: formatRecipients(args.cc) }
|
|
47535
|
+
}
|
|
47536
|
+
}
|
|
47537
|
+
);
|
|
47538
|
+
return {
|
|
47539
|
+
success: true,
|
|
47540
|
+
draftId: draft.id,
|
|
47541
|
+
webLink: draft.webLink
|
|
47542
|
+
};
|
|
47543
|
+
} catch (error) {
|
|
47544
|
+
return {
|
|
47545
|
+
success: false,
|
|
47546
|
+
error: `Failed to create draft email: ${error instanceof Error ? error.message : String(error)}`
|
|
47547
|
+
};
|
|
47548
|
+
}
|
|
47549
|
+
}
|
|
47550
|
+
};
|
|
47551
|
+
}
|
|
47552
|
+
|
|
47553
|
+
// src/tools/microsoft/sendEmail.ts
|
|
47554
|
+
function createSendEmailTool(connector, userId) {
|
|
47555
|
+
return {
|
|
47556
|
+
definition: {
|
|
47557
|
+
type: "function",
|
|
47558
|
+
function: {
|
|
47559
|
+
name: "send_email",
|
|
47560
|
+
description: `Send an email immediately or reply to an existing message via Microsoft Graph (Outlook). The email is sent right away \u2014 use create_draft_email to save a draft instead.
|
|
47561
|
+
|
|
47562
|
+
PARAMETER FORMATS:
|
|
47563
|
+
- to/cc: plain string array of email addresses. Example: ["alice@contoso.com", "bob@contoso.com"]. Do NOT use objects.
|
|
47564
|
+
- subject: plain string. Example: "Meeting tomorrow" or "Re: Meeting tomorrow" for replies.
|
|
47565
|
+
- body: HTML string. Example: "<p>Hi Alice,</p><p>Can we meet at 2pm?</p>". Use <p>, <br>, <b>, <ul> tags.
|
|
47566
|
+
- replyToMessageId: Graph message ID string (starts with "AAMk..."). Only set when replying to an existing email.
|
|
47567
|
+
|
|
47568
|
+
EXAMPLES:
|
|
47569
|
+
- Send email: { "to": ["alice@contoso.com"], "subject": "Meeting tomorrow", "body": "<p>Can we meet at 2pm?</p>" }
|
|
47570
|
+
- Reply: { "to": ["alice@contoso.com"], "subject": "Re: Meeting", "body": "<p>Confirmed!</p>", "replyToMessageId": "AAMkADI1..." }
|
|
47571
|
+
- With CC: { "to": ["alice@contoso.com"], "subject": "Update", "body": "<p>FYI</p>", "cc": ["bob@contoso.com"] }`,
|
|
47572
|
+
parameters: {
|
|
47573
|
+
type: "object",
|
|
47574
|
+
properties: {
|
|
47575
|
+
to: {
|
|
47576
|
+
type: "array",
|
|
47577
|
+
items: { type: "string" },
|
|
47578
|
+
description: 'Recipient email addresses as plain strings. Example: ["alice@contoso.com", "bob@contoso.com"]'
|
|
47579
|
+
},
|
|
47580
|
+
subject: {
|
|
47581
|
+
type: "string",
|
|
47582
|
+
description: 'Email subject as plain string. Example: "Meeting tomorrow" or "Re: Original subject" for replies.'
|
|
47583
|
+
},
|
|
47584
|
+
body: {
|
|
47585
|
+
type: "string",
|
|
47586
|
+
description: 'Email body as an HTML string. Example: "<p>Hi!</p><p>Can we meet at 2pm?</p>"'
|
|
47587
|
+
},
|
|
47588
|
+
cc: {
|
|
47589
|
+
type: "array",
|
|
47590
|
+
items: { type: "string" },
|
|
47591
|
+
description: 'CC email addresses as plain strings. Example: ["bob@contoso.com"]. Optional.'
|
|
47592
|
+
},
|
|
47593
|
+
replyToMessageId: {
|
|
47594
|
+
type: "string",
|
|
47595
|
+
description: 'Graph message ID of the email to reply to. Example: "AAMkADI1M2I3YzgtODg...". When set, sends a threaded reply.'
|
|
47596
|
+
},
|
|
47597
|
+
targetUser: {
|
|
47598
|
+
type: "string",
|
|
47599
|
+
description: 'User ID or email (UPN) for app-only auth. Example: "alice@contoso.com". Ignored in delegated auth.'
|
|
47600
|
+
}
|
|
47601
|
+
},
|
|
47602
|
+
required: ["to", "subject", "body"]
|
|
47603
|
+
}
|
|
47604
|
+
}
|
|
47605
|
+
},
|
|
47606
|
+
describeCall: (args) => {
|
|
47607
|
+
const action = args.replyToMessageId ? "Reply" : "Send";
|
|
47608
|
+
return `${action} to ${args.to.join(", ")}: ${args.subject}`;
|
|
47609
|
+
},
|
|
47610
|
+
permission: {
|
|
47611
|
+
scope: "session",
|
|
47612
|
+
riskLevel: "medium",
|
|
47613
|
+
approvalMessage: `Send an email via ${connector.displayName}`
|
|
47614
|
+
},
|
|
47615
|
+
execute: async (args, context) => {
|
|
47616
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
47617
|
+
try {
|
|
47618
|
+
const prefix = getUserPathPrefix(connector, args.targetUser);
|
|
47619
|
+
if (args.replyToMessageId) {
|
|
47620
|
+
await microsoftFetch(
|
|
47621
|
+
connector,
|
|
47622
|
+
`${prefix}/messages/${args.replyToMessageId}/reply`,
|
|
47623
|
+
{
|
|
47624
|
+
method: "POST",
|
|
47625
|
+
userId: effectiveUserId,
|
|
47626
|
+
body: {
|
|
47627
|
+
message: {
|
|
47628
|
+
toRecipients: formatRecipients(args.to),
|
|
47629
|
+
...args.cc && { ccRecipients: formatRecipients(args.cc) }
|
|
47630
|
+
},
|
|
47631
|
+
comment: args.body
|
|
47632
|
+
}
|
|
47633
|
+
}
|
|
47634
|
+
);
|
|
47635
|
+
} else {
|
|
47636
|
+
await microsoftFetch(
|
|
47637
|
+
connector,
|
|
47638
|
+
`${prefix}/sendMail`,
|
|
47639
|
+
{
|
|
47640
|
+
method: "POST",
|
|
47641
|
+
userId: effectiveUserId,
|
|
47642
|
+
body: {
|
|
47643
|
+
message: {
|
|
47644
|
+
subject: args.subject,
|
|
47645
|
+
body: { contentType: "HTML", content: args.body },
|
|
47646
|
+
toRecipients: formatRecipients(args.to),
|
|
47647
|
+
...args.cc && { ccRecipients: formatRecipients(args.cc) }
|
|
47648
|
+
},
|
|
47649
|
+
saveToSentItems: true
|
|
47650
|
+
}
|
|
47651
|
+
}
|
|
47652
|
+
);
|
|
47653
|
+
}
|
|
47654
|
+
return { success: true };
|
|
47655
|
+
} catch (error) {
|
|
47656
|
+
return {
|
|
47657
|
+
success: false,
|
|
47658
|
+
error: `Failed to send email: ${error instanceof Error ? error.message : String(error)}`
|
|
47659
|
+
};
|
|
47660
|
+
}
|
|
47661
|
+
}
|
|
47662
|
+
};
|
|
47663
|
+
}
|
|
47664
|
+
|
|
47665
|
+
// src/tools/microsoft/createMeeting.ts
|
|
47666
|
+
function createMeetingTool(connector, userId) {
|
|
47667
|
+
return {
|
|
47668
|
+
definition: {
|
|
47669
|
+
type: "function",
|
|
47670
|
+
function: {
|
|
47671
|
+
name: "create_meeting",
|
|
47672
|
+
description: `Create a calendar event on the user's Outlook calendar via Microsoft Graph, optionally with a Teams online meeting link.
|
|
47673
|
+
|
|
47674
|
+
PARAMETER FORMATS:
|
|
47675
|
+
- subject: plain string. Example: "Sprint Review"
|
|
47676
|
+
- startDateTime/endDateTime: ISO 8601 string WITHOUT timezone suffix (timezone is a separate param). Example: "2025-01-15T09:00:00"
|
|
47677
|
+
- attendees: plain string array of email addresses. Example: ["alice@contoso.com", "bob@contoso.com"]. Do NOT use objects.
|
|
47678
|
+
- body: HTML string for the invitation body. Example: "<p>Agenda: discuss Q1 goals</p>". Optional.
|
|
47679
|
+
- timeZone: IANA timezone string. Example: "America/New_York", "Europe/Zurich". Default: "UTC".
|
|
47680
|
+
- isOnlineMeeting: boolean. Set true to auto-generate a Teams meeting link.
|
|
47681
|
+
- location: plain string. Example: "Conference Room A". Optional.
|
|
47682
|
+
|
|
47683
|
+
EXAMPLES:
|
|
47684
|
+
- Simple: { "subject": "Standup", "startDateTime": "2025-01-15T09:00:00", "endDateTime": "2025-01-15T09:30:00", "attendees": ["alice@contoso.com"], "timeZone": "America/New_York" }
|
|
47685
|
+
- Teams: { "subject": "Sprint Review", "startDateTime": "2025-01-15T14:00:00", "endDateTime": "2025-01-15T15:00:00", "attendees": ["alice@contoso.com", "bob@contoso.com"], "isOnlineMeeting": true }`,
|
|
47686
|
+
parameters: {
|
|
47687
|
+
type: "object",
|
|
47688
|
+
properties: {
|
|
47689
|
+
subject: {
|
|
47690
|
+
type: "string",
|
|
47691
|
+
description: 'Meeting title as plain string. Example: "Sprint Review"'
|
|
47692
|
+
},
|
|
47693
|
+
startDateTime: {
|
|
47694
|
+
type: "string",
|
|
47695
|
+
description: 'Start date/time as ISO 8601 string without timezone suffix. Example: "2025-01-15T09:00:00"'
|
|
47696
|
+
},
|
|
47697
|
+
endDateTime: {
|
|
47698
|
+
type: "string",
|
|
47699
|
+
description: 'End date/time as ISO 8601 string without timezone suffix. Example: "2025-01-15T09:30:00"'
|
|
47700
|
+
},
|
|
47701
|
+
attendees: {
|
|
47702
|
+
type: "array",
|
|
47703
|
+
items: { type: "string" },
|
|
47704
|
+
description: 'Attendee email addresses as plain strings. Example: ["alice@contoso.com", "bob@contoso.com"]'
|
|
47705
|
+
},
|
|
47706
|
+
body: {
|
|
47707
|
+
type: "string",
|
|
47708
|
+
description: 'Meeting description as HTML string. Example: "<p>Agenda: discuss Q1 goals</p>". Optional.'
|
|
47709
|
+
},
|
|
47710
|
+
isOnlineMeeting: {
|
|
47711
|
+
type: "boolean",
|
|
47712
|
+
description: "Set to true to generate a Teams online meeting link. Default: false."
|
|
47713
|
+
},
|
|
47714
|
+
location: {
|
|
47715
|
+
type: "string",
|
|
47716
|
+
description: 'Physical location as plain string. Example: "Conference Room A". Optional.'
|
|
47717
|
+
},
|
|
47718
|
+
timeZone: {
|
|
47719
|
+
type: "string",
|
|
47720
|
+
description: 'IANA timezone string for start/end times. Example: "America/New_York", "Europe/Zurich". Default: "UTC".'
|
|
47721
|
+
},
|
|
47722
|
+
targetUser: {
|
|
47723
|
+
type: "string",
|
|
47724
|
+
description: 'User ID or email (UPN) for app-only auth. Example: "alice@contoso.com". Ignored in delegated auth.'
|
|
47725
|
+
}
|
|
47726
|
+
},
|
|
47727
|
+
required: ["subject", "startDateTime", "endDateTime", "attendees"]
|
|
47728
|
+
}
|
|
47729
|
+
}
|
|
47730
|
+
},
|
|
47731
|
+
describeCall: (args) => {
|
|
47732
|
+
return `Create meeting: ${args.subject} (${args.attendees.length} attendees)`;
|
|
47733
|
+
},
|
|
47734
|
+
permission: {
|
|
47735
|
+
scope: "session",
|
|
47736
|
+
riskLevel: "medium",
|
|
47737
|
+
approvalMessage: `Create a calendar event via ${connector.displayName}`
|
|
47738
|
+
},
|
|
47739
|
+
execute: async (args, context) => {
|
|
47740
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
47741
|
+
try {
|
|
47742
|
+
const prefix = getUserPathPrefix(connector, args.targetUser);
|
|
47743
|
+
const tz = args.timeZone ?? "UTC";
|
|
47744
|
+
const eventBody = {
|
|
47745
|
+
subject: args.subject,
|
|
47746
|
+
start: { dateTime: args.startDateTime, timeZone: tz },
|
|
47747
|
+
end: { dateTime: args.endDateTime, timeZone: tz },
|
|
47748
|
+
attendees: formatAttendees(args.attendees)
|
|
47749
|
+
};
|
|
47750
|
+
if (args.body) {
|
|
47751
|
+
eventBody.body = { contentType: "HTML", content: args.body };
|
|
47752
|
+
}
|
|
47753
|
+
if (args.isOnlineMeeting) {
|
|
47754
|
+
eventBody.isOnlineMeeting = true;
|
|
47755
|
+
eventBody.onlineMeetingProvider = "teamsForBusiness";
|
|
47756
|
+
}
|
|
47757
|
+
if (args.location) {
|
|
47758
|
+
eventBody.location = { displayName: args.location };
|
|
47759
|
+
}
|
|
47760
|
+
const event = await microsoftFetch(
|
|
47761
|
+
connector,
|
|
47762
|
+
`${prefix}/events`,
|
|
47763
|
+
{ method: "POST", userId: effectiveUserId, body: eventBody }
|
|
47764
|
+
);
|
|
47765
|
+
return {
|
|
47766
|
+
success: true,
|
|
47767
|
+
eventId: event.id,
|
|
47768
|
+
webLink: event.webLink,
|
|
47769
|
+
onlineMeetingUrl: event.onlineMeeting?.joinUrl
|
|
47770
|
+
};
|
|
47771
|
+
} catch (error) {
|
|
47772
|
+
return {
|
|
47773
|
+
success: false,
|
|
47774
|
+
error: `Failed to create meeting: ${error instanceof Error ? error.message : String(error)}`
|
|
47775
|
+
};
|
|
47776
|
+
}
|
|
47777
|
+
}
|
|
47778
|
+
};
|
|
47779
|
+
}
|
|
47780
|
+
|
|
47781
|
+
// src/tools/microsoft/editMeeting.ts
|
|
47782
|
+
function createEditMeetingTool(connector, userId) {
|
|
47783
|
+
return {
|
|
47784
|
+
definition: {
|
|
47785
|
+
type: "function",
|
|
47786
|
+
function: {
|
|
47787
|
+
name: "edit_meeting",
|
|
47788
|
+
description: `Update an existing Outlook calendar event via Microsoft Graph. Only the fields you provide will be changed \u2014 omitted fields keep their current values.
|
|
47789
|
+
|
|
47790
|
+
IMPORTANT: The "attendees" field REPLACES the entire attendee list. Include ALL desired attendees (both new and existing), not just the ones you want to add.
|
|
47791
|
+
|
|
47792
|
+
PARAMETER FORMATS:
|
|
47793
|
+
- eventId: Graph event ID string (starts with "AAMk..."). Get this from a previous create_meeting result.
|
|
47794
|
+
- subject: plain string. Example: "Updated: Sprint Review"
|
|
47795
|
+
- startDateTime/endDateTime: ISO 8601 string without timezone suffix. Example: "2025-01-15T10:00:00"
|
|
47796
|
+
- attendees: plain string array of email addresses. Example: ["alice@contoso.com", "charlie@contoso.com"]. Do NOT use objects. REPLACES all attendees.
|
|
47797
|
+
- body: HTML string. Example: "<p>Updated agenda</p>"
|
|
47798
|
+
- timeZone: IANA timezone string. Example: "Europe/Zurich". Default: "UTC".
|
|
47799
|
+
- isOnlineMeeting: boolean. true = add Teams link, false = remove it.
|
|
47800
|
+
- location: plain string. Example: "Room 201"
|
|
47801
|
+
|
|
47802
|
+
EXAMPLES:
|
|
47803
|
+
- Reschedule: { "eventId": "AAMkADI1...", "startDateTime": "2025-01-15T10:00:00", "endDateTime": "2025-01-15T10:30:00", "timeZone": "America/New_York" }
|
|
47804
|
+
- Change attendees: { "eventId": "AAMkADI1...", "attendees": ["alice@contoso.com", "charlie@contoso.com"] }
|
|
47805
|
+
- Add Teams link: { "eventId": "AAMkADI1...", "isOnlineMeeting": true }`,
|
|
47806
|
+
parameters: {
|
|
47807
|
+
type: "object",
|
|
47808
|
+
properties: {
|
|
47809
|
+
eventId: {
|
|
47810
|
+
type: "string",
|
|
47811
|
+
description: 'Calendar event ID string from create_meeting result. Example: "AAMkADI1M2I3YzgtODg..."'
|
|
47812
|
+
},
|
|
47813
|
+
subject: {
|
|
47814
|
+
type: "string",
|
|
47815
|
+
description: 'New meeting title as plain string. Example: "Updated: Sprint Review"'
|
|
47816
|
+
},
|
|
47817
|
+
startDateTime: {
|
|
47818
|
+
type: "string",
|
|
47819
|
+
description: 'New start date/time as ISO 8601 string without timezone suffix. Example: "2025-01-15T10:00:00"'
|
|
47820
|
+
},
|
|
47821
|
+
endDateTime: {
|
|
47822
|
+
type: "string",
|
|
47823
|
+
description: 'New end date/time as ISO 8601 string without timezone suffix. Example: "2025-01-15T10:30:00"'
|
|
47824
|
+
},
|
|
47825
|
+
attendees: {
|
|
47826
|
+
type: "array",
|
|
47827
|
+
items: { type: "string" },
|
|
47828
|
+
description: 'FULL replacement attendee list as plain email strings. Example: ["alice@contoso.com", "charlie@contoso.com"]. Include ALL attendees.'
|
|
47829
|
+
},
|
|
47830
|
+
body: {
|
|
47831
|
+
type: "string",
|
|
47832
|
+
description: 'New meeting description as HTML string. Example: "<p>Updated agenda</p>"'
|
|
47833
|
+
},
|
|
47834
|
+
isOnlineMeeting: {
|
|
47835
|
+
type: "boolean",
|
|
47836
|
+
description: "true to add Teams meeting link, false to remove it."
|
|
47837
|
+
},
|
|
47838
|
+
location: {
|
|
47839
|
+
type: "string",
|
|
47840
|
+
description: 'New location as plain string. Example: "Conference Room A"'
|
|
47841
|
+
},
|
|
47842
|
+
timeZone: {
|
|
47843
|
+
type: "string",
|
|
47844
|
+
description: 'IANA timezone string for start/end times. Example: "Europe/Zurich". Default: "UTC".'
|
|
47845
|
+
},
|
|
47846
|
+
targetUser: {
|
|
47847
|
+
type: "string",
|
|
47848
|
+
description: 'User ID or email (UPN) for app-only auth. Example: "alice@contoso.com". Ignored in delegated auth.'
|
|
47849
|
+
}
|
|
47850
|
+
},
|
|
47851
|
+
required: ["eventId"]
|
|
47852
|
+
}
|
|
47853
|
+
}
|
|
47854
|
+
},
|
|
47855
|
+
describeCall: (args) => {
|
|
47856
|
+
const fields = ["subject", "startDateTime", "endDateTime", "attendees", "body", "location"];
|
|
47857
|
+
const changed = fields.filter((f) => args[f] !== void 0);
|
|
47858
|
+
return `Edit meeting ${args.eventId.slice(0, 12)}... (${changed.join(", ") || "no changes"})`;
|
|
47859
|
+
},
|
|
47860
|
+
permission: {
|
|
47861
|
+
scope: "session",
|
|
47862
|
+
riskLevel: "medium",
|
|
47863
|
+
approvalMessage: `Update a calendar event via ${connector.displayName}`
|
|
47864
|
+
},
|
|
47865
|
+
execute: async (args, context) => {
|
|
47866
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
47867
|
+
try {
|
|
47868
|
+
const prefix = getUserPathPrefix(connector, args.targetUser);
|
|
47869
|
+
const tz = args.timeZone ?? "UTC";
|
|
47870
|
+
const patchBody = {};
|
|
47871
|
+
if (args.subject !== void 0) patchBody.subject = args.subject;
|
|
47872
|
+
if (args.body !== void 0) patchBody.body = { contentType: "HTML", content: args.body };
|
|
47873
|
+
if (args.startDateTime !== void 0) patchBody.start = { dateTime: args.startDateTime, timeZone: tz };
|
|
47874
|
+
if (args.endDateTime !== void 0) patchBody.end = { dateTime: args.endDateTime, timeZone: tz };
|
|
47875
|
+
if (args.attendees !== void 0) {
|
|
47876
|
+
patchBody.attendees = formatAttendees(args.attendees);
|
|
47877
|
+
}
|
|
47878
|
+
if (args.isOnlineMeeting !== void 0) {
|
|
47879
|
+
patchBody.isOnlineMeeting = args.isOnlineMeeting;
|
|
47880
|
+
if (args.isOnlineMeeting) {
|
|
47881
|
+
patchBody.onlineMeetingProvider = "teamsForBusiness";
|
|
47882
|
+
}
|
|
47883
|
+
}
|
|
47884
|
+
if (args.location !== void 0) {
|
|
47885
|
+
patchBody.location = { displayName: args.location };
|
|
47886
|
+
}
|
|
47887
|
+
const event = await microsoftFetch(
|
|
47888
|
+
connector,
|
|
47889
|
+
`${prefix}/events/${args.eventId}`,
|
|
47890
|
+
{ method: "PATCH", userId: effectiveUserId, body: patchBody }
|
|
47891
|
+
);
|
|
47892
|
+
return {
|
|
47893
|
+
success: true,
|
|
47894
|
+
eventId: event.id,
|
|
47895
|
+
webLink: event.webLink
|
|
47896
|
+
};
|
|
47897
|
+
} catch (error) {
|
|
47898
|
+
return {
|
|
47899
|
+
success: false,
|
|
47900
|
+
error: `Failed to edit meeting: ${error instanceof Error ? error.message : String(error)}`
|
|
47901
|
+
};
|
|
47902
|
+
}
|
|
47903
|
+
}
|
|
47904
|
+
};
|
|
47905
|
+
}
|
|
47906
|
+
|
|
47907
|
+
// src/tools/microsoft/getMeetingTranscript.ts
|
|
47908
|
+
function parseVttToText(vtt) {
|
|
47909
|
+
const lines = vtt.split("\n");
|
|
47910
|
+
const textLines = [];
|
|
47911
|
+
for (const line of lines) {
|
|
47912
|
+
const trimmed = line.trim();
|
|
47913
|
+
if (trimmed === "" || trimmed === "WEBVTT" || trimmed.startsWith("NOTE") || /^\d+$/.test(trimmed) || /^\d{2}:\d{2}/.test(trimmed)) {
|
|
47914
|
+
continue;
|
|
47915
|
+
}
|
|
47916
|
+
textLines.push(trimmed);
|
|
47917
|
+
}
|
|
47918
|
+
return textLines.join("\n");
|
|
47919
|
+
}
|
|
47920
|
+
function createGetMeetingTranscriptTool(connector, userId) {
|
|
47921
|
+
return {
|
|
47922
|
+
definition: {
|
|
47923
|
+
type: "function",
|
|
47924
|
+
function: {
|
|
47925
|
+
name: "get_meeting_transcript",
|
|
47926
|
+
description: `Retrieve the transcript from a Teams online meeting via Microsoft Graph. Returns plain text with speaker labels (VTT timestamps are stripped).
|
|
47927
|
+
|
|
47928
|
+
NOTE: Requires the OnlineMeetingTranscript.Read.All permission. Transcription must have been enabled during the meeting.
|
|
47929
|
+
|
|
47930
|
+
USAGE:
|
|
47931
|
+
- Provide the Teams online meeting ID (NOT the calendar event ID \u2014 this is different) or a Teams meeting join URL
|
|
47932
|
+
- The meetingId can be found in the Teams meeting details or extracted from the join URL
|
|
47933
|
+
|
|
47934
|
+
EXAMPLES:
|
|
47935
|
+
- By meeting ID: { "meetingId": "MSo1N2Y5ZGFjYy03MWJmLTQ3NDMtYjQxMy01M2EdFGkdRWHJlQ" }
|
|
47936
|
+
- By Teams join URL: { "meetingId": "https://teams.microsoft.com/l/meetup-join/19%3ameeting_MjA5YjFi..." }`,
|
|
47937
|
+
parameters: {
|
|
47938
|
+
type: "object",
|
|
47939
|
+
properties: {
|
|
47940
|
+
meetingId: {
|
|
47941
|
+
type: "string",
|
|
47942
|
+
description: 'Teams online meeting ID (e.g. "MSo1N2Y5...") or Teams meeting join URL. This is NOT the calendar event ID.'
|
|
47943
|
+
},
|
|
47944
|
+
targetUser: {
|
|
47945
|
+
type: "string",
|
|
47946
|
+
description: "User ID or email (UPN) to act on behalf of. Only needed for app-only (client_credentials) auth. Ignored in delegated auth."
|
|
47947
|
+
}
|
|
47948
|
+
},
|
|
47949
|
+
required: ["meetingId"]
|
|
47950
|
+
}
|
|
47951
|
+
}
|
|
47952
|
+
},
|
|
47953
|
+
describeCall: (args) => {
|
|
47954
|
+
return `Get transcript for meeting ${args.meetingId.slice(0, 20)}...`;
|
|
47955
|
+
},
|
|
47956
|
+
permission: {
|
|
47957
|
+
scope: "session",
|
|
47958
|
+
riskLevel: "low",
|
|
47959
|
+
approvalMessage: `Get a meeting transcript via ${connector.displayName}`
|
|
47960
|
+
},
|
|
47961
|
+
execute: async (args, context) => {
|
|
47962
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
47963
|
+
try {
|
|
47964
|
+
const prefix = getUserPathPrefix(connector, args.targetUser);
|
|
47965
|
+
const resolved = await resolveMeetingId(connector, args.meetingId, prefix, effectiveUserId);
|
|
47966
|
+
const meetingId = resolved.meetingId;
|
|
47967
|
+
const transcriptList = await microsoftFetch(
|
|
47968
|
+
connector,
|
|
47969
|
+
`${prefix}/onlineMeetings/${meetingId}/transcripts`,
|
|
47970
|
+
{ userId: effectiveUserId }
|
|
47971
|
+
);
|
|
47972
|
+
if (!transcriptList.value || transcriptList.value.length === 0) {
|
|
47973
|
+
return {
|
|
47974
|
+
success: false,
|
|
47975
|
+
error: "No transcripts found for this meeting. The meeting may not have had transcription enabled."
|
|
47976
|
+
};
|
|
47977
|
+
}
|
|
47978
|
+
const transcriptId = transcriptList.value[0].id;
|
|
47979
|
+
const contentUrl = `${prefix}/onlineMeetings/${meetingId}/transcripts/${transcriptId}/content`;
|
|
47980
|
+
const response = await connector.fetch(
|
|
47981
|
+
contentUrl + "?$format=text/vtt",
|
|
47982
|
+
{ method: "GET", headers: { "Accept": "text/vtt" } },
|
|
47983
|
+
effectiveUserId
|
|
47984
|
+
);
|
|
47985
|
+
if (!response.ok) {
|
|
47986
|
+
const errorText = await response.text();
|
|
47987
|
+
return {
|
|
47988
|
+
success: false,
|
|
47989
|
+
error: `Failed to fetch transcript content: ${response.status} ${errorText}`
|
|
47990
|
+
};
|
|
47991
|
+
}
|
|
47992
|
+
const vttContent = await response.text();
|
|
47993
|
+
const transcript = parseVttToText(vttContent);
|
|
47994
|
+
return {
|
|
47995
|
+
success: true,
|
|
47996
|
+
transcript,
|
|
47997
|
+
meetingSubject: resolved.subject
|
|
47998
|
+
};
|
|
47999
|
+
} catch (error) {
|
|
48000
|
+
return {
|
|
48001
|
+
success: false,
|
|
48002
|
+
error: `Failed to get meeting transcript: ${error instanceof Error ? error.message : String(error)}`
|
|
48003
|
+
};
|
|
48004
|
+
}
|
|
48005
|
+
}
|
|
48006
|
+
};
|
|
48007
|
+
}
|
|
48008
|
+
|
|
48009
|
+
// src/tools/microsoft/findMeetingSlots.ts
|
|
48010
|
+
function createFindMeetingSlotsTool(connector, userId) {
|
|
48011
|
+
return {
|
|
48012
|
+
definition: {
|
|
48013
|
+
type: "function",
|
|
48014
|
+
function: {
|
|
48015
|
+
name: "find_meeting_slots",
|
|
48016
|
+
description: `Find available meeting time slots when all attendees are free, via Microsoft Graph. Checks each attendee's Outlook calendar and suggests times when everyone is available.
|
|
48017
|
+
|
|
48018
|
+
PARAMETER FORMATS:
|
|
48019
|
+
- attendees: plain string array of email addresses. Example: ["alice@contoso.com", "bob@contoso.com"]. Do NOT use objects \u2014 just plain email strings.
|
|
48020
|
+
- startDateTime/endDateTime: ISO 8601 string without timezone suffix. Example: "2025-01-15T08:00:00". Can span multiple days.
|
|
48021
|
+
- duration: number of minutes as integer. Example: 30 or 60.
|
|
48022
|
+
- timeZone: IANA timezone string. Example: "America/New_York", "Europe/Zurich". Default: "UTC".
|
|
48023
|
+
- maxResults: integer. Default: 5.
|
|
48024
|
+
|
|
48025
|
+
EXAMPLES:
|
|
48026
|
+
- Find 30min slot: { "attendees": ["alice@contoso.com", "bob@contoso.com"], "startDateTime": "2025-01-15T08:00:00", "endDateTime": "2025-01-15T18:00:00", "duration": 30, "timeZone": "America/New_York" }
|
|
48027
|
+
- Find 1hr slot across days: { "attendees": ["alice@contoso.com"], "startDateTime": "2025-01-15T08:00:00", "endDateTime": "2025-01-17T18:00:00", "duration": 60, "maxResults": 10 }`,
|
|
48028
|
+
parameters: {
|
|
48029
|
+
type: "object",
|
|
48030
|
+
properties: {
|
|
48031
|
+
attendees: {
|
|
48032
|
+
type: "array",
|
|
48033
|
+
items: { type: "string" },
|
|
48034
|
+
description: 'Attendee email addresses as plain strings. Example: ["alice@contoso.com", "bob@contoso.com"]. Do NOT pass objects.'
|
|
48035
|
+
},
|
|
48036
|
+
startDateTime: {
|
|
48037
|
+
type: "string",
|
|
48038
|
+
description: 'Search window start as ISO 8601 string without timezone suffix. Example: "2025-01-15T08:00:00"'
|
|
48039
|
+
},
|
|
48040
|
+
endDateTime: {
|
|
48041
|
+
type: "string",
|
|
48042
|
+
description: 'Search window end as ISO 8601 string without timezone suffix. Example: "2025-01-15T18:00:00". Can span multiple days.'
|
|
48043
|
+
},
|
|
48044
|
+
duration: {
|
|
48045
|
+
type: "number",
|
|
48046
|
+
description: "Meeting duration in minutes as integer. Example: 30 or 60."
|
|
48047
|
+
},
|
|
48048
|
+
timeZone: {
|
|
48049
|
+
type: "string",
|
|
48050
|
+
description: 'IANA timezone string for start/end times. Example: "America/New_York", "Europe/Zurich". Default: "UTC".'
|
|
48051
|
+
},
|
|
48052
|
+
maxResults: {
|
|
48053
|
+
type: "number",
|
|
48054
|
+
description: "Maximum number of time slot suggestions as integer. Default: 5."
|
|
48055
|
+
},
|
|
48056
|
+
targetUser: {
|
|
48057
|
+
type: "string",
|
|
48058
|
+
description: 'User ID or email (UPN) for app-only auth. Example: "alice@contoso.com". Ignored in delegated auth.'
|
|
48059
|
+
}
|
|
48060
|
+
},
|
|
48061
|
+
required: ["attendees", "startDateTime", "endDateTime", "duration"]
|
|
48062
|
+
}
|
|
48063
|
+
}
|
|
48064
|
+
},
|
|
48065
|
+
describeCall: (args) => {
|
|
48066
|
+
return `Find ${args.duration}min slots for ${args.attendees.length} attendees`;
|
|
48067
|
+
},
|
|
48068
|
+
permission: {
|
|
48069
|
+
scope: "session",
|
|
48070
|
+
riskLevel: "low",
|
|
48071
|
+
approvalMessage: `Find meeting time slots via ${connector.displayName}`
|
|
48072
|
+
},
|
|
48073
|
+
execute: async (args, context) => {
|
|
48074
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
48075
|
+
try {
|
|
48076
|
+
const prefix = getUserPathPrefix(connector, args.targetUser);
|
|
48077
|
+
const tz = args.timeZone ?? "UTC";
|
|
48078
|
+
const result = await microsoftFetch(
|
|
48079
|
+
connector,
|
|
48080
|
+
`${prefix}/findMeetingTimes`,
|
|
48081
|
+
{
|
|
48082
|
+
method: "POST",
|
|
48083
|
+
userId: effectiveUserId,
|
|
48084
|
+
body: {
|
|
48085
|
+
attendees: formatAttendees(args.attendees),
|
|
48086
|
+
timeConstraint: {
|
|
48087
|
+
timeslots: [
|
|
48088
|
+
{
|
|
48089
|
+
start: { dateTime: args.startDateTime, timeZone: tz },
|
|
48090
|
+
end: { dateTime: args.endDateTime, timeZone: tz }
|
|
48091
|
+
}
|
|
48092
|
+
]
|
|
48093
|
+
},
|
|
48094
|
+
meetingDuration: `PT${args.duration}M`,
|
|
48095
|
+
maxCandidates: args.maxResults ?? 5
|
|
48096
|
+
}
|
|
48097
|
+
}
|
|
48098
|
+
);
|
|
48099
|
+
const slots = (result.meetingTimeSuggestions ?? []).map((s) => ({
|
|
48100
|
+
start: s.meetingTimeSlot.start.dateTime,
|
|
48101
|
+
end: s.meetingTimeSlot.end.dateTime,
|
|
48102
|
+
confidence: String(s.confidence),
|
|
48103
|
+
attendeeAvailability: (s.attendeeAvailability ?? []).map((a) => ({
|
|
48104
|
+
attendee: a.attendee.emailAddress.address,
|
|
48105
|
+
availability: a.availability
|
|
48106
|
+
}))
|
|
48107
|
+
}));
|
|
48108
|
+
return {
|
|
48109
|
+
success: true,
|
|
48110
|
+
slots,
|
|
48111
|
+
emptySuggestionsReason: result.emptySuggestionsReason
|
|
48112
|
+
};
|
|
48113
|
+
} catch (error) {
|
|
48114
|
+
return {
|
|
48115
|
+
success: false,
|
|
48116
|
+
error: `Failed to find meeting slots: ${error instanceof Error ? error.message : String(error)}`
|
|
48117
|
+
};
|
|
48118
|
+
}
|
|
48119
|
+
}
|
|
48120
|
+
};
|
|
48121
|
+
}
|
|
48122
|
+
|
|
48123
|
+
// src/tools/microsoft/register.ts
|
|
48124
|
+
function registerMicrosoftTools() {
|
|
48125
|
+
ConnectorTools.registerService("microsoft", (connector, userId) => {
|
|
48126
|
+
return [
|
|
48127
|
+
createDraftEmailTool(connector, userId),
|
|
48128
|
+
createSendEmailTool(connector, userId),
|
|
48129
|
+
createMeetingTool(connector, userId),
|
|
48130
|
+
createEditMeetingTool(connector, userId),
|
|
48131
|
+
createGetMeetingTranscriptTool(connector, userId),
|
|
48132
|
+
createFindMeetingSlotsTool(connector, userId)
|
|
48133
|
+
];
|
|
48134
|
+
});
|
|
48135
|
+
}
|
|
48136
|
+
|
|
48137
|
+
// src/tools/microsoft/index.ts
|
|
48138
|
+
registerMicrosoftTools();
|
|
48139
|
+
|
|
45977
48140
|
// src/tools/desktop/types.ts
|
|
45978
48141
|
var DEFAULT_DESKTOP_CONFIG = {
|
|
45979
48142
|
driver: null,
|
|
@@ -46789,7 +48952,7 @@ var desktopTools = [
|
|
|
46789
48952
|
|
|
46790
48953
|
// src/tools/custom-tools/resolveStorage.ts
|
|
46791
48954
|
init_StorageRegistry();
|
|
46792
|
-
function
|
|
48955
|
+
function buildStorageContext2(toolContext) {
|
|
46793
48956
|
const global2 = exports.StorageRegistry.getContext();
|
|
46794
48957
|
if (global2) return global2;
|
|
46795
48958
|
if (toolContext?.userId) return { userId: toolContext.userId };
|
|
@@ -46799,7 +48962,7 @@ function resolveCustomToolStorage(explicit, toolContext) {
|
|
|
46799
48962
|
if (explicit) return explicit;
|
|
46800
48963
|
const factory = exports.StorageRegistry.get("customTools");
|
|
46801
48964
|
if (factory) {
|
|
46802
|
-
return factory(
|
|
48965
|
+
return factory(buildStorageContext2(toolContext));
|
|
46803
48966
|
}
|
|
46804
48967
|
return new FileCustomToolStorage();
|
|
46805
48968
|
}
|
|
@@ -46827,12 +48990,13 @@ function createCustomToolDelete(storage) {
|
|
|
46827
48990
|
permission: { scope: "session", riskLevel: "medium" },
|
|
46828
48991
|
execute: async (args, context) => {
|
|
46829
48992
|
try {
|
|
48993
|
+
const userId = context?.userId;
|
|
46830
48994
|
const s = resolveCustomToolStorage(storage, context);
|
|
46831
|
-
const exists = await s.exists(args.name);
|
|
48995
|
+
const exists = await s.exists(userId, args.name);
|
|
46832
48996
|
if (!exists) {
|
|
46833
48997
|
return { success: false, name: args.name, error: `Custom tool '${args.name}' not found` };
|
|
46834
48998
|
}
|
|
46835
|
-
await s.delete(args.name);
|
|
48999
|
+
await s.delete(userId, args.name);
|
|
46836
49000
|
return { success: true, name: args.name };
|
|
46837
49001
|
} catch (error) {
|
|
46838
49002
|
return { success: false, name: args.name, error: error.message };
|
|
@@ -47073,8 +49237,9 @@ function createCustomToolList(storage) {
|
|
|
47073
49237
|
},
|
|
47074
49238
|
permission: { scope: "always", riskLevel: "low" },
|
|
47075
49239
|
execute: async (args, context) => {
|
|
49240
|
+
const userId = context?.userId;
|
|
47076
49241
|
const s = resolveCustomToolStorage(storage, context);
|
|
47077
|
-
const tools = await s.list({
|
|
49242
|
+
const tools = await s.list(userId, {
|
|
47078
49243
|
search: args.search,
|
|
47079
49244
|
tags: args.tags,
|
|
47080
49245
|
category: args.category,
|
|
@@ -47110,8 +49275,9 @@ function createCustomToolLoad(storage) {
|
|
|
47110
49275
|
},
|
|
47111
49276
|
permission: { scope: "always", riskLevel: "low" },
|
|
47112
49277
|
execute: async (args, context) => {
|
|
49278
|
+
const userId = context?.userId;
|
|
47113
49279
|
const s = resolveCustomToolStorage(storage, context);
|
|
47114
|
-
const tool = await s.load(args.name);
|
|
49280
|
+
const tool = await s.load(userId, args.name);
|
|
47115
49281
|
if (!tool) {
|
|
47116
49282
|
return { success: false, error: `Custom tool '${args.name}' not found` };
|
|
47117
49283
|
}
|
|
@@ -47183,9 +49349,10 @@ function createCustomToolSave(storage) {
|
|
|
47183
49349
|
permission: { scope: "session", riskLevel: "medium" },
|
|
47184
49350
|
execute: async (args, context) => {
|
|
47185
49351
|
try {
|
|
49352
|
+
const userId = context?.userId;
|
|
47186
49353
|
const s = resolveCustomToolStorage(storage, context);
|
|
47187
49354
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
47188
|
-
const existing = await s.load(args.name);
|
|
49355
|
+
const existing = await s.load(userId, args.name);
|
|
47189
49356
|
const definition = {
|
|
47190
49357
|
version: CUSTOM_TOOL_DEFINITION_VERSION,
|
|
47191
49358
|
name: args.name,
|
|
@@ -47204,17 +49371,17 @@ function createCustomToolSave(storage) {
|
|
|
47204
49371
|
requiresConnector: (args.connectorNames?.length ?? 0) > 0
|
|
47205
49372
|
}
|
|
47206
49373
|
};
|
|
47207
|
-
await s.save(definition);
|
|
49374
|
+
await s.save(userId, definition);
|
|
47208
49375
|
return {
|
|
47209
49376
|
success: true,
|
|
47210
49377
|
name: args.name,
|
|
47211
|
-
storagePath: s.getPath()
|
|
49378
|
+
storagePath: s.getPath(userId)
|
|
47212
49379
|
};
|
|
47213
49380
|
} catch (error) {
|
|
47214
49381
|
return {
|
|
47215
49382
|
success: false,
|
|
47216
49383
|
name: args.name,
|
|
47217
|
-
storagePath: resolveCustomToolStorage(storage, context).getPath(),
|
|
49384
|
+
storagePath: resolveCustomToolStorage(storage, context).getPath(context?.userId),
|
|
47218
49385
|
error: error.message
|
|
47219
49386
|
};
|
|
47220
49387
|
}
|
|
@@ -48001,7 +50168,9 @@ exports.FileCustomToolStorage = FileCustomToolStorage;
|
|
|
48001
50168
|
exports.FileMediaOutputHandler = FileMediaStorage;
|
|
48002
50169
|
exports.FileMediaStorage = FileMediaStorage;
|
|
48003
50170
|
exports.FilePersistentInstructionsStorage = FilePersistentInstructionsStorage;
|
|
50171
|
+
exports.FileRoutineDefinitionStorage = FileRoutineDefinitionStorage;
|
|
48004
50172
|
exports.FileStorage = FileStorage;
|
|
50173
|
+
exports.FileUserInfoStorage = FileUserInfoStorage;
|
|
48005
50174
|
exports.FormatDetector = FormatDetector;
|
|
48006
50175
|
exports.HookManager = HookManager;
|
|
48007
50176
|
exports.IMAGE_MODELS = IMAGE_MODELS;
|
|
@@ -48077,6 +50246,7 @@ exports.ToolPermissionManager = ToolPermissionManager;
|
|
|
48077
50246
|
exports.ToolRegistry = ToolRegistry;
|
|
48078
50247
|
exports.ToolTimeoutError = ToolTimeoutError;
|
|
48079
50248
|
exports.TruncateCompactor = TruncateCompactor;
|
|
50249
|
+
exports.UserInfoPluginNextGen = UserInfoPluginNextGen;
|
|
48080
50250
|
exports.VENDORS = VENDORS;
|
|
48081
50251
|
exports.VENDOR_ICON_MAP = VENDOR_ICON_MAP;
|
|
48082
50252
|
exports.VIDEO_MODELS = VIDEO_MODELS;
|
|
@@ -48126,13 +50296,18 @@ exports.createDesktopMouseScrollTool = createDesktopMouseScrollTool;
|
|
|
48126
50296
|
exports.createDesktopScreenshotTool = createDesktopScreenshotTool;
|
|
48127
50297
|
exports.createDesktopWindowFocusTool = createDesktopWindowFocusTool;
|
|
48128
50298
|
exports.createDesktopWindowListTool = createDesktopWindowListTool;
|
|
50299
|
+
exports.createDraftEmailTool = createDraftEmailTool;
|
|
48129
50300
|
exports.createEditFileTool = createEditFileTool;
|
|
50301
|
+
exports.createEditMeetingTool = createEditMeetingTool;
|
|
48130
50302
|
exports.createEstimator = createEstimator;
|
|
48131
50303
|
exports.createExecuteJavaScriptTool = createExecuteJavaScriptTool;
|
|
48132
50304
|
exports.createFileAgentDefinitionStorage = createFileAgentDefinitionStorage;
|
|
48133
50305
|
exports.createFileContextStorage = createFileContextStorage;
|
|
48134
50306
|
exports.createFileCustomToolStorage = createFileCustomToolStorage;
|
|
48135
50307
|
exports.createFileMediaStorage = createFileMediaStorage;
|
|
50308
|
+
exports.createFileRoutineDefinitionStorage = createFileRoutineDefinitionStorage;
|
|
50309
|
+
exports.createFindMeetingSlotsTool = createFindMeetingSlotsTool;
|
|
50310
|
+
exports.createGetMeetingTranscriptTool = createGetMeetingTranscriptTool;
|
|
48136
50311
|
exports.createGetPRTool = createGetPRTool;
|
|
48137
50312
|
exports.createGitHubReadFileTool = createGitHubReadFileTool;
|
|
48138
50313
|
exports.createGlobTool = createGlobTool;
|
|
@@ -48140,6 +50315,7 @@ exports.createGrepTool = createGrepTool;
|
|
|
48140
50315
|
exports.createImageGenerationTool = createImageGenerationTool;
|
|
48141
50316
|
exports.createImageProvider = createImageProvider;
|
|
48142
50317
|
exports.createListDirectoryTool = createListDirectoryTool;
|
|
50318
|
+
exports.createMeetingTool = createMeetingTool;
|
|
48143
50319
|
exports.createMessageWithImages = createMessageWithImages;
|
|
48144
50320
|
exports.createMetricsCollector = createMetricsCollector;
|
|
48145
50321
|
exports.createPRCommentsTool = createPRCommentsTool;
|
|
@@ -48147,8 +50323,11 @@ exports.createPRFilesTool = createPRFilesTool;
|
|
|
48147
50323
|
exports.createPlan = createPlan;
|
|
48148
50324
|
exports.createProvider = createProvider;
|
|
48149
50325
|
exports.createReadFileTool = createReadFileTool;
|
|
50326
|
+
exports.createRoutineDefinition = createRoutineDefinition;
|
|
50327
|
+
exports.createRoutineExecution = createRoutineExecution;
|
|
48150
50328
|
exports.createSearchCodeTool = createSearchCodeTool;
|
|
48151
50329
|
exports.createSearchFilesTool = createSearchFilesTool;
|
|
50330
|
+
exports.createSendEmailTool = createSendEmailTool;
|
|
48152
50331
|
exports.createSpeechToTextTool = createSpeechToTextTool;
|
|
48153
50332
|
exports.createTask = createTask;
|
|
48154
50333
|
exports.createTextMessage = createTextMessage;
|
|
@@ -48181,12 +50360,15 @@ exports.developerTools = developerTools;
|
|
|
48181
50360
|
exports.documentToContent = documentToContent;
|
|
48182
50361
|
exports.editFile = editFile;
|
|
48183
50362
|
exports.evaluateCondition = evaluateCondition;
|
|
50363
|
+
exports.executeRoutine = executeRoutine;
|
|
48184
50364
|
exports.extractJSON = extractJSON;
|
|
48185
50365
|
exports.extractJSONField = extractJSONField;
|
|
48186
50366
|
exports.extractNumber = extractNumber;
|
|
48187
50367
|
exports.findConnectorByServiceTypes = findConnectorByServiceTypes;
|
|
48188
50368
|
exports.forPlan = forPlan;
|
|
48189
50369
|
exports.forTasks = forTasks;
|
|
50370
|
+
exports.formatAttendees = formatAttendees;
|
|
50371
|
+
exports.formatRecipients = formatRecipients;
|
|
48190
50372
|
exports.generateEncryptionKey = generateEncryptionKey;
|
|
48191
50373
|
exports.generateSimplePlan = generateSimplePlan;
|
|
48192
50374
|
exports.generateWebAPITool = generateWebAPITool;
|
|
@@ -48213,6 +50395,7 @@ exports.getModelInfo = getModelInfo;
|
|
|
48213
50395
|
exports.getModelsByVendor = getModelsByVendor;
|
|
48214
50396
|
exports.getNextExecutableTasks = getNextExecutableTasks;
|
|
48215
50397
|
exports.getRegisteredScrapeProviders = getRegisteredScrapeProviders;
|
|
50398
|
+
exports.getRoutineProgress = getRoutineProgress;
|
|
48216
50399
|
exports.getSTTModelInfo = getSTTModelInfo;
|
|
48217
50400
|
exports.getSTTModelsByVendor = getSTTModelsByVendor;
|
|
48218
50401
|
exports.getSTTModelsWithFeature = getSTTModelsWithFeature;
|
|
@@ -48229,6 +50412,7 @@ exports.getToolCategories = getToolCategories;
|
|
|
48229
50412
|
exports.getToolRegistry = getToolRegistry;
|
|
48230
50413
|
exports.getToolsByCategory = getToolsByCategory;
|
|
48231
50414
|
exports.getToolsRequiringConnector = getToolsRequiringConnector;
|
|
50415
|
+
exports.getUserPathPrefix = getUserPathPrefix;
|
|
48232
50416
|
exports.getVendorAuthTemplate = getVendorAuthTemplate;
|
|
48233
50417
|
exports.getVendorColor = getVendorColor;
|
|
48234
50418
|
exports.getVendorDefaultBaseURL = getVendorDefaultBaseURL;
|
|
@@ -48257,6 +50441,7 @@ exports.isSimpleScope = isSimpleScope;
|
|
|
48257
50441
|
exports.isStreamEvent = isStreamEvent;
|
|
48258
50442
|
exports.isTaskAwareScope = isTaskAwareScope;
|
|
48259
50443
|
exports.isTaskBlocked = isTaskBlocked;
|
|
50444
|
+
exports.isTeamsMeetingUrl = isTeamsMeetingUrl;
|
|
48260
50445
|
exports.isTerminalMemoryStatus = isTerminalMemoryStatus;
|
|
48261
50446
|
exports.isTerminalStatus = isTerminalStatus;
|
|
48262
50447
|
exports.isToolCallArgumentsDelta = isToolCallArgumentsDelta;
|
|
@@ -48272,6 +50457,8 @@ exports.listVendorsByAuthType = listVendorsByAuthType;
|
|
|
48272
50457
|
exports.listVendorsByCategory = listVendorsByCategory;
|
|
48273
50458
|
exports.listVendorsWithLogos = listVendorsWithLogos;
|
|
48274
50459
|
exports.mergeTextPieces = mergeTextPieces;
|
|
50460
|
+
exports.microsoftFetch = microsoftFetch;
|
|
50461
|
+
exports.normalizeEmails = normalizeEmails;
|
|
48275
50462
|
exports.parseKeyCombo = parseKeyCombo;
|
|
48276
50463
|
exports.parseRepository = parseRepository;
|
|
48277
50464
|
exports.readClipboardImage = readClipboardImage;
|
|
@@ -48282,6 +50469,7 @@ exports.resetDefaultDriver = resetDefaultDriver;
|
|
|
48282
50469
|
exports.resolveConnector = resolveConnector;
|
|
48283
50470
|
exports.resolveDependencies = resolveDependencies;
|
|
48284
50471
|
exports.resolveMaxContextTokens = resolveMaxContextTokens;
|
|
50472
|
+
exports.resolveMeetingId = resolveMeetingId;
|
|
48285
50473
|
exports.resolveModelCapabilities = resolveModelCapabilities;
|
|
48286
50474
|
exports.resolveRepository = resolveRepository;
|
|
48287
50475
|
exports.retryWithBackoff = retryWithBackoff;
|