@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.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as crypto2 from 'crypto';
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
3
|
import { importPKCS8, SignJWT } from 'jose';
|
|
4
|
-
import * as
|
|
4
|
+
import * as fs19 from 'fs';
|
|
5
5
|
import { promises, existsSync } from 'fs';
|
|
6
6
|
import { EventEmitter } from 'eventemitter3';
|
|
7
7
|
import * as path2 from 'path';
|
|
@@ -19,7 +19,7 @@ import * as z from 'zod/v4';
|
|
|
19
19
|
import spawn$1 from 'cross-spawn';
|
|
20
20
|
import process2 from 'process';
|
|
21
21
|
import { PassThrough } from 'stream';
|
|
22
|
-
import * as
|
|
22
|
+
import * as fs18 from 'fs/promises';
|
|
23
23
|
import { stat, readFile, mkdir, writeFile, readdir } from 'fs/promises';
|
|
24
24
|
import * as simpleIcons from 'simple-icons';
|
|
25
25
|
import { exec, spawn } from 'child_process';
|
|
@@ -641,7 +641,7 @@ var init_JWTBearer = __esm({
|
|
|
641
641
|
this.privateKey = config.privateKey;
|
|
642
642
|
} else if (config.privateKeyPath) {
|
|
643
643
|
try {
|
|
644
|
-
this.privateKey =
|
|
644
|
+
this.privateKey = fs19.readFileSync(config.privateKeyPath, "utf8");
|
|
645
645
|
} catch (error) {
|
|
646
646
|
throw new Error(`Failed to read private key from ${config.privateKeyPath}: ${error.message}`);
|
|
647
647
|
}
|
|
@@ -1400,10 +1400,10 @@ var init_Logger = __esm({
|
|
|
1400
1400
|
initFileStream(filePath) {
|
|
1401
1401
|
try {
|
|
1402
1402
|
const dir = path2.dirname(filePath);
|
|
1403
|
-
if (!
|
|
1404
|
-
|
|
1403
|
+
if (!fs19.existsSync(dir)) {
|
|
1404
|
+
fs19.mkdirSync(dir, { recursive: true });
|
|
1405
1405
|
}
|
|
1406
|
-
this.fileStream =
|
|
1406
|
+
this.fileStream = fs19.createWriteStream(filePath, {
|
|
1407
1407
|
flags: "a",
|
|
1408
1408
|
// append mode
|
|
1409
1409
|
encoding: "utf8"
|
|
@@ -9133,12 +9133,12 @@ var require_dist = __commonJS({
|
|
|
9133
9133
|
throw new Error(`Unknown format "${name}"`);
|
|
9134
9134
|
return f;
|
|
9135
9135
|
};
|
|
9136
|
-
function addFormats(ajv, list,
|
|
9136
|
+
function addFormats(ajv, list, fs20, exportName) {
|
|
9137
9137
|
var _a;
|
|
9138
9138
|
var _b;
|
|
9139
9139
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
9140
9140
|
for (const f of list)
|
|
9141
|
-
ajv.addFormat(f,
|
|
9141
|
+
ajv.addFormat(f, fs20[f]);
|
|
9142
9142
|
}
|
|
9143
9143
|
module.exports = exports$1 = formatsPlugin;
|
|
9144
9144
|
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
@@ -10141,6 +10141,11 @@ var DEFAULT_ALLOWLIST = [
|
|
|
10141
10141
|
"instructions_remove",
|
|
10142
10142
|
"instructions_list",
|
|
10143
10143
|
"instructions_clear",
|
|
10144
|
+
// User info tools (user-specific data - safe)
|
|
10145
|
+
"user_info_set",
|
|
10146
|
+
"user_info_get",
|
|
10147
|
+
"user_info_remove",
|
|
10148
|
+
"user_info_clear",
|
|
10144
10149
|
// Meta-tools (internal coordination)
|
|
10145
10150
|
"_start_planning",
|
|
10146
10151
|
"_modify_plan",
|
|
@@ -15440,6 +15445,572 @@ ${entry.content}`).join("\n\n");
|
|
|
15440
15445
|
};
|
|
15441
15446
|
}
|
|
15442
15447
|
};
|
|
15448
|
+
function getDefaultBaseDirectory2() {
|
|
15449
|
+
const platform2 = process.platform;
|
|
15450
|
+
if (platform2 === "win32") {
|
|
15451
|
+
const appData = process.env.APPDATA || process.env.LOCALAPPDATA;
|
|
15452
|
+
if (appData) {
|
|
15453
|
+
return join(appData, "oneringai", "users");
|
|
15454
|
+
}
|
|
15455
|
+
}
|
|
15456
|
+
return join(homedir(), ".oneringai", "users");
|
|
15457
|
+
}
|
|
15458
|
+
var DEFAULT_USER_ID = "default";
|
|
15459
|
+
function sanitizeUserId(userId) {
|
|
15460
|
+
if (!userId) return DEFAULT_USER_ID;
|
|
15461
|
+
return userId.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase() || DEFAULT_USER_ID;
|
|
15462
|
+
}
|
|
15463
|
+
var FileUserInfoStorage = class {
|
|
15464
|
+
baseDirectory;
|
|
15465
|
+
filename;
|
|
15466
|
+
constructor(config) {
|
|
15467
|
+
this.baseDirectory = config?.baseDirectory ?? getDefaultBaseDirectory2();
|
|
15468
|
+
this.filename = config?.filename ?? "user_info.json";
|
|
15469
|
+
}
|
|
15470
|
+
/**
|
|
15471
|
+
* Get the directory path for a specific user
|
|
15472
|
+
*/
|
|
15473
|
+
getUserDirectory(userId) {
|
|
15474
|
+
const sanitizedId = sanitizeUserId(userId);
|
|
15475
|
+
return join(this.baseDirectory, sanitizedId);
|
|
15476
|
+
}
|
|
15477
|
+
/**
|
|
15478
|
+
* Get the file path for a specific user
|
|
15479
|
+
*/
|
|
15480
|
+
getUserFilePath(userId) {
|
|
15481
|
+
return join(this.getUserDirectory(userId), this.filename);
|
|
15482
|
+
}
|
|
15483
|
+
/**
|
|
15484
|
+
* Load user info entries from file for a specific user
|
|
15485
|
+
*/
|
|
15486
|
+
async load(userId) {
|
|
15487
|
+
const filePath = this.getUserFilePath(userId);
|
|
15488
|
+
try {
|
|
15489
|
+
const raw = await promises.readFile(filePath, "utf-8");
|
|
15490
|
+
const data = JSON.parse(raw);
|
|
15491
|
+
if (data.version === 1 && Array.isArray(data.entries)) {
|
|
15492
|
+
return data.entries.length > 0 ? data.entries : null;
|
|
15493
|
+
}
|
|
15494
|
+
return null;
|
|
15495
|
+
} catch (error) {
|
|
15496
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) {
|
|
15497
|
+
throw error;
|
|
15498
|
+
}
|
|
15499
|
+
return null;
|
|
15500
|
+
}
|
|
15501
|
+
}
|
|
15502
|
+
/**
|
|
15503
|
+
* Save user info entries to file for a specific user
|
|
15504
|
+
* Creates directory if it doesn't exist.
|
|
15505
|
+
*/
|
|
15506
|
+
async save(userId, entries) {
|
|
15507
|
+
const directory = this.getUserDirectory(userId);
|
|
15508
|
+
const filePath = this.getUserFilePath(userId);
|
|
15509
|
+
await this.ensureDirectory(directory);
|
|
15510
|
+
const data = {
|
|
15511
|
+
version: 1,
|
|
15512
|
+
userId: userId || DEFAULT_USER_ID,
|
|
15513
|
+
entries
|
|
15514
|
+
};
|
|
15515
|
+
const tempPath = `${filePath}.tmp`;
|
|
15516
|
+
try {
|
|
15517
|
+
await promises.writeFile(tempPath, JSON.stringify(data, null, 2), "utf-8");
|
|
15518
|
+
await promises.rename(tempPath, filePath);
|
|
15519
|
+
} catch (error) {
|
|
15520
|
+
try {
|
|
15521
|
+
await promises.unlink(tempPath);
|
|
15522
|
+
} catch {
|
|
15523
|
+
}
|
|
15524
|
+
throw error;
|
|
15525
|
+
}
|
|
15526
|
+
}
|
|
15527
|
+
/**
|
|
15528
|
+
* Delete user info file for a specific user
|
|
15529
|
+
*/
|
|
15530
|
+
async delete(userId) {
|
|
15531
|
+
const filePath = this.getUserFilePath(userId);
|
|
15532
|
+
try {
|
|
15533
|
+
await promises.unlink(filePath);
|
|
15534
|
+
} catch (error) {
|
|
15535
|
+
if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
|
|
15536
|
+
throw error;
|
|
15537
|
+
}
|
|
15538
|
+
}
|
|
15539
|
+
}
|
|
15540
|
+
/**
|
|
15541
|
+
* Check if user info file exists for a specific user
|
|
15542
|
+
*/
|
|
15543
|
+
async exists(userId) {
|
|
15544
|
+
const filePath = this.getUserFilePath(userId);
|
|
15545
|
+
try {
|
|
15546
|
+
await promises.access(filePath);
|
|
15547
|
+
return true;
|
|
15548
|
+
} catch {
|
|
15549
|
+
return false;
|
|
15550
|
+
}
|
|
15551
|
+
}
|
|
15552
|
+
/**
|
|
15553
|
+
* Get the file path for a specific user (for display/debugging)
|
|
15554
|
+
*/
|
|
15555
|
+
getPath(userId) {
|
|
15556
|
+
return this.getUserFilePath(userId);
|
|
15557
|
+
}
|
|
15558
|
+
/**
|
|
15559
|
+
* Ensure the directory exists
|
|
15560
|
+
*/
|
|
15561
|
+
async ensureDirectory(directory) {
|
|
15562
|
+
try {
|
|
15563
|
+
await promises.mkdir(directory, { recursive: true });
|
|
15564
|
+
} catch (error) {
|
|
15565
|
+
if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
|
|
15566
|
+
throw error;
|
|
15567
|
+
}
|
|
15568
|
+
}
|
|
15569
|
+
}
|
|
15570
|
+
};
|
|
15571
|
+
|
|
15572
|
+
// src/core/context-nextgen/plugins/UserInfoPluginNextGen.ts
|
|
15573
|
+
init_StorageRegistry();
|
|
15574
|
+
var DEFAULT_MAX_TOTAL_SIZE = 1e5;
|
|
15575
|
+
var DEFAULT_MAX_ENTRIES2 = 100;
|
|
15576
|
+
var KEY_MAX_LENGTH2 = 100;
|
|
15577
|
+
var KEY_PATTERN2 = /^[a-zA-Z0-9_-]+$/;
|
|
15578
|
+
var USER_INFO_INSTRUCTIONS = `User Info stores key-value information about the current user.
|
|
15579
|
+
Data is user-specific and persists across sessions and agents.
|
|
15580
|
+
User info is automatically shown in context \u2014 no need to call user_info_get every turn.
|
|
15581
|
+
|
|
15582
|
+
**To manage:**
|
|
15583
|
+
- \`user_info_set(key, value, description?)\`: Store/update user information
|
|
15584
|
+
- \`user_info_get(key?)\`: Retrieve one entry by key, or all entries if no key
|
|
15585
|
+
- \`user_info_remove(key)\`: Remove a specific entry
|
|
15586
|
+
- \`user_info_clear(confirm: true)\`: Remove all entries (destructive!)
|
|
15587
|
+
|
|
15588
|
+
**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.
|
|
15589
|
+
|
|
15590
|
+
**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.
|
|
15591
|
+
|
|
15592
|
+
**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!`;
|
|
15593
|
+
var userInfoSetDefinition = {
|
|
15594
|
+
type: "function",
|
|
15595
|
+
function: {
|
|
15596
|
+
name: "user_info_set",
|
|
15597
|
+
description: `Store or update user information by key. Data persists across sessions.
|
|
15598
|
+
If the key exists, it will be updated. If not, a new entry is created.`,
|
|
15599
|
+
parameters: {
|
|
15600
|
+
type: "object",
|
|
15601
|
+
properties: {
|
|
15602
|
+
key: {
|
|
15603
|
+
type: "string",
|
|
15604
|
+
description: "Unique key for the information (alphanumeric, dash, underscore; max 100 chars)"
|
|
15605
|
+
},
|
|
15606
|
+
value: {
|
|
15607
|
+
description: "Value to store (any JSON-serializable data: string, number, boolean, object, array)"
|
|
15608
|
+
},
|
|
15609
|
+
description: {
|
|
15610
|
+
type: "string",
|
|
15611
|
+
description: "Optional description for self-documentation"
|
|
15612
|
+
}
|
|
15613
|
+
},
|
|
15614
|
+
required: ["key", "value"]
|
|
15615
|
+
}
|
|
15616
|
+
}
|
|
15617
|
+
};
|
|
15618
|
+
var userInfoGetDefinition = {
|
|
15619
|
+
type: "function",
|
|
15620
|
+
function: {
|
|
15621
|
+
name: "user_info_get",
|
|
15622
|
+
description: "Retrieve user information. If key is provided, returns that entry. Otherwise returns all entries.",
|
|
15623
|
+
parameters: {
|
|
15624
|
+
type: "object",
|
|
15625
|
+
properties: {
|
|
15626
|
+
key: {
|
|
15627
|
+
type: "string",
|
|
15628
|
+
description: "Key of the entry to retrieve (optional - omit to get all entries)"
|
|
15629
|
+
}
|
|
15630
|
+
},
|
|
15631
|
+
required: []
|
|
15632
|
+
}
|
|
15633
|
+
}
|
|
15634
|
+
};
|
|
15635
|
+
var userInfoRemoveDefinition = {
|
|
15636
|
+
type: "function",
|
|
15637
|
+
function: {
|
|
15638
|
+
name: "user_info_remove",
|
|
15639
|
+
description: "Remove a specific user information entry by key.",
|
|
15640
|
+
parameters: {
|
|
15641
|
+
type: "object",
|
|
15642
|
+
properties: {
|
|
15643
|
+
key: {
|
|
15644
|
+
type: "string",
|
|
15645
|
+
description: "Key of the entry to remove"
|
|
15646
|
+
}
|
|
15647
|
+
},
|
|
15648
|
+
required: ["key"]
|
|
15649
|
+
}
|
|
15650
|
+
}
|
|
15651
|
+
};
|
|
15652
|
+
var userInfoClearDefinition = {
|
|
15653
|
+
type: "function",
|
|
15654
|
+
function: {
|
|
15655
|
+
name: "user_info_clear",
|
|
15656
|
+
description: "Clear all user information entries (DESTRUCTIVE). Requires confirmation.",
|
|
15657
|
+
parameters: {
|
|
15658
|
+
type: "object",
|
|
15659
|
+
properties: {
|
|
15660
|
+
confirm: {
|
|
15661
|
+
type: "boolean",
|
|
15662
|
+
description: "Must be true to confirm deletion"
|
|
15663
|
+
}
|
|
15664
|
+
},
|
|
15665
|
+
required: ["confirm"]
|
|
15666
|
+
}
|
|
15667
|
+
}
|
|
15668
|
+
};
|
|
15669
|
+
function validateKey2(key) {
|
|
15670
|
+
if (typeof key !== "string") return "Key must be a string";
|
|
15671
|
+
const trimmed = key.trim();
|
|
15672
|
+
if (trimmed.length === 0) return "Key cannot be empty";
|
|
15673
|
+
if (trimmed.length > KEY_MAX_LENGTH2) return `Key exceeds maximum length (${KEY_MAX_LENGTH2} chars)`;
|
|
15674
|
+
if (!KEY_PATTERN2.test(trimmed)) return "Key must contain only alphanumeric characters, dashes, and underscores";
|
|
15675
|
+
return null;
|
|
15676
|
+
}
|
|
15677
|
+
function getValueType(value) {
|
|
15678
|
+
if (value === null) return "null";
|
|
15679
|
+
if (Array.isArray(value)) return "array";
|
|
15680
|
+
return typeof value;
|
|
15681
|
+
}
|
|
15682
|
+
function calculateValueSize(value) {
|
|
15683
|
+
const json = JSON.stringify(value);
|
|
15684
|
+
return Buffer.byteLength(json, "utf-8");
|
|
15685
|
+
}
|
|
15686
|
+
function buildStorageContext(toolContext) {
|
|
15687
|
+
const global2 = StorageRegistry.getContext();
|
|
15688
|
+
if (global2) return global2;
|
|
15689
|
+
if (toolContext?.userId) return { userId: toolContext.userId };
|
|
15690
|
+
return void 0;
|
|
15691
|
+
}
|
|
15692
|
+
function formatValue(value) {
|
|
15693
|
+
if (value === null) return "null";
|
|
15694
|
+
if (typeof value === "string") return value;
|
|
15695
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
15696
|
+
return JSON.stringify(value);
|
|
15697
|
+
}
|
|
15698
|
+
var UserInfoPluginNextGen = class {
|
|
15699
|
+
name = "user_info";
|
|
15700
|
+
_destroyed = false;
|
|
15701
|
+
_storage = null;
|
|
15702
|
+
/** In-memory cache of entries */
|
|
15703
|
+
_entries = /* @__PURE__ */ new Map();
|
|
15704
|
+
/** Whether entries have been loaded from storage */
|
|
15705
|
+
_initialized = false;
|
|
15706
|
+
maxTotalSize;
|
|
15707
|
+
maxEntries;
|
|
15708
|
+
estimator = simpleTokenEstimator;
|
|
15709
|
+
explicitStorage;
|
|
15710
|
+
/** UserId for getContent() and lazy initialization */
|
|
15711
|
+
userId;
|
|
15712
|
+
_tokenCache = null;
|
|
15713
|
+
_instructionsTokenCache = null;
|
|
15714
|
+
constructor(config) {
|
|
15715
|
+
this.maxTotalSize = config?.maxTotalSize ?? DEFAULT_MAX_TOTAL_SIZE;
|
|
15716
|
+
this.maxEntries = config?.maxEntries ?? DEFAULT_MAX_ENTRIES2;
|
|
15717
|
+
this.explicitStorage = config?.storage;
|
|
15718
|
+
this.userId = config?.userId;
|
|
15719
|
+
}
|
|
15720
|
+
// ============================================================================
|
|
15721
|
+
// IContextPluginNextGen Implementation
|
|
15722
|
+
// ============================================================================
|
|
15723
|
+
getInstructions() {
|
|
15724
|
+
return USER_INFO_INSTRUCTIONS;
|
|
15725
|
+
}
|
|
15726
|
+
async getContent() {
|
|
15727
|
+
await this.ensureInitialized();
|
|
15728
|
+
if (this._entries.size === 0) {
|
|
15729
|
+
this._tokenCache = 0;
|
|
15730
|
+
return null;
|
|
15731
|
+
}
|
|
15732
|
+
const rendered = this.renderContent();
|
|
15733
|
+
this._tokenCache = this.estimator.estimateTokens(rendered);
|
|
15734
|
+
return rendered;
|
|
15735
|
+
}
|
|
15736
|
+
getContents() {
|
|
15737
|
+
return new Map(this._entries);
|
|
15738
|
+
}
|
|
15739
|
+
getTokenSize() {
|
|
15740
|
+
return this._tokenCache ?? 0;
|
|
15741
|
+
}
|
|
15742
|
+
getInstructionsTokenSize() {
|
|
15743
|
+
if (this._instructionsTokenCache === null) {
|
|
15744
|
+
this._instructionsTokenCache = this.estimator.estimateTokens(USER_INFO_INSTRUCTIONS);
|
|
15745
|
+
}
|
|
15746
|
+
return this._instructionsTokenCache;
|
|
15747
|
+
}
|
|
15748
|
+
isCompactable() {
|
|
15749
|
+
return false;
|
|
15750
|
+
}
|
|
15751
|
+
async compact(_targetTokensToFree) {
|
|
15752
|
+
return 0;
|
|
15753
|
+
}
|
|
15754
|
+
getTools() {
|
|
15755
|
+
return [
|
|
15756
|
+
this.createUserInfoSetTool(),
|
|
15757
|
+
this.createUserInfoGetTool(),
|
|
15758
|
+
this.createUserInfoRemoveTool(),
|
|
15759
|
+
this.createUserInfoClearTool()
|
|
15760
|
+
];
|
|
15761
|
+
}
|
|
15762
|
+
destroy() {
|
|
15763
|
+
if (this._destroyed) return;
|
|
15764
|
+
this._entries.clear();
|
|
15765
|
+
this._destroyed = true;
|
|
15766
|
+
this._tokenCache = null;
|
|
15767
|
+
}
|
|
15768
|
+
getState() {
|
|
15769
|
+
return {
|
|
15770
|
+
version: 1,
|
|
15771
|
+
entries: Array.from(this._entries.values()),
|
|
15772
|
+
userId: this.userId
|
|
15773
|
+
};
|
|
15774
|
+
}
|
|
15775
|
+
restoreState(state) {
|
|
15776
|
+
if (!state || typeof state !== "object") return;
|
|
15777
|
+
const s = state;
|
|
15778
|
+
if ("version" in s && s.version === 1 && Array.isArray(s.entries)) {
|
|
15779
|
+
this._entries.clear();
|
|
15780
|
+
for (const entry of s.entries) {
|
|
15781
|
+
this._entries.set(entry.id, entry);
|
|
15782
|
+
}
|
|
15783
|
+
this._initialized = true;
|
|
15784
|
+
this._tokenCache = null;
|
|
15785
|
+
}
|
|
15786
|
+
}
|
|
15787
|
+
// ============================================================================
|
|
15788
|
+
// Public API
|
|
15789
|
+
// ============================================================================
|
|
15790
|
+
/**
|
|
15791
|
+
* Check if initialized
|
|
15792
|
+
*/
|
|
15793
|
+
get isInitialized() {
|
|
15794
|
+
return this._initialized;
|
|
15795
|
+
}
|
|
15796
|
+
// ============================================================================
|
|
15797
|
+
// Private Helpers
|
|
15798
|
+
// ============================================================================
|
|
15799
|
+
assertNotDestroyed() {
|
|
15800
|
+
if (this._destroyed) {
|
|
15801
|
+
throw new Error("UserInfoPluginNextGen is destroyed");
|
|
15802
|
+
}
|
|
15803
|
+
}
|
|
15804
|
+
/**
|
|
15805
|
+
* Lazy load entries from storage
|
|
15806
|
+
*/
|
|
15807
|
+
async ensureInitialized() {
|
|
15808
|
+
if (this._initialized || this._destroyed) return;
|
|
15809
|
+
try {
|
|
15810
|
+
const storage = this.resolveStorage();
|
|
15811
|
+
const entries = await storage.load(this.userId);
|
|
15812
|
+
this._entries.clear();
|
|
15813
|
+
if (entries) {
|
|
15814
|
+
for (const entry of entries) {
|
|
15815
|
+
this._entries.set(entry.id, entry);
|
|
15816
|
+
}
|
|
15817
|
+
}
|
|
15818
|
+
this._initialized = true;
|
|
15819
|
+
} catch (error) {
|
|
15820
|
+
console.warn(`Failed to load user info for userId '${this.userId ?? "default"}':`, error);
|
|
15821
|
+
this._entries.clear();
|
|
15822
|
+
this._initialized = true;
|
|
15823
|
+
}
|
|
15824
|
+
this._tokenCache = null;
|
|
15825
|
+
}
|
|
15826
|
+
/**
|
|
15827
|
+
* Render entries as markdown for context injection
|
|
15828
|
+
*/
|
|
15829
|
+
renderContent() {
|
|
15830
|
+
const sorted = Array.from(this._entries.values()).sort((a, b) => a.createdAt - b.createdAt);
|
|
15831
|
+
return sorted.map((entry) => `### ${entry.id}
|
|
15832
|
+
${formatValue(entry.value)}`).join("\n\n");
|
|
15833
|
+
}
|
|
15834
|
+
/**
|
|
15835
|
+
* Resolve storage instance (lazy singleton)
|
|
15836
|
+
*/
|
|
15837
|
+
resolveStorage(context) {
|
|
15838
|
+
if (this._storage) return this._storage;
|
|
15839
|
+
if (this.explicitStorage) {
|
|
15840
|
+
this._storage = this.explicitStorage;
|
|
15841
|
+
return this._storage;
|
|
15842
|
+
}
|
|
15843
|
+
const factory = StorageRegistry.get("userInfo");
|
|
15844
|
+
if (factory) {
|
|
15845
|
+
this._storage = factory(buildStorageContext(context));
|
|
15846
|
+
return this._storage;
|
|
15847
|
+
}
|
|
15848
|
+
this._storage = new FileUserInfoStorage();
|
|
15849
|
+
return this._storage;
|
|
15850
|
+
}
|
|
15851
|
+
/**
|
|
15852
|
+
* Persist current entries to storage
|
|
15853
|
+
*/
|
|
15854
|
+
async persistToStorage(userId) {
|
|
15855
|
+
const storage = this.resolveStorage();
|
|
15856
|
+
if (this._entries.size === 0) {
|
|
15857
|
+
await storage.delete(userId);
|
|
15858
|
+
} else {
|
|
15859
|
+
await storage.save(userId, Array.from(this._entries.values()));
|
|
15860
|
+
}
|
|
15861
|
+
}
|
|
15862
|
+
// ============================================================================
|
|
15863
|
+
// Tool Factories
|
|
15864
|
+
// ============================================================================
|
|
15865
|
+
createUserInfoSetTool() {
|
|
15866
|
+
return {
|
|
15867
|
+
definition: userInfoSetDefinition,
|
|
15868
|
+
execute: async (args, context) => {
|
|
15869
|
+
this.assertNotDestroyed();
|
|
15870
|
+
await this.ensureInitialized();
|
|
15871
|
+
const userId = context?.userId ?? this.userId;
|
|
15872
|
+
const key = args.key;
|
|
15873
|
+
const value = args.value;
|
|
15874
|
+
const description = args.description;
|
|
15875
|
+
const keyError = validateKey2(key);
|
|
15876
|
+
if (keyError) {
|
|
15877
|
+
return { error: keyError };
|
|
15878
|
+
}
|
|
15879
|
+
const trimmedKey = key.trim();
|
|
15880
|
+
if (value === void 0) {
|
|
15881
|
+
return { error: "Value cannot be undefined. Use null for explicit null value." };
|
|
15882
|
+
}
|
|
15883
|
+
if (!this._entries.has(trimmedKey) && this._entries.size >= this.maxEntries) {
|
|
15884
|
+
return { error: `Maximum number of entries reached (${this.maxEntries})` };
|
|
15885
|
+
}
|
|
15886
|
+
const valueSize = calculateValueSize(value);
|
|
15887
|
+
let currentTotal = 0;
|
|
15888
|
+
for (const e of this._entries.values()) {
|
|
15889
|
+
currentTotal += calculateValueSize(e.value);
|
|
15890
|
+
}
|
|
15891
|
+
const existingSize = this._entries.has(trimmedKey) ? calculateValueSize(this._entries.get(trimmedKey).value) : 0;
|
|
15892
|
+
const newTotal = currentTotal - existingSize + valueSize;
|
|
15893
|
+
if (newTotal > this.maxTotalSize) {
|
|
15894
|
+
return { error: `Total size would exceed maximum (${this.maxTotalSize} bytes)` };
|
|
15895
|
+
}
|
|
15896
|
+
const now = Date.now();
|
|
15897
|
+
const existing = this._entries.get(trimmedKey);
|
|
15898
|
+
const entry = {
|
|
15899
|
+
id: trimmedKey,
|
|
15900
|
+
value,
|
|
15901
|
+
valueType: getValueType(value),
|
|
15902
|
+
description,
|
|
15903
|
+
createdAt: existing?.createdAt ?? now,
|
|
15904
|
+
updatedAt: now
|
|
15905
|
+
};
|
|
15906
|
+
this._entries.set(trimmedKey, entry);
|
|
15907
|
+
this._tokenCache = null;
|
|
15908
|
+
await this.persistToStorage(userId);
|
|
15909
|
+
return {
|
|
15910
|
+
success: true,
|
|
15911
|
+
message: existing ? `User info '${trimmedKey}' updated` : `User info '${trimmedKey}' added`,
|
|
15912
|
+
key: trimmedKey,
|
|
15913
|
+
valueType: entry.valueType,
|
|
15914
|
+
valueSize
|
|
15915
|
+
};
|
|
15916
|
+
},
|
|
15917
|
+
permission: { scope: "always", riskLevel: "low" },
|
|
15918
|
+
describeCall: (args) => `set user info '${args.key}'`
|
|
15919
|
+
};
|
|
15920
|
+
}
|
|
15921
|
+
createUserInfoGetTool() {
|
|
15922
|
+
return {
|
|
15923
|
+
definition: userInfoGetDefinition,
|
|
15924
|
+
execute: async (args, _context) => {
|
|
15925
|
+
this.assertNotDestroyed();
|
|
15926
|
+
await this.ensureInitialized();
|
|
15927
|
+
const key = args.key;
|
|
15928
|
+
if (this._entries.size === 0) {
|
|
15929
|
+
return { error: "User info not found" };
|
|
15930
|
+
}
|
|
15931
|
+
if (key !== void 0) {
|
|
15932
|
+
const trimmedKey = key.trim();
|
|
15933
|
+
const entry = this._entries.get(trimmedKey);
|
|
15934
|
+
if (!entry) {
|
|
15935
|
+
return { error: `User info '${trimmedKey}' not found` };
|
|
15936
|
+
}
|
|
15937
|
+
return {
|
|
15938
|
+
key: entry.id,
|
|
15939
|
+
value: entry.value,
|
|
15940
|
+
valueType: entry.valueType,
|
|
15941
|
+
description: entry.description,
|
|
15942
|
+
createdAt: entry.createdAt,
|
|
15943
|
+
updatedAt: entry.updatedAt
|
|
15944
|
+
};
|
|
15945
|
+
}
|
|
15946
|
+
const entries = Array.from(this._entries.values());
|
|
15947
|
+
return {
|
|
15948
|
+
count: entries.length,
|
|
15949
|
+
entries: entries.map((e) => ({
|
|
15950
|
+
key: e.id,
|
|
15951
|
+
value: e.value,
|
|
15952
|
+
valueType: e.valueType,
|
|
15953
|
+
description: e.description,
|
|
15954
|
+
createdAt: e.createdAt,
|
|
15955
|
+
updatedAt: e.updatedAt
|
|
15956
|
+
}))
|
|
15957
|
+
};
|
|
15958
|
+
},
|
|
15959
|
+
permission: { scope: "always", riskLevel: "low" },
|
|
15960
|
+
describeCall: (args) => args.key ? `get user info '${args.key}'` : "get all user info"
|
|
15961
|
+
};
|
|
15962
|
+
}
|
|
15963
|
+
createUserInfoRemoveTool() {
|
|
15964
|
+
return {
|
|
15965
|
+
definition: userInfoRemoveDefinition,
|
|
15966
|
+
execute: async (args, context) => {
|
|
15967
|
+
this.assertNotDestroyed();
|
|
15968
|
+
await this.ensureInitialized();
|
|
15969
|
+
const userId = context?.userId ?? this.userId;
|
|
15970
|
+
const key = args.key;
|
|
15971
|
+
if (!key || typeof key !== "string" || key.trim().length === 0) {
|
|
15972
|
+
return { error: "Key is required" };
|
|
15973
|
+
}
|
|
15974
|
+
const trimmedKey = key.trim();
|
|
15975
|
+
if (!this._entries.has(trimmedKey)) {
|
|
15976
|
+
return { error: `User info '${trimmedKey}' not found` };
|
|
15977
|
+
}
|
|
15978
|
+
this._entries.delete(trimmedKey);
|
|
15979
|
+
this._tokenCache = null;
|
|
15980
|
+
await this.persistToStorage(userId);
|
|
15981
|
+
return {
|
|
15982
|
+
success: true,
|
|
15983
|
+
message: `User info '${trimmedKey}' removed`,
|
|
15984
|
+
key: trimmedKey
|
|
15985
|
+
};
|
|
15986
|
+
},
|
|
15987
|
+
permission: { scope: "always", riskLevel: "low" },
|
|
15988
|
+
describeCall: (args) => `remove user info '${args.key}'`
|
|
15989
|
+
};
|
|
15990
|
+
}
|
|
15991
|
+
createUserInfoClearTool() {
|
|
15992
|
+
return {
|
|
15993
|
+
definition: userInfoClearDefinition,
|
|
15994
|
+
execute: async (args, context) => {
|
|
15995
|
+
this.assertNotDestroyed();
|
|
15996
|
+
const userId = context?.userId ?? this.userId;
|
|
15997
|
+
if (args.confirm !== true) {
|
|
15998
|
+
return { error: "Must pass confirm: true to clear user info" };
|
|
15999
|
+
}
|
|
16000
|
+
this._entries.clear();
|
|
16001
|
+
this._tokenCache = null;
|
|
16002
|
+
const storage = this.resolveStorage(context);
|
|
16003
|
+
await storage.delete(userId);
|
|
16004
|
+
return {
|
|
16005
|
+
success: true,
|
|
16006
|
+
message: "All user information cleared"
|
|
16007
|
+
};
|
|
16008
|
+
},
|
|
16009
|
+
permission: { scope: "once", riskLevel: "medium" },
|
|
16010
|
+
describeCall: () => "clear user info"
|
|
16011
|
+
};
|
|
16012
|
+
}
|
|
16013
|
+
};
|
|
15443
16014
|
|
|
15444
16015
|
// src/core/context-nextgen/AgentContextNextGen.ts
|
|
15445
16016
|
init_StorageRegistry();
|
|
@@ -16088,7 +16659,8 @@ var StrategyRegistry = class {
|
|
|
16088
16659
|
var DEFAULT_FEATURES = {
|
|
16089
16660
|
workingMemory: true,
|
|
16090
16661
|
inContextMemory: false,
|
|
16091
|
-
persistentInstructions: false
|
|
16662
|
+
persistentInstructions: false,
|
|
16663
|
+
userInfo: false
|
|
16092
16664
|
};
|
|
16093
16665
|
var DEFAULT_CONFIG2 = {
|
|
16094
16666
|
responseReserve: 4096,
|
|
@@ -16206,6 +16778,13 @@ var AgentContextNextGen = class _AgentContextNextGen extends EventEmitter {
|
|
|
16206
16778
|
...piConfig
|
|
16207
16779
|
}));
|
|
16208
16780
|
}
|
|
16781
|
+
if (features.userInfo) {
|
|
16782
|
+
const uiConfig = configs.userInfo;
|
|
16783
|
+
this.registerPlugin(new UserInfoPluginNextGen({
|
|
16784
|
+
userId: this._userId,
|
|
16785
|
+
...uiConfig
|
|
16786
|
+
}));
|
|
16787
|
+
}
|
|
16209
16788
|
this.validateStrategyDependencies(this._compactionStrategy);
|
|
16210
16789
|
}
|
|
16211
16790
|
/**
|
|
@@ -16759,6 +17338,8 @@ ${content}`);
|
|
|
16759
17338
|
breakdown.pluginContents[plugin.name] = plugin.getTokenSize();
|
|
16760
17339
|
}
|
|
16761
17340
|
}
|
|
17341
|
+
const now = /* @__PURE__ */ new Date();
|
|
17342
|
+
parts.push(`CURRENT DATE AND TIME: ${now.toLocaleString("en-US", { dateStyle: "full", timeStyle: "long", timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone })}`);
|
|
16762
17343
|
const systemText = parts.join("\n\n---\n\n");
|
|
16763
17344
|
const systemTokens = this._estimator.estimateTokens(systemText);
|
|
16764
17345
|
const systemMessage = {
|
|
@@ -20217,6 +20798,7 @@ var BaseAgent = class extends EventEmitter {
|
|
|
20217
20798
|
_agentContext;
|
|
20218
20799
|
// SINGLE SOURCE OF TRUTH for tools and sessions
|
|
20219
20800
|
_permissionManager;
|
|
20801
|
+
_ownsContext = true;
|
|
20220
20802
|
_isDestroyed = false;
|
|
20221
20803
|
_cleanupCallbacks = [];
|
|
20222
20804
|
_logger;
|
|
@@ -20269,8 +20851,10 @@ var BaseAgent = class extends EventEmitter {
|
|
|
20269
20851
|
*/
|
|
20270
20852
|
initializeAgentContext(config) {
|
|
20271
20853
|
if (config.context instanceof AgentContextNextGen) {
|
|
20854
|
+
this._ownsContext = false;
|
|
20272
20855
|
return config.context;
|
|
20273
20856
|
}
|
|
20857
|
+
this._ownsContext = true;
|
|
20274
20858
|
const contextConfig = {
|
|
20275
20859
|
model: config.model,
|
|
20276
20860
|
agentId: config.name,
|
|
@@ -20782,7 +21366,9 @@ var BaseAgent = class extends EventEmitter {
|
|
|
20782
21366
|
clearInterval(this._autoSaveInterval);
|
|
20783
21367
|
this._autoSaveInterval = null;
|
|
20784
21368
|
}
|
|
20785
|
-
this.
|
|
21369
|
+
if (this._ownsContext) {
|
|
21370
|
+
this._agentContext.destroy();
|
|
21371
|
+
}
|
|
20786
21372
|
this._permissionManager.removeAllListeners();
|
|
20787
21373
|
this.removeAllListeners();
|
|
20788
21374
|
}
|
|
@@ -21123,6 +21709,18 @@ var HookManager = class {
|
|
|
21123
21709
|
}
|
|
21124
21710
|
existing.push(hook);
|
|
21125
21711
|
}
|
|
21712
|
+
/**
|
|
21713
|
+
* Unregister a specific hook function by reference.
|
|
21714
|
+
* Returns true if the hook was found and removed.
|
|
21715
|
+
*/
|
|
21716
|
+
unregister(name, hook) {
|
|
21717
|
+
const hooks = this.hooks.get(name);
|
|
21718
|
+
if (!hooks) return false;
|
|
21719
|
+
const index = hooks.indexOf(hook);
|
|
21720
|
+
if (index === -1) return false;
|
|
21721
|
+
hooks.splice(index, 1);
|
|
21722
|
+
return true;
|
|
21723
|
+
}
|
|
21126
21724
|
/**
|
|
21127
21725
|
* Execute hooks for a given name
|
|
21128
21726
|
*/
|
|
@@ -21145,7 +21743,7 @@ var HookManager = class {
|
|
|
21145
21743
|
const hook = hooks[i];
|
|
21146
21744
|
const hookKey = this.getHookKey(hook, i);
|
|
21147
21745
|
const hookResult = await this.executeHookSafely(hook, context, hookKey);
|
|
21148
|
-
if (hookResult
|
|
21746
|
+
if (hookResult == null) {
|
|
21149
21747
|
continue;
|
|
21150
21748
|
}
|
|
21151
21749
|
result = { ...result, ...hookResult };
|
|
@@ -21165,7 +21763,7 @@ var HookManager = class {
|
|
|
21165
21763
|
return this.executeHookSafely(hook, context, hookKey);
|
|
21166
21764
|
})
|
|
21167
21765
|
);
|
|
21168
|
-
const validResults = results.filter((r) => r
|
|
21766
|
+
const validResults = results.filter((r) => r != null);
|
|
21169
21767
|
return validResults.reduce(
|
|
21170
21768
|
(acc, hookResult) => ({ ...acc, ...hookResult }),
|
|
21171
21769
|
defaultResult
|
|
@@ -21841,7 +22439,6 @@ var Agent = class _Agent extends BaseAgent {
|
|
|
21841
22439
|
_cleanupExecution(streamState) {
|
|
21842
22440
|
streamState?.clear();
|
|
21843
22441
|
this.executionContext?.cleanup();
|
|
21844
|
-
this.hookManager.clear();
|
|
21845
22442
|
}
|
|
21846
22443
|
/**
|
|
21847
22444
|
* Emit iteration complete event (helper for run loop)
|
|
@@ -22726,6 +23323,27 @@ var Agent = class _Agent extends BaseAgent {
|
|
|
22726
23323
|
isCancelled() {
|
|
22727
23324
|
return this._cancelled;
|
|
22728
23325
|
}
|
|
23326
|
+
/**
|
|
23327
|
+
* Clear conversation history, resetting the context for a fresh interaction.
|
|
23328
|
+
* Plugins (working memory, in-context memory, etc.) are NOT affected.
|
|
23329
|
+
*/
|
|
23330
|
+
clearConversation(reason) {
|
|
23331
|
+
this._agentContext.clearConversation(reason);
|
|
23332
|
+
this._logger.info({ reason }, "Conversation cleared");
|
|
23333
|
+
}
|
|
23334
|
+
// ===== Hook Management =====
|
|
23335
|
+
/**
|
|
23336
|
+
* Register a hook on the agent. Can be called after creation.
|
|
23337
|
+
*/
|
|
23338
|
+
registerHook(name, hook) {
|
|
23339
|
+
this.hookManager.register(name, hook);
|
|
23340
|
+
}
|
|
23341
|
+
/**
|
|
23342
|
+
* Unregister a previously registered hook by reference.
|
|
23343
|
+
*/
|
|
23344
|
+
unregisterHook(name, hook) {
|
|
23345
|
+
return this.hookManager.unregister(name, hook);
|
|
23346
|
+
}
|
|
22729
23347
|
// ===== Cleanup =====
|
|
22730
23348
|
destroy() {
|
|
22731
23349
|
if (this._isDestroyed) {
|
|
@@ -22755,6 +23373,874 @@ var Agent = class _Agent extends BaseAgent {
|
|
|
22755
23373
|
}
|
|
22756
23374
|
};
|
|
22757
23375
|
|
|
23376
|
+
// src/domain/entities/Task.ts
|
|
23377
|
+
var TERMINAL_TASK_STATUSES = ["completed", "failed", "skipped", "cancelled"];
|
|
23378
|
+
function isTerminalStatus(status) {
|
|
23379
|
+
return TERMINAL_TASK_STATUSES.includes(status);
|
|
23380
|
+
}
|
|
23381
|
+
function createTask(input) {
|
|
23382
|
+
const now = Date.now();
|
|
23383
|
+
const id = input.id ?? `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
23384
|
+
return {
|
|
23385
|
+
id,
|
|
23386
|
+
name: input.name,
|
|
23387
|
+
description: input.description,
|
|
23388
|
+
status: "pending",
|
|
23389
|
+
dependsOn: input.dependsOn ?? [],
|
|
23390
|
+
externalDependency: input.externalDependency,
|
|
23391
|
+
condition: input.condition,
|
|
23392
|
+
execution: input.execution,
|
|
23393
|
+
suggestedTools: input.suggestedTools,
|
|
23394
|
+
validation: input.validation,
|
|
23395
|
+
expectedOutput: input.expectedOutput,
|
|
23396
|
+
attempts: 0,
|
|
23397
|
+
maxAttempts: input.maxAttempts ?? 3,
|
|
23398
|
+
createdAt: now,
|
|
23399
|
+
lastUpdatedAt: now,
|
|
23400
|
+
metadata: input.metadata
|
|
23401
|
+
};
|
|
23402
|
+
}
|
|
23403
|
+
function createPlan(input) {
|
|
23404
|
+
const now = Date.now();
|
|
23405
|
+
const id = `plan-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
23406
|
+
const tasks = input.tasks.map((taskInput) => createTask(taskInput));
|
|
23407
|
+
const nameToId = /* @__PURE__ */ new Map();
|
|
23408
|
+
for (const task of tasks) {
|
|
23409
|
+
nameToId.set(task.name, task.id);
|
|
23410
|
+
}
|
|
23411
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
23412
|
+
const taskInput = input.tasks[i];
|
|
23413
|
+
const task = tasks[i];
|
|
23414
|
+
if (taskInput.dependsOn && taskInput.dependsOn.length > 0) {
|
|
23415
|
+
task.dependsOn = taskInput.dependsOn.map((dep) => {
|
|
23416
|
+
if (dep.startsWith("task-")) {
|
|
23417
|
+
return dep;
|
|
23418
|
+
}
|
|
23419
|
+
const resolvedId = nameToId.get(dep);
|
|
23420
|
+
if (!resolvedId) {
|
|
23421
|
+
throw new Error(`Task dependency "${dep}" not found in plan`);
|
|
23422
|
+
}
|
|
23423
|
+
return resolvedId;
|
|
23424
|
+
});
|
|
23425
|
+
}
|
|
23426
|
+
}
|
|
23427
|
+
if (!input.skipCycleCheck) {
|
|
23428
|
+
const cycle = detectDependencyCycle(tasks);
|
|
23429
|
+
if (cycle) {
|
|
23430
|
+
const cycleNames = cycle.map((taskId) => {
|
|
23431
|
+
const task = tasks.find((t) => t.id === taskId);
|
|
23432
|
+
return task ? task.name : taskId;
|
|
23433
|
+
});
|
|
23434
|
+
throw new DependencyCycleError(cycleNames, id);
|
|
23435
|
+
}
|
|
23436
|
+
}
|
|
23437
|
+
return {
|
|
23438
|
+
id,
|
|
23439
|
+
goal: input.goal,
|
|
23440
|
+
context: input.context,
|
|
23441
|
+
tasks,
|
|
23442
|
+
concurrency: input.concurrency,
|
|
23443
|
+
allowDynamicTasks: input.allowDynamicTasks ?? true,
|
|
23444
|
+
status: "pending",
|
|
23445
|
+
createdAt: now,
|
|
23446
|
+
lastUpdatedAt: now,
|
|
23447
|
+
metadata: input.metadata
|
|
23448
|
+
};
|
|
23449
|
+
}
|
|
23450
|
+
function canTaskExecute(task, allTasks) {
|
|
23451
|
+
if (task.status !== "pending") {
|
|
23452
|
+
return false;
|
|
23453
|
+
}
|
|
23454
|
+
if (task.dependsOn.length > 0) {
|
|
23455
|
+
for (const depId of task.dependsOn) {
|
|
23456
|
+
const depTask = allTasks.find((t) => t.id === depId);
|
|
23457
|
+
if (!depTask || depTask.status !== "completed") {
|
|
23458
|
+
return false;
|
|
23459
|
+
}
|
|
23460
|
+
}
|
|
23461
|
+
}
|
|
23462
|
+
return true;
|
|
23463
|
+
}
|
|
23464
|
+
function getNextExecutableTasks(plan) {
|
|
23465
|
+
const executable = plan.tasks.filter((task) => canTaskExecute(task, plan.tasks));
|
|
23466
|
+
if (executable.length === 0) {
|
|
23467
|
+
return [];
|
|
23468
|
+
}
|
|
23469
|
+
if (!plan.concurrency) {
|
|
23470
|
+
return [executable[0]];
|
|
23471
|
+
}
|
|
23472
|
+
const runningCount = plan.tasks.filter((t) => t.status === "in_progress").length;
|
|
23473
|
+
const availableSlots = plan.concurrency.maxParallelTasks - runningCount;
|
|
23474
|
+
if (availableSlots <= 0) {
|
|
23475
|
+
return [];
|
|
23476
|
+
}
|
|
23477
|
+
const parallelTasks = executable.filter((task) => task.execution?.parallel === true);
|
|
23478
|
+
if (parallelTasks.length === 0) {
|
|
23479
|
+
return [executable[0]];
|
|
23480
|
+
}
|
|
23481
|
+
let sortedTasks = [...parallelTasks];
|
|
23482
|
+
if (plan.concurrency.strategy === "priority") {
|
|
23483
|
+
sortedTasks.sort((a, b) => (b.execution?.priority ?? 0) - (a.execution?.priority ?? 0));
|
|
23484
|
+
}
|
|
23485
|
+
return sortedTasks.slice(0, availableSlots);
|
|
23486
|
+
}
|
|
23487
|
+
async function evaluateCondition(condition, memory) {
|
|
23488
|
+
const value = await memory.get(condition.memoryKey);
|
|
23489
|
+
switch (condition.operator) {
|
|
23490
|
+
case "exists":
|
|
23491
|
+
return value !== void 0;
|
|
23492
|
+
case "not_exists":
|
|
23493
|
+
return value === void 0;
|
|
23494
|
+
case "equals":
|
|
23495
|
+
return value === condition.value;
|
|
23496
|
+
case "contains":
|
|
23497
|
+
if (Array.isArray(value)) {
|
|
23498
|
+
return value.includes(condition.value);
|
|
23499
|
+
}
|
|
23500
|
+
if (typeof value === "string" && typeof condition.value === "string") {
|
|
23501
|
+
return value.includes(condition.value);
|
|
23502
|
+
}
|
|
23503
|
+
return false;
|
|
23504
|
+
case "truthy":
|
|
23505
|
+
return !!value;
|
|
23506
|
+
case "greater_than":
|
|
23507
|
+
if (typeof value === "number" && typeof condition.value === "number") {
|
|
23508
|
+
return value > condition.value;
|
|
23509
|
+
}
|
|
23510
|
+
return false;
|
|
23511
|
+
case "less_than":
|
|
23512
|
+
if (typeof value === "number" && typeof condition.value === "number") {
|
|
23513
|
+
return value < condition.value;
|
|
23514
|
+
}
|
|
23515
|
+
return false;
|
|
23516
|
+
default:
|
|
23517
|
+
return false;
|
|
23518
|
+
}
|
|
23519
|
+
}
|
|
23520
|
+
function updateTaskStatus(task, status) {
|
|
23521
|
+
const now = Date.now();
|
|
23522
|
+
const updated = {
|
|
23523
|
+
...task,
|
|
23524
|
+
status,
|
|
23525
|
+
lastUpdatedAt: now
|
|
23526
|
+
};
|
|
23527
|
+
if (status === "in_progress") {
|
|
23528
|
+
if (!updated.startedAt) {
|
|
23529
|
+
updated.startedAt = now;
|
|
23530
|
+
}
|
|
23531
|
+
updated.attempts += 1;
|
|
23532
|
+
}
|
|
23533
|
+
if ((status === "completed" || status === "failed") && !updated.completedAt) {
|
|
23534
|
+
updated.completedAt = now;
|
|
23535
|
+
}
|
|
23536
|
+
return updated;
|
|
23537
|
+
}
|
|
23538
|
+
function isTaskBlocked(task, allTasks) {
|
|
23539
|
+
if (task.dependsOn.length === 0) {
|
|
23540
|
+
return false;
|
|
23541
|
+
}
|
|
23542
|
+
for (const depId of task.dependsOn) {
|
|
23543
|
+
const depTask = allTasks.find((t) => t.id === depId);
|
|
23544
|
+
if (!depTask) {
|
|
23545
|
+
return true;
|
|
23546
|
+
}
|
|
23547
|
+
if (depTask.status !== "completed") {
|
|
23548
|
+
return true;
|
|
23549
|
+
}
|
|
23550
|
+
}
|
|
23551
|
+
return false;
|
|
23552
|
+
}
|
|
23553
|
+
function getTaskDependencies(task, allTasks) {
|
|
23554
|
+
if (task.dependsOn.length === 0) {
|
|
23555
|
+
return [];
|
|
23556
|
+
}
|
|
23557
|
+
return task.dependsOn.map((depId) => allTasks.find((t) => t.id === depId)).filter((t) => t !== void 0);
|
|
23558
|
+
}
|
|
23559
|
+
function resolveDependencies(taskInputs, tasks) {
|
|
23560
|
+
const nameToId = /* @__PURE__ */ new Map();
|
|
23561
|
+
for (const task of tasks) {
|
|
23562
|
+
nameToId.set(task.name, task.id);
|
|
23563
|
+
}
|
|
23564
|
+
for (const input of taskInputs) {
|
|
23565
|
+
if (input.dependsOn && input.dependsOn.length > 0) {
|
|
23566
|
+
input.dependsOn = input.dependsOn.map((dep) => {
|
|
23567
|
+
if (dep.startsWith("task-")) {
|
|
23568
|
+
return dep;
|
|
23569
|
+
}
|
|
23570
|
+
const resolvedId = nameToId.get(dep);
|
|
23571
|
+
if (!resolvedId) {
|
|
23572
|
+
throw new Error(`Task dependency "${dep}" not found`);
|
|
23573
|
+
}
|
|
23574
|
+
return resolvedId;
|
|
23575
|
+
});
|
|
23576
|
+
}
|
|
23577
|
+
}
|
|
23578
|
+
}
|
|
23579
|
+
function detectDependencyCycle(tasks) {
|
|
23580
|
+
const visited = /* @__PURE__ */ new Set();
|
|
23581
|
+
const recStack = /* @__PURE__ */ new Set();
|
|
23582
|
+
const taskMap = new Map(tasks.map((t) => [t.id, t]));
|
|
23583
|
+
function dfs(taskId, path6) {
|
|
23584
|
+
if (recStack.has(taskId)) {
|
|
23585
|
+
const cycleStart = path6.indexOf(taskId);
|
|
23586
|
+
return [...path6.slice(cycleStart), taskId];
|
|
23587
|
+
}
|
|
23588
|
+
if (visited.has(taskId)) {
|
|
23589
|
+
return null;
|
|
23590
|
+
}
|
|
23591
|
+
visited.add(taskId);
|
|
23592
|
+
recStack.add(taskId);
|
|
23593
|
+
const task = taskMap.get(taskId);
|
|
23594
|
+
if (task) {
|
|
23595
|
+
for (const depId of task.dependsOn) {
|
|
23596
|
+
const cycle = dfs(depId, [...path6, taskId]);
|
|
23597
|
+
if (cycle) {
|
|
23598
|
+
return cycle;
|
|
23599
|
+
}
|
|
23600
|
+
}
|
|
23601
|
+
}
|
|
23602
|
+
recStack.delete(taskId);
|
|
23603
|
+
return null;
|
|
23604
|
+
}
|
|
23605
|
+
for (const task of tasks) {
|
|
23606
|
+
if (!visited.has(task.id)) {
|
|
23607
|
+
const cycle = dfs(task.id, []);
|
|
23608
|
+
if (cycle) {
|
|
23609
|
+
return cycle;
|
|
23610
|
+
}
|
|
23611
|
+
}
|
|
23612
|
+
}
|
|
23613
|
+
return null;
|
|
23614
|
+
}
|
|
23615
|
+
|
|
23616
|
+
// src/domain/entities/Routine.ts
|
|
23617
|
+
function createRoutineDefinition(input) {
|
|
23618
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23619
|
+
const id = input.id ?? `routine-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
23620
|
+
const taskNames = new Set(input.tasks.map((t) => t.name));
|
|
23621
|
+
const taskIds = new Set(input.tasks.filter((t) => t.id).map((t) => t.id));
|
|
23622
|
+
for (const task of input.tasks) {
|
|
23623
|
+
if (task.dependsOn) {
|
|
23624
|
+
for (const dep of task.dependsOn) {
|
|
23625
|
+
if (!taskNames.has(dep) && !taskIds.has(dep)) {
|
|
23626
|
+
throw new Error(
|
|
23627
|
+
`Routine "${input.name}": task "${task.name}" depends on unknown task "${dep}"`
|
|
23628
|
+
);
|
|
23629
|
+
}
|
|
23630
|
+
}
|
|
23631
|
+
}
|
|
23632
|
+
}
|
|
23633
|
+
createPlan({
|
|
23634
|
+
goal: input.name,
|
|
23635
|
+
tasks: input.tasks
|
|
23636
|
+
});
|
|
23637
|
+
return {
|
|
23638
|
+
id,
|
|
23639
|
+
name: input.name,
|
|
23640
|
+
description: input.description,
|
|
23641
|
+
version: input.version,
|
|
23642
|
+
tasks: input.tasks,
|
|
23643
|
+
requiredTools: input.requiredTools,
|
|
23644
|
+
requiredPlugins: input.requiredPlugins,
|
|
23645
|
+
instructions: input.instructions,
|
|
23646
|
+
concurrency: input.concurrency,
|
|
23647
|
+
allowDynamicTasks: input.allowDynamicTasks ?? false,
|
|
23648
|
+
tags: input.tags,
|
|
23649
|
+
author: input.author,
|
|
23650
|
+
createdAt: now,
|
|
23651
|
+
updatedAt: now,
|
|
23652
|
+
metadata: input.metadata
|
|
23653
|
+
};
|
|
23654
|
+
}
|
|
23655
|
+
function createRoutineExecution(definition) {
|
|
23656
|
+
const now = Date.now();
|
|
23657
|
+
const executionId = `rexec-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
23658
|
+
const plan = createPlan({
|
|
23659
|
+
goal: definition.name,
|
|
23660
|
+
context: definition.description,
|
|
23661
|
+
tasks: definition.tasks,
|
|
23662
|
+
concurrency: definition.concurrency,
|
|
23663
|
+
allowDynamicTasks: definition.allowDynamicTasks
|
|
23664
|
+
});
|
|
23665
|
+
return {
|
|
23666
|
+
id: executionId,
|
|
23667
|
+
routineId: definition.id,
|
|
23668
|
+
plan,
|
|
23669
|
+
status: "pending",
|
|
23670
|
+
progress: 0,
|
|
23671
|
+
lastUpdatedAt: now
|
|
23672
|
+
};
|
|
23673
|
+
}
|
|
23674
|
+
function getRoutineProgress(execution) {
|
|
23675
|
+
const { tasks } = execution.plan;
|
|
23676
|
+
if (tasks.length === 0) return 100;
|
|
23677
|
+
const completed = tasks.filter((t) => isTerminalStatus(t.status)).length;
|
|
23678
|
+
return Math.round(completed / tasks.length * 100);
|
|
23679
|
+
}
|
|
23680
|
+
|
|
23681
|
+
// src/utils/jsonExtractor.ts
|
|
23682
|
+
function extractJSON(text) {
|
|
23683
|
+
if (!text || typeof text !== "string") {
|
|
23684
|
+
return {
|
|
23685
|
+
success: false,
|
|
23686
|
+
error: "Input is empty or not a string"
|
|
23687
|
+
};
|
|
23688
|
+
}
|
|
23689
|
+
const trimmedText = text.trim();
|
|
23690
|
+
const codeBlockResult = extractFromCodeBlock(trimmedText);
|
|
23691
|
+
if (codeBlockResult.success) {
|
|
23692
|
+
return codeBlockResult;
|
|
23693
|
+
}
|
|
23694
|
+
const inlineResult = extractInlineJSON(trimmedText);
|
|
23695
|
+
if (inlineResult.success) {
|
|
23696
|
+
return inlineResult;
|
|
23697
|
+
}
|
|
23698
|
+
try {
|
|
23699
|
+
const data = JSON.parse(trimmedText);
|
|
23700
|
+
return {
|
|
23701
|
+
success: true,
|
|
23702
|
+
data,
|
|
23703
|
+
rawJson: trimmedText,
|
|
23704
|
+
method: "raw"
|
|
23705
|
+
};
|
|
23706
|
+
} catch (e) {
|
|
23707
|
+
return {
|
|
23708
|
+
success: false,
|
|
23709
|
+
error: `Could not extract JSON from text: ${e instanceof Error ? e.message : String(e)}`
|
|
23710
|
+
};
|
|
23711
|
+
}
|
|
23712
|
+
}
|
|
23713
|
+
function extractFromCodeBlock(text) {
|
|
23714
|
+
const codeBlockRegex = /```(?:json)?\s*([\s\S]*?)```/g;
|
|
23715
|
+
let match;
|
|
23716
|
+
while ((match = codeBlockRegex.exec(text)) !== null) {
|
|
23717
|
+
const content = match[1];
|
|
23718
|
+
if (content) {
|
|
23719
|
+
const trimmed = content.trim();
|
|
23720
|
+
try {
|
|
23721
|
+
const data = JSON.parse(trimmed);
|
|
23722
|
+
return {
|
|
23723
|
+
success: true,
|
|
23724
|
+
data,
|
|
23725
|
+
rawJson: trimmed,
|
|
23726
|
+
method: "code_block"
|
|
23727
|
+
};
|
|
23728
|
+
} catch {
|
|
23729
|
+
continue;
|
|
23730
|
+
}
|
|
23731
|
+
}
|
|
23732
|
+
}
|
|
23733
|
+
return { success: false };
|
|
23734
|
+
}
|
|
23735
|
+
function extractInlineJSON(text) {
|
|
23736
|
+
const objectMatch = findJSONObject(text);
|
|
23737
|
+
if (objectMatch) {
|
|
23738
|
+
try {
|
|
23739
|
+
const data = JSON.parse(objectMatch);
|
|
23740
|
+
return {
|
|
23741
|
+
success: true,
|
|
23742
|
+
data,
|
|
23743
|
+
rawJson: objectMatch,
|
|
23744
|
+
method: "inline"
|
|
23745
|
+
};
|
|
23746
|
+
} catch {
|
|
23747
|
+
}
|
|
23748
|
+
}
|
|
23749
|
+
const arrayMatch = findJSONArray(text);
|
|
23750
|
+
if (arrayMatch) {
|
|
23751
|
+
try {
|
|
23752
|
+
const data = JSON.parse(arrayMatch);
|
|
23753
|
+
return {
|
|
23754
|
+
success: true,
|
|
23755
|
+
data,
|
|
23756
|
+
rawJson: arrayMatch,
|
|
23757
|
+
method: "inline"
|
|
23758
|
+
};
|
|
23759
|
+
} catch {
|
|
23760
|
+
}
|
|
23761
|
+
}
|
|
23762
|
+
return { success: false };
|
|
23763
|
+
}
|
|
23764
|
+
function findJSONObject(text) {
|
|
23765
|
+
const startIndex = text.indexOf("{");
|
|
23766
|
+
if (startIndex === -1) return null;
|
|
23767
|
+
let depth = 0;
|
|
23768
|
+
let inString = false;
|
|
23769
|
+
let escaped = false;
|
|
23770
|
+
for (let i = startIndex; i < text.length; i++) {
|
|
23771
|
+
const char = text[i];
|
|
23772
|
+
if (escaped) {
|
|
23773
|
+
escaped = false;
|
|
23774
|
+
continue;
|
|
23775
|
+
}
|
|
23776
|
+
if (char === "\\" && inString) {
|
|
23777
|
+
escaped = true;
|
|
23778
|
+
continue;
|
|
23779
|
+
}
|
|
23780
|
+
if (char === '"') {
|
|
23781
|
+
inString = !inString;
|
|
23782
|
+
continue;
|
|
23783
|
+
}
|
|
23784
|
+
if (inString) continue;
|
|
23785
|
+
if (char === "{") {
|
|
23786
|
+
depth++;
|
|
23787
|
+
} else if (char === "}") {
|
|
23788
|
+
depth--;
|
|
23789
|
+
if (depth === 0) {
|
|
23790
|
+
return text.slice(startIndex, i + 1);
|
|
23791
|
+
}
|
|
23792
|
+
}
|
|
23793
|
+
}
|
|
23794
|
+
return null;
|
|
23795
|
+
}
|
|
23796
|
+
function findJSONArray(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 extractJSONField(text, field, defaultValue) {
|
|
23829
|
+
const result = extractJSON(text);
|
|
23830
|
+
if (result.success && result.data && field in result.data) {
|
|
23831
|
+
return result.data[field];
|
|
23832
|
+
}
|
|
23833
|
+
return defaultValue;
|
|
23834
|
+
}
|
|
23835
|
+
function extractNumber(text, patterns = [
|
|
23836
|
+
/(\d{1,3})%?\s*(?:complete|score|percent)/i,
|
|
23837
|
+
/(?:score|completion|rating)[:\s]+(\d{1,3})/i,
|
|
23838
|
+
/(\d{1,3})\s*(?:out of|\/)\s*100/i
|
|
23839
|
+
], defaultValue = 0) {
|
|
23840
|
+
const jsonResult = extractJSON(text);
|
|
23841
|
+
if (jsonResult.success && jsonResult.data) {
|
|
23842
|
+
const scoreFields = ["score", "completionScore", "completion_score", "rating", "percent", "value"];
|
|
23843
|
+
for (const field of scoreFields) {
|
|
23844
|
+
if (field in jsonResult.data && typeof jsonResult.data[field] === "number") {
|
|
23845
|
+
return jsonResult.data[field];
|
|
23846
|
+
}
|
|
23847
|
+
}
|
|
23848
|
+
}
|
|
23849
|
+
for (const pattern of patterns) {
|
|
23850
|
+
const match = text.match(pattern);
|
|
23851
|
+
if (match && match[1]) {
|
|
23852
|
+
const num = parseInt(match[1], 10);
|
|
23853
|
+
if (!isNaN(num)) {
|
|
23854
|
+
return num;
|
|
23855
|
+
}
|
|
23856
|
+
}
|
|
23857
|
+
}
|
|
23858
|
+
return defaultValue;
|
|
23859
|
+
}
|
|
23860
|
+
|
|
23861
|
+
// src/core/routineRunner.ts
|
|
23862
|
+
init_Logger();
|
|
23863
|
+
function defaultSystemPrompt(definition) {
|
|
23864
|
+
const parts = [];
|
|
23865
|
+
if (definition.instructions) {
|
|
23866
|
+
parts.push(definition.instructions);
|
|
23867
|
+
}
|
|
23868
|
+
parts.push(
|
|
23869
|
+
`You are executing a routine called "${definition.name}".`,
|
|
23870
|
+
"",
|
|
23871
|
+
"Between tasks, your conversation history is cleared but your memory persists.",
|
|
23872
|
+
"Use these strategies to pass information between tasks:",
|
|
23873
|
+
"- Use context_set for small key results that subsequent tasks need immediately (visible in context, no retrieval needed).",
|
|
23874
|
+
'- Use memory_store with tier="findings" for larger data that may be needed later.',
|
|
23875
|
+
"- Use memory_retrieve to access data stored by previous tasks.",
|
|
23876
|
+
"",
|
|
23877
|
+
"IMPORTANT: When you have completed the current task, you MUST stop immediately.",
|
|
23878
|
+
"Do NOT repeat work you have already done. Do NOT re-fetch data you already have.",
|
|
23879
|
+
"Store key results in memory once, then produce a final text response (no more tool calls) to signal completion."
|
|
23880
|
+
);
|
|
23881
|
+
return parts.join("\n");
|
|
23882
|
+
}
|
|
23883
|
+
function defaultTaskPrompt(task) {
|
|
23884
|
+
const parts = [];
|
|
23885
|
+
parts.push(`## Current Task: ${task.name}`, "");
|
|
23886
|
+
parts.push(task.description, "");
|
|
23887
|
+
if (task.expectedOutput) {
|
|
23888
|
+
parts.push(`**Expected output:** ${task.expectedOutput}`, "");
|
|
23889
|
+
}
|
|
23890
|
+
if (task.suggestedTools && task.suggestedTools.length > 0) {
|
|
23891
|
+
parts.push(`**Suggested tools:** ${task.suggestedTools.join(", ")}`, "");
|
|
23892
|
+
}
|
|
23893
|
+
const criteria = task.validation?.completionCriteria;
|
|
23894
|
+
if (criteria && criteria.length > 0) {
|
|
23895
|
+
parts.push("### Completion Criteria");
|
|
23896
|
+
parts.push("When you are done, ensure the following are met:");
|
|
23897
|
+
for (const c of criteria) {
|
|
23898
|
+
parts.push(`- ${c}`);
|
|
23899
|
+
}
|
|
23900
|
+
parts.push("");
|
|
23901
|
+
}
|
|
23902
|
+
parts.push("After completing the work, store key results in memory once, then respond with a text summary (no more tool calls).");
|
|
23903
|
+
return parts.join("\n");
|
|
23904
|
+
}
|
|
23905
|
+
function defaultValidationPrompt(task, context) {
|
|
23906
|
+
const criteria = task.validation?.completionCriteria ?? [];
|
|
23907
|
+
const criteriaList = criteria.length > 0 ? criteria.map((c, i) => `${i + 1}. ${c}`).join("\n") : "The task was completed as described.";
|
|
23908
|
+
const parts = [
|
|
23909
|
+
`Evaluate if the task "${task.name}" was completed successfully.`,
|
|
23910
|
+
"",
|
|
23911
|
+
`Task description: ${task.description}`,
|
|
23912
|
+
"",
|
|
23913
|
+
"Completion criteria:",
|
|
23914
|
+
criteriaList,
|
|
23915
|
+
"",
|
|
23916
|
+
"--- EVIDENCE ---",
|
|
23917
|
+
"",
|
|
23918
|
+
"Agent response (final text output):",
|
|
23919
|
+
context.responseText || "(no text output)",
|
|
23920
|
+
"",
|
|
23921
|
+
"Tool calls made during this task:",
|
|
23922
|
+
context.toolCallLog
|
|
23923
|
+
];
|
|
23924
|
+
if (context.inContextMemory) {
|
|
23925
|
+
parts.push("", "In-context memory (current state):", context.inContextMemory);
|
|
23926
|
+
}
|
|
23927
|
+
if (context.workingMemoryIndex) {
|
|
23928
|
+
parts.push("", "Working memory index (stored data):", context.workingMemoryIndex);
|
|
23929
|
+
}
|
|
23930
|
+
parts.push(
|
|
23931
|
+
"",
|
|
23932
|
+
"--- END EVIDENCE ---",
|
|
23933
|
+
"",
|
|
23934
|
+
"Use the evidence above to verify each criterion. Check tool call results, not just the agent's claims.",
|
|
23935
|
+
"",
|
|
23936
|
+
"Return a JSON object with the following structure:",
|
|
23937
|
+
"```json",
|
|
23938
|
+
'{ "isComplete": boolean, "completionScore": number (0-100), "explanation": "..." }',
|
|
23939
|
+
"```",
|
|
23940
|
+
"",
|
|
23941
|
+
"Be strict: only mark isComplete=true if all criteria are clearly met based on the evidence."
|
|
23942
|
+
);
|
|
23943
|
+
return parts.join("\n");
|
|
23944
|
+
}
|
|
23945
|
+
function formatToolCallLog(conversation) {
|
|
23946
|
+
const calls = [];
|
|
23947
|
+
for (const item of conversation) {
|
|
23948
|
+
if (!("content" in item) || !Array.isArray(item.content)) continue;
|
|
23949
|
+
const msg = item;
|
|
23950
|
+
for (const c of msg.content) {
|
|
23951
|
+
if (c.type === "tool_use" /* TOOL_USE */) {
|
|
23952
|
+
let argsStr;
|
|
23953
|
+
try {
|
|
23954
|
+
const parsed = JSON.parse(c.arguments);
|
|
23955
|
+
argsStr = JSON.stringify(parsed, null, 2);
|
|
23956
|
+
if (argsStr.length > 500) argsStr = argsStr.slice(0, 500) + "... (truncated)";
|
|
23957
|
+
} catch {
|
|
23958
|
+
argsStr = c.arguments;
|
|
23959
|
+
}
|
|
23960
|
+
calls.push(`CALL: ${c.name}(${argsStr})`);
|
|
23961
|
+
} else if (c.type === "tool_result" /* TOOL_RESULT */) {
|
|
23962
|
+
let resultStr = typeof c.content === "string" ? c.content : JSON.stringify(c.content);
|
|
23963
|
+
if (resultStr.length > 500) resultStr = resultStr.slice(0, 500) + "... (truncated)";
|
|
23964
|
+
const prefix = c.error ? "ERROR" : "RESULT";
|
|
23965
|
+
calls.push(` ${prefix}: ${resultStr}`);
|
|
23966
|
+
}
|
|
23967
|
+
}
|
|
23968
|
+
}
|
|
23969
|
+
return calls.length > 0 ? calls.join("\n") : "(no tool calls)";
|
|
23970
|
+
}
|
|
23971
|
+
async function collectValidationContext(agent, responseText) {
|
|
23972
|
+
const icmPlugin = agent.context.getPlugin("in_context_memory");
|
|
23973
|
+
const inContextMemory = icmPlugin ? await icmPlugin.getContent() : null;
|
|
23974
|
+
const wmPlugin = agent.context.memory;
|
|
23975
|
+
const workingMemoryIndex = wmPlugin ? await wmPlugin.getContent() : null;
|
|
23976
|
+
const conversation = agent.context.getConversation();
|
|
23977
|
+
const toolCallLog = formatToolCallLog(conversation);
|
|
23978
|
+
return {
|
|
23979
|
+
responseText,
|
|
23980
|
+
inContextMemory,
|
|
23981
|
+
workingMemoryIndex,
|
|
23982
|
+
toolCallLog
|
|
23983
|
+
};
|
|
23984
|
+
}
|
|
23985
|
+
async function validateTaskCompletion(agent, task, responseText, validationPromptBuilder) {
|
|
23986
|
+
const hasExplicitValidation = task.validation?.skipReflection === false && task.validation?.completionCriteria && task.validation.completionCriteria.length > 0;
|
|
23987
|
+
if (!hasExplicitValidation) {
|
|
23988
|
+
return {
|
|
23989
|
+
isComplete: true,
|
|
23990
|
+
completionScore: 100,
|
|
23991
|
+
explanation: "Auto-passed (LLM validation not enabled)",
|
|
23992
|
+
requiresUserApproval: false
|
|
23993
|
+
};
|
|
23994
|
+
}
|
|
23995
|
+
const validationContext = await collectValidationContext(agent, responseText);
|
|
23996
|
+
const prompt = validationPromptBuilder(task, validationContext);
|
|
23997
|
+
const response = await agent.runDirect(prompt, {
|
|
23998
|
+
instructions: "You are a task completion evaluator. Return only JSON.",
|
|
23999
|
+
temperature: 0.1
|
|
24000
|
+
});
|
|
24001
|
+
const text = response.output_text ?? "";
|
|
24002
|
+
const extracted = extractJSON(text);
|
|
24003
|
+
if (!extracted.success || !extracted.data) {
|
|
24004
|
+
return {
|
|
24005
|
+
isComplete: false,
|
|
24006
|
+
completionScore: 0,
|
|
24007
|
+
explanation: `Failed to parse validation response: ${extracted.error ?? "unknown error"}`,
|
|
24008
|
+
requiresUserApproval: false
|
|
24009
|
+
};
|
|
24010
|
+
}
|
|
24011
|
+
const { isComplete, completionScore, explanation } = extracted.data;
|
|
24012
|
+
const minScore = task.validation?.minCompletionScore ?? 80;
|
|
24013
|
+
return {
|
|
24014
|
+
isComplete: isComplete && completionScore >= minScore,
|
|
24015
|
+
completionScore,
|
|
24016
|
+
explanation,
|
|
24017
|
+
requiresUserApproval: false
|
|
24018
|
+
};
|
|
24019
|
+
}
|
|
24020
|
+
async function executeRoutine(options) {
|
|
24021
|
+
const {
|
|
24022
|
+
definition,
|
|
24023
|
+
agent: existingAgent,
|
|
24024
|
+
connector,
|
|
24025
|
+
model,
|
|
24026
|
+
tools: extraTools,
|
|
24027
|
+
onTaskStarted,
|
|
24028
|
+
onTaskComplete,
|
|
24029
|
+
onTaskFailed,
|
|
24030
|
+
onTaskValidation,
|
|
24031
|
+
hooks,
|
|
24032
|
+
prompts
|
|
24033
|
+
} = options;
|
|
24034
|
+
if (!existingAgent && (!connector || !model)) {
|
|
24035
|
+
throw new Error("executeRoutine requires either `agent` or both `connector` and `model`");
|
|
24036
|
+
}
|
|
24037
|
+
const ownsAgent = !existingAgent;
|
|
24038
|
+
const log = logger.child({ routine: definition.name });
|
|
24039
|
+
const execution = createRoutineExecution(definition);
|
|
24040
|
+
execution.status = "running";
|
|
24041
|
+
execution.startedAt = Date.now();
|
|
24042
|
+
execution.lastUpdatedAt = Date.now();
|
|
24043
|
+
const buildSystemPrompt = prompts?.system ?? defaultSystemPrompt;
|
|
24044
|
+
const buildTaskPrompt = prompts?.task ?? defaultTaskPrompt;
|
|
24045
|
+
const buildValidationPrompt = prompts?.validation ?? defaultValidationPrompt;
|
|
24046
|
+
let agent;
|
|
24047
|
+
const registeredHooks = [];
|
|
24048
|
+
if (existingAgent) {
|
|
24049
|
+
agent = existingAgent;
|
|
24050
|
+
if (hooks) {
|
|
24051
|
+
const hookNames = [
|
|
24052
|
+
"before:execution",
|
|
24053
|
+
"after:execution",
|
|
24054
|
+
"before:llm",
|
|
24055
|
+
"after:llm",
|
|
24056
|
+
"before:tool",
|
|
24057
|
+
"after:tool",
|
|
24058
|
+
"approve:tool",
|
|
24059
|
+
"pause:check"
|
|
24060
|
+
];
|
|
24061
|
+
for (const name of hookNames) {
|
|
24062
|
+
const hook = hooks[name];
|
|
24063
|
+
if (hook) {
|
|
24064
|
+
agent.registerHook(name, hook);
|
|
24065
|
+
registeredHooks.push({ name, hook });
|
|
24066
|
+
}
|
|
24067
|
+
}
|
|
24068
|
+
}
|
|
24069
|
+
} else {
|
|
24070
|
+
const allTools = [...extraTools ?? []];
|
|
24071
|
+
if (definition.requiredTools && definition.requiredTools.length > 0) {
|
|
24072
|
+
const availableToolNames = new Set(allTools.map((t) => t.definition.function.name));
|
|
24073
|
+
const missing = definition.requiredTools.filter((name) => !availableToolNames.has(name));
|
|
24074
|
+
if (missing.length > 0) {
|
|
24075
|
+
execution.status = "failed";
|
|
24076
|
+
execution.error = `Missing required tools: ${missing.join(", ")}`;
|
|
24077
|
+
execution.completedAt = Date.now();
|
|
24078
|
+
execution.lastUpdatedAt = Date.now();
|
|
24079
|
+
return execution;
|
|
24080
|
+
}
|
|
24081
|
+
}
|
|
24082
|
+
agent = Agent.create({
|
|
24083
|
+
connector,
|
|
24084
|
+
model,
|
|
24085
|
+
tools: allTools,
|
|
24086
|
+
instructions: buildSystemPrompt(definition),
|
|
24087
|
+
hooks,
|
|
24088
|
+
context: {
|
|
24089
|
+
model,
|
|
24090
|
+
features: {
|
|
24091
|
+
workingMemory: true,
|
|
24092
|
+
inContextMemory: true
|
|
24093
|
+
}
|
|
24094
|
+
}
|
|
24095
|
+
});
|
|
24096
|
+
}
|
|
24097
|
+
if (definition.requiredPlugins && definition.requiredPlugins.length > 0) {
|
|
24098
|
+
const missing = definition.requiredPlugins.filter(
|
|
24099
|
+
(name) => !agent.context.hasPlugin(name)
|
|
24100
|
+
);
|
|
24101
|
+
if (missing.length > 0) {
|
|
24102
|
+
if (ownsAgent) agent.destroy();
|
|
24103
|
+
execution.status = "failed";
|
|
24104
|
+
execution.error = `Missing required plugins: ${missing.join(", ")}`;
|
|
24105
|
+
execution.completedAt = Date.now();
|
|
24106
|
+
execution.lastUpdatedAt = Date.now();
|
|
24107
|
+
return execution;
|
|
24108
|
+
}
|
|
24109
|
+
}
|
|
24110
|
+
const failureMode = definition.concurrency?.failureMode ?? "fail-fast";
|
|
24111
|
+
try {
|
|
24112
|
+
let nextTasks = getNextExecutableTasks(execution.plan);
|
|
24113
|
+
while (nextTasks.length > 0) {
|
|
24114
|
+
const task = nextTasks[0];
|
|
24115
|
+
const taskIndex = execution.plan.tasks.findIndex((t) => t.id === task.id);
|
|
24116
|
+
log.info({ taskName: task.name, taskId: task.id }, "Starting task");
|
|
24117
|
+
execution.plan.tasks[taskIndex] = updateTaskStatus(task, "in_progress");
|
|
24118
|
+
execution.lastUpdatedAt = Date.now();
|
|
24119
|
+
onTaskStarted?.(execution.plan.tasks[taskIndex], execution);
|
|
24120
|
+
let taskCompleted = false;
|
|
24121
|
+
const maxTaskIterations = task.execution?.maxIterations ?? 15;
|
|
24122
|
+
const iterationLimiter = async (ctx) => {
|
|
24123
|
+
if (ctx.iteration >= maxTaskIterations) {
|
|
24124
|
+
agent.cancel(`Task "${task.name}" exceeded max iterations (${maxTaskIterations})`);
|
|
24125
|
+
}
|
|
24126
|
+
return { shouldPause: false };
|
|
24127
|
+
};
|
|
24128
|
+
agent.registerHook("pause:check", iterationLimiter);
|
|
24129
|
+
const getTask = () => execution.plan.tasks[taskIndex];
|
|
24130
|
+
while (!taskCompleted) {
|
|
24131
|
+
try {
|
|
24132
|
+
const taskPrompt = buildTaskPrompt(getTask());
|
|
24133
|
+
const response = await agent.run(taskPrompt);
|
|
24134
|
+
const responseText = response.output_text ?? "";
|
|
24135
|
+
const validationResult = await validateTaskCompletion(
|
|
24136
|
+
agent,
|
|
24137
|
+
getTask(),
|
|
24138
|
+
responseText,
|
|
24139
|
+
buildValidationPrompt
|
|
24140
|
+
);
|
|
24141
|
+
onTaskValidation?.(getTask(), validationResult, execution);
|
|
24142
|
+
if (validationResult.isComplete) {
|
|
24143
|
+
execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "completed");
|
|
24144
|
+
execution.plan.tasks[taskIndex].result = {
|
|
24145
|
+
success: true,
|
|
24146
|
+
output: responseText,
|
|
24147
|
+
validationScore: validationResult.completionScore,
|
|
24148
|
+
validationExplanation: validationResult.explanation
|
|
24149
|
+
};
|
|
24150
|
+
taskCompleted = true;
|
|
24151
|
+
log.info(
|
|
24152
|
+
{ taskName: getTask().name, score: validationResult.completionScore },
|
|
24153
|
+
"Task completed"
|
|
24154
|
+
);
|
|
24155
|
+
execution.progress = getRoutineProgress(execution);
|
|
24156
|
+
execution.lastUpdatedAt = Date.now();
|
|
24157
|
+
onTaskComplete?.(execution.plan.tasks[taskIndex], execution);
|
|
24158
|
+
} else {
|
|
24159
|
+
log.warn(
|
|
24160
|
+
{
|
|
24161
|
+
taskName: getTask().name,
|
|
24162
|
+
score: validationResult.completionScore,
|
|
24163
|
+
attempt: getTask().attempts,
|
|
24164
|
+
maxAttempts: getTask().maxAttempts
|
|
24165
|
+
},
|
|
24166
|
+
"Task validation failed"
|
|
24167
|
+
);
|
|
24168
|
+
if (getTask().attempts >= getTask().maxAttempts) {
|
|
24169
|
+
execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "failed");
|
|
24170
|
+
execution.plan.tasks[taskIndex].result = {
|
|
24171
|
+
success: false,
|
|
24172
|
+
error: validationResult.explanation,
|
|
24173
|
+
validationScore: validationResult.completionScore,
|
|
24174
|
+
validationExplanation: validationResult.explanation
|
|
24175
|
+
};
|
|
24176
|
+
break;
|
|
24177
|
+
}
|
|
24178
|
+
execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "in_progress");
|
|
24179
|
+
}
|
|
24180
|
+
} catch (error) {
|
|
24181
|
+
const errorMessage = error.message;
|
|
24182
|
+
log.error({ taskName: getTask().name, error: errorMessage }, "Task execution error");
|
|
24183
|
+
if (getTask().attempts >= getTask().maxAttempts) {
|
|
24184
|
+
execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "failed");
|
|
24185
|
+
execution.plan.tasks[taskIndex].result = {
|
|
24186
|
+
success: false,
|
|
24187
|
+
error: errorMessage
|
|
24188
|
+
};
|
|
24189
|
+
break;
|
|
24190
|
+
}
|
|
24191
|
+
execution.plan.tasks[taskIndex] = updateTaskStatus(getTask(), "in_progress");
|
|
24192
|
+
}
|
|
24193
|
+
}
|
|
24194
|
+
if (!taskCompleted) {
|
|
24195
|
+
execution.progress = getRoutineProgress(execution);
|
|
24196
|
+
execution.lastUpdatedAt = Date.now();
|
|
24197
|
+
onTaskFailed?.(execution.plan.tasks[taskIndex], execution);
|
|
24198
|
+
if (failureMode === "fail-fast") {
|
|
24199
|
+
execution.status = "failed";
|
|
24200
|
+
execution.error = `Task "${getTask().name}" failed after ${getTask().attempts} attempt(s)`;
|
|
24201
|
+
execution.completedAt = Date.now();
|
|
24202
|
+
execution.lastUpdatedAt = Date.now();
|
|
24203
|
+
break;
|
|
24204
|
+
}
|
|
24205
|
+
}
|
|
24206
|
+
agent.unregisterHook("pause:check", iterationLimiter);
|
|
24207
|
+
agent.clearConversation("task-boundary");
|
|
24208
|
+
nextTasks = getNextExecutableTasks(execution.plan);
|
|
24209
|
+
}
|
|
24210
|
+
if (execution.status === "running") {
|
|
24211
|
+
const allTerminal = execution.plan.tasks.every((t) => isTerminalStatus(t.status));
|
|
24212
|
+
const allCompleted = execution.plan.tasks.every((t) => t.status === "completed");
|
|
24213
|
+
if (allCompleted) {
|
|
24214
|
+
execution.status = "completed";
|
|
24215
|
+
} else if (allTerminal) {
|
|
24216
|
+
execution.status = "failed";
|
|
24217
|
+
execution.error = "Not all tasks completed successfully";
|
|
24218
|
+
} else {
|
|
24219
|
+
execution.status = "failed";
|
|
24220
|
+
execution.error = "Execution stalled: remaining tasks are blocked by incomplete dependencies";
|
|
24221
|
+
}
|
|
24222
|
+
execution.completedAt = Date.now();
|
|
24223
|
+
execution.lastUpdatedAt = Date.now();
|
|
24224
|
+
execution.progress = getRoutineProgress(execution);
|
|
24225
|
+
}
|
|
24226
|
+
log.info(
|
|
24227
|
+
{ status: execution.status, progress: execution.progress },
|
|
24228
|
+
"Routine execution finished"
|
|
24229
|
+
);
|
|
24230
|
+
return execution;
|
|
24231
|
+
} finally {
|
|
24232
|
+
for (const { name, hook } of registeredHooks) {
|
|
24233
|
+
try {
|
|
24234
|
+
agent.unregisterHook(name, hook);
|
|
24235
|
+
} catch {
|
|
24236
|
+
}
|
|
24237
|
+
}
|
|
24238
|
+
if (ownsAgent) {
|
|
24239
|
+
agent.destroy();
|
|
24240
|
+
}
|
|
24241
|
+
}
|
|
24242
|
+
}
|
|
24243
|
+
|
|
22758
24244
|
// src/core/index.ts
|
|
22759
24245
|
init_constants();
|
|
22760
24246
|
(class {
|
|
@@ -22792,8 +24278,8 @@ init_constants();
|
|
|
22792
24278
|
throw new Error("Configuration file not found. Searched: " + this.DEFAULT_PATHS.join(", "));
|
|
22793
24279
|
}
|
|
22794
24280
|
try {
|
|
22795
|
-
const
|
|
22796
|
-
const content =
|
|
24281
|
+
const fs20 = __require("fs");
|
|
24282
|
+
const content = fs20.readFileSync(configPath, "utf-8");
|
|
22797
24283
|
let config = JSON.parse(content);
|
|
22798
24284
|
config = this.interpolateEnvVars(config);
|
|
22799
24285
|
this.validate(config);
|
|
@@ -22822,10 +24308,10 @@ init_constants();
|
|
|
22822
24308
|
* Find configuration file synchronously
|
|
22823
24309
|
*/
|
|
22824
24310
|
static findConfigSync() {
|
|
22825
|
-
const
|
|
24311
|
+
const fs20 = __require("fs");
|
|
22826
24312
|
for (const path6 of this.DEFAULT_PATHS) {
|
|
22827
24313
|
try {
|
|
22828
|
-
|
|
24314
|
+
fs20.accessSync(resolve(path6));
|
|
22829
24315
|
return resolve(path6);
|
|
22830
24316
|
} catch {
|
|
22831
24317
|
}
|
|
@@ -29004,7 +30490,7 @@ var OpenAISTTProvider = class extends BaseMediaProvider {
|
|
|
29004
30490
|
if (Buffer.isBuffer(audio)) {
|
|
29005
30491
|
return new File([new Uint8Array(audio)], "audio.wav", { type: "audio/wav" });
|
|
29006
30492
|
} else if (typeof audio === "string") {
|
|
29007
|
-
return
|
|
30493
|
+
return fs19.createReadStream(audio);
|
|
29008
30494
|
} else {
|
|
29009
30495
|
throw new Error("Invalid audio input: must be Buffer or file path");
|
|
29010
30496
|
}
|
|
@@ -29557,7 +31043,7 @@ var TextToSpeech = class _TextToSpeech {
|
|
|
29557
31043
|
*/
|
|
29558
31044
|
async toFile(text, filePath, options) {
|
|
29559
31045
|
const response = await this.synthesize(text, options);
|
|
29560
|
-
await
|
|
31046
|
+
await fs18.writeFile(filePath, response.audio);
|
|
29561
31047
|
}
|
|
29562
31048
|
// ======================== Introspection Methods ========================
|
|
29563
31049
|
/**
|
|
@@ -29905,7 +31391,7 @@ var SpeechToText = class _SpeechToText {
|
|
|
29905
31391
|
* @param options - Optional transcription parameters
|
|
29906
31392
|
*/
|
|
29907
31393
|
async transcribeFile(filePath, options) {
|
|
29908
|
-
const audio = await
|
|
31394
|
+
const audio = await fs18.readFile(filePath);
|
|
29909
31395
|
return this.transcribe(audio, options);
|
|
29910
31396
|
}
|
|
29911
31397
|
/**
|
|
@@ -30231,7 +31717,7 @@ var OpenAIImageProvider = class extends BaseMediaProvider {
|
|
|
30231
31717
|
if (Buffer.isBuffer(image)) {
|
|
30232
31718
|
return new File([new Uint8Array(image)], "image.png", { type: "image/png" });
|
|
30233
31719
|
}
|
|
30234
|
-
return
|
|
31720
|
+
return fs19.createReadStream(image);
|
|
30235
31721
|
}
|
|
30236
31722
|
/**
|
|
30237
31723
|
* Handle OpenAI API errors
|
|
@@ -30378,8 +31864,8 @@ var GoogleImageProvider = class extends BaseMediaProvider {
|
|
|
30378
31864
|
if (Buffer.isBuffer(image)) {
|
|
30379
31865
|
imageBytes = image.toString("base64");
|
|
30380
31866
|
} else {
|
|
30381
|
-
const
|
|
30382
|
-
const buffer =
|
|
31867
|
+
const fs20 = await import('fs');
|
|
31868
|
+
const buffer = fs20.readFileSync(image);
|
|
30383
31869
|
imageBytes = buffer.toString("base64");
|
|
30384
31870
|
}
|
|
30385
31871
|
return {
|
|
@@ -30540,7 +32026,7 @@ var GrokImageProvider = class extends BaseMediaProvider {
|
|
|
30540
32026
|
if (Buffer.isBuffer(image)) {
|
|
30541
32027
|
return new File([new Uint8Array(image)], "image.png", { type: "image/png" });
|
|
30542
32028
|
}
|
|
30543
|
-
return
|
|
32029
|
+
return fs19.createReadStream(image);
|
|
30544
32030
|
}
|
|
30545
32031
|
/**
|
|
30546
32032
|
* Handle API errors
|
|
@@ -31990,8 +33476,8 @@ var OpenAISoraProvider = class extends BaseMediaProvider {
|
|
|
31990
33476
|
return new File([new Uint8Array(image)], "input.png", { type: "image/png" });
|
|
31991
33477
|
}
|
|
31992
33478
|
if (!image.startsWith("http")) {
|
|
31993
|
-
const
|
|
31994
|
-
const data =
|
|
33479
|
+
const fs20 = await import('fs');
|
|
33480
|
+
const data = fs20.readFileSync(image);
|
|
31995
33481
|
return new File([new Uint8Array(data)], "input.png", { type: "image/png" });
|
|
31996
33482
|
}
|
|
31997
33483
|
const response = await fetch(image);
|
|
@@ -32169,7 +33655,7 @@ var GoogleVeoProvider = class extends BaseMediaProvider {
|
|
|
32169
33655
|
if (video.videoBytes) {
|
|
32170
33656
|
buffer = Buffer.from(video.videoBytes, "base64");
|
|
32171
33657
|
} else if (video.uri) {
|
|
32172
|
-
const
|
|
33658
|
+
const fs20 = await import('fs/promises');
|
|
32173
33659
|
const os3 = await import('os');
|
|
32174
33660
|
const path6 = await import('path');
|
|
32175
33661
|
const tempDir = os3.tmpdir();
|
|
@@ -32180,11 +33666,11 @@ var GoogleVeoProvider = class extends BaseMediaProvider {
|
|
|
32180
33666
|
// Pass as GeneratedVideo
|
|
32181
33667
|
downloadPath: tempFile
|
|
32182
33668
|
});
|
|
32183
|
-
buffer = await
|
|
32184
|
-
await
|
|
33669
|
+
buffer = await fs20.readFile(tempFile);
|
|
33670
|
+
await fs20.unlink(tempFile).catch(() => {
|
|
32185
33671
|
});
|
|
32186
33672
|
} catch (downloadError) {
|
|
32187
|
-
await
|
|
33673
|
+
await fs20.unlink(tempFile).catch(() => {
|
|
32188
33674
|
});
|
|
32189
33675
|
throw new ProviderError(
|
|
32190
33676
|
"google",
|
|
@@ -32306,8 +33792,8 @@ var GoogleVeoProvider = class extends BaseMediaProvider {
|
|
|
32306
33792
|
if (image.startsWith("http://") || image.startsWith("https://")) {
|
|
32307
33793
|
return { imageUri: image };
|
|
32308
33794
|
}
|
|
32309
|
-
const
|
|
32310
|
-
const data = await
|
|
33795
|
+
const fs20 = await import('fs/promises');
|
|
33796
|
+
const data = await fs20.readFile(image);
|
|
32311
33797
|
return {
|
|
32312
33798
|
imageBytes: data.toString("base64")
|
|
32313
33799
|
};
|
|
@@ -32614,8 +34100,8 @@ var GrokImagineProvider = class extends BaseMediaProvider {
|
|
|
32614
34100
|
if (image.startsWith("http") || image.startsWith("data:")) {
|
|
32615
34101
|
return image;
|
|
32616
34102
|
}
|
|
32617
|
-
const
|
|
32618
|
-
const data =
|
|
34103
|
+
const fs20 = await import('fs');
|
|
34104
|
+
const data = fs20.readFileSync(image);
|
|
32619
34105
|
const base64 = data.toString("base64");
|
|
32620
34106
|
const ext = image.split(".").pop()?.toLowerCase() || "png";
|
|
32621
34107
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext}`;
|
|
@@ -35284,245 +36770,6 @@ var CheckpointManager = class {
|
|
|
35284
36770
|
}
|
|
35285
36771
|
};
|
|
35286
36772
|
|
|
35287
|
-
// src/domain/entities/Task.ts
|
|
35288
|
-
var TERMINAL_TASK_STATUSES = ["completed", "failed", "skipped", "cancelled"];
|
|
35289
|
-
function isTerminalStatus(status) {
|
|
35290
|
-
return TERMINAL_TASK_STATUSES.includes(status);
|
|
35291
|
-
}
|
|
35292
|
-
function createTask(input) {
|
|
35293
|
-
const now = Date.now();
|
|
35294
|
-
const id = input.id ?? `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
35295
|
-
return {
|
|
35296
|
-
id,
|
|
35297
|
-
name: input.name,
|
|
35298
|
-
description: input.description,
|
|
35299
|
-
status: "pending",
|
|
35300
|
-
dependsOn: input.dependsOn ?? [],
|
|
35301
|
-
externalDependency: input.externalDependency,
|
|
35302
|
-
condition: input.condition,
|
|
35303
|
-
execution: input.execution,
|
|
35304
|
-
validation: input.validation,
|
|
35305
|
-
expectedOutput: input.expectedOutput,
|
|
35306
|
-
attempts: 0,
|
|
35307
|
-
maxAttempts: input.maxAttempts ?? 3,
|
|
35308
|
-
createdAt: now,
|
|
35309
|
-
lastUpdatedAt: now,
|
|
35310
|
-
metadata: input.metadata
|
|
35311
|
-
};
|
|
35312
|
-
}
|
|
35313
|
-
function createPlan(input) {
|
|
35314
|
-
const now = Date.now();
|
|
35315
|
-
const id = `plan-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
35316
|
-
const tasks = input.tasks.map((taskInput) => createTask(taskInput));
|
|
35317
|
-
const nameToId = /* @__PURE__ */ new Map();
|
|
35318
|
-
for (const task of tasks) {
|
|
35319
|
-
nameToId.set(task.name, task.id);
|
|
35320
|
-
}
|
|
35321
|
-
for (let i = 0; i < tasks.length; i++) {
|
|
35322
|
-
const taskInput = input.tasks[i];
|
|
35323
|
-
const task = tasks[i];
|
|
35324
|
-
if (taskInput.dependsOn && taskInput.dependsOn.length > 0) {
|
|
35325
|
-
task.dependsOn = taskInput.dependsOn.map((dep) => {
|
|
35326
|
-
if (dep.startsWith("task-")) {
|
|
35327
|
-
return dep;
|
|
35328
|
-
}
|
|
35329
|
-
const resolvedId = nameToId.get(dep);
|
|
35330
|
-
if (!resolvedId) {
|
|
35331
|
-
throw new Error(`Task dependency "${dep}" not found in plan`);
|
|
35332
|
-
}
|
|
35333
|
-
return resolvedId;
|
|
35334
|
-
});
|
|
35335
|
-
}
|
|
35336
|
-
}
|
|
35337
|
-
if (!input.skipCycleCheck) {
|
|
35338
|
-
const cycle = detectDependencyCycle(tasks);
|
|
35339
|
-
if (cycle) {
|
|
35340
|
-
const cycleNames = cycle.map((taskId) => {
|
|
35341
|
-
const task = tasks.find((t) => t.id === taskId);
|
|
35342
|
-
return task ? task.name : taskId;
|
|
35343
|
-
});
|
|
35344
|
-
throw new DependencyCycleError(cycleNames, id);
|
|
35345
|
-
}
|
|
35346
|
-
}
|
|
35347
|
-
return {
|
|
35348
|
-
id,
|
|
35349
|
-
goal: input.goal,
|
|
35350
|
-
context: input.context,
|
|
35351
|
-
tasks,
|
|
35352
|
-
concurrency: input.concurrency,
|
|
35353
|
-
allowDynamicTasks: input.allowDynamicTasks ?? true,
|
|
35354
|
-
status: "pending",
|
|
35355
|
-
createdAt: now,
|
|
35356
|
-
lastUpdatedAt: now,
|
|
35357
|
-
metadata: input.metadata
|
|
35358
|
-
};
|
|
35359
|
-
}
|
|
35360
|
-
function canTaskExecute(task, allTasks) {
|
|
35361
|
-
if (task.status !== "pending") {
|
|
35362
|
-
return false;
|
|
35363
|
-
}
|
|
35364
|
-
if (task.dependsOn.length > 0) {
|
|
35365
|
-
for (const depId of task.dependsOn) {
|
|
35366
|
-
const depTask = allTasks.find((t) => t.id === depId);
|
|
35367
|
-
if (!depTask || depTask.status !== "completed") {
|
|
35368
|
-
return false;
|
|
35369
|
-
}
|
|
35370
|
-
}
|
|
35371
|
-
}
|
|
35372
|
-
return true;
|
|
35373
|
-
}
|
|
35374
|
-
function getNextExecutableTasks(plan) {
|
|
35375
|
-
const executable = plan.tasks.filter((task) => canTaskExecute(task, plan.tasks));
|
|
35376
|
-
if (executable.length === 0) {
|
|
35377
|
-
return [];
|
|
35378
|
-
}
|
|
35379
|
-
if (!plan.concurrency) {
|
|
35380
|
-
return [executable[0]];
|
|
35381
|
-
}
|
|
35382
|
-
const runningCount = plan.tasks.filter((t) => t.status === "in_progress").length;
|
|
35383
|
-
const availableSlots = plan.concurrency.maxParallelTasks - runningCount;
|
|
35384
|
-
if (availableSlots <= 0) {
|
|
35385
|
-
return [];
|
|
35386
|
-
}
|
|
35387
|
-
const parallelTasks = executable.filter((task) => task.execution?.parallel === true);
|
|
35388
|
-
if (parallelTasks.length === 0) {
|
|
35389
|
-
return [executable[0]];
|
|
35390
|
-
}
|
|
35391
|
-
let sortedTasks = [...parallelTasks];
|
|
35392
|
-
if (plan.concurrency.strategy === "priority") {
|
|
35393
|
-
sortedTasks.sort((a, b) => (b.execution?.priority ?? 0) - (a.execution?.priority ?? 0));
|
|
35394
|
-
}
|
|
35395
|
-
return sortedTasks.slice(0, availableSlots);
|
|
35396
|
-
}
|
|
35397
|
-
async function evaluateCondition(condition, memory) {
|
|
35398
|
-
const value = await memory.get(condition.memoryKey);
|
|
35399
|
-
switch (condition.operator) {
|
|
35400
|
-
case "exists":
|
|
35401
|
-
return value !== void 0;
|
|
35402
|
-
case "not_exists":
|
|
35403
|
-
return value === void 0;
|
|
35404
|
-
case "equals":
|
|
35405
|
-
return value === condition.value;
|
|
35406
|
-
case "contains":
|
|
35407
|
-
if (Array.isArray(value)) {
|
|
35408
|
-
return value.includes(condition.value);
|
|
35409
|
-
}
|
|
35410
|
-
if (typeof value === "string" && typeof condition.value === "string") {
|
|
35411
|
-
return value.includes(condition.value);
|
|
35412
|
-
}
|
|
35413
|
-
return false;
|
|
35414
|
-
case "truthy":
|
|
35415
|
-
return !!value;
|
|
35416
|
-
case "greater_than":
|
|
35417
|
-
if (typeof value === "number" && typeof condition.value === "number") {
|
|
35418
|
-
return value > condition.value;
|
|
35419
|
-
}
|
|
35420
|
-
return false;
|
|
35421
|
-
case "less_than":
|
|
35422
|
-
if (typeof value === "number" && typeof condition.value === "number") {
|
|
35423
|
-
return value < condition.value;
|
|
35424
|
-
}
|
|
35425
|
-
return false;
|
|
35426
|
-
default:
|
|
35427
|
-
return false;
|
|
35428
|
-
}
|
|
35429
|
-
}
|
|
35430
|
-
function updateTaskStatus(task, status) {
|
|
35431
|
-
const now = Date.now();
|
|
35432
|
-
const updated = {
|
|
35433
|
-
...task,
|
|
35434
|
-
status,
|
|
35435
|
-
lastUpdatedAt: now
|
|
35436
|
-
};
|
|
35437
|
-
if (status === "in_progress") {
|
|
35438
|
-
if (!updated.startedAt) {
|
|
35439
|
-
updated.startedAt = now;
|
|
35440
|
-
}
|
|
35441
|
-
updated.attempts += 1;
|
|
35442
|
-
}
|
|
35443
|
-
if ((status === "completed" || status === "failed") && !updated.completedAt) {
|
|
35444
|
-
updated.completedAt = now;
|
|
35445
|
-
}
|
|
35446
|
-
return updated;
|
|
35447
|
-
}
|
|
35448
|
-
function isTaskBlocked(task, allTasks) {
|
|
35449
|
-
if (task.dependsOn.length === 0) {
|
|
35450
|
-
return false;
|
|
35451
|
-
}
|
|
35452
|
-
for (const depId of task.dependsOn) {
|
|
35453
|
-
const depTask = allTasks.find((t) => t.id === depId);
|
|
35454
|
-
if (!depTask) {
|
|
35455
|
-
return true;
|
|
35456
|
-
}
|
|
35457
|
-
if (depTask.status !== "completed") {
|
|
35458
|
-
return true;
|
|
35459
|
-
}
|
|
35460
|
-
}
|
|
35461
|
-
return false;
|
|
35462
|
-
}
|
|
35463
|
-
function getTaskDependencies(task, allTasks) {
|
|
35464
|
-
if (task.dependsOn.length === 0) {
|
|
35465
|
-
return [];
|
|
35466
|
-
}
|
|
35467
|
-
return task.dependsOn.map((depId) => allTasks.find((t) => t.id === depId)).filter((t) => t !== void 0);
|
|
35468
|
-
}
|
|
35469
|
-
function resolveDependencies(taskInputs, tasks) {
|
|
35470
|
-
const nameToId = /* @__PURE__ */ new Map();
|
|
35471
|
-
for (const task of tasks) {
|
|
35472
|
-
nameToId.set(task.name, task.id);
|
|
35473
|
-
}
|
|
35474
|
-
for (const input of taskInputs) {
|
|
35475
|
-
if (input.dependsOn && input.dependsOn.length > 0) {
|
|
35476
|
-
input.dependsOn = input.dependsOn.map((dep) => {
|
|
35477
|
-
if (dep.startsWith("task-")) {
|
|
35478
|
-
return dep;
|
|
35479
|
-
}
|
|
35480
|
-
const resolvedId = nameToId.get(dep);
|
|
35481
|
-
if (!resolvedId) {
|
|
35482
|
-
throw new Error(`Task dependency "${dep}" not found`);
|
|
35483
|
-
}
|
|
35484
|
-
return resolvedId;
|
|
35485
|
-
});
|
|
35486
|
-
}
|
|
35487
|
-
}
|
|
35488
|
-
}
|
|
35489
|
-
function detectDependencyCycle(tasks) {
|
|
35490
|
-
const visited = /* @__PURE__ */ new Set();
|
|
35491
|
-
const recStack = /* @__PURE__ */ new Set();
|
|
35492
|
-
const taskMap = new Map(tasks.map((t) => [t.id, t]));
|
|
35493
|
-
function dfs(taskId, path6) {
|
|
35494
|
-
if (recStack.has(taskId)) {
|
|
35495
|
-
const cycleStart = path6.indexOf(taskId);
|
|
35496
|
-
return [...path6.slice(cycleStart), taskId];
|
|
35497
|
-
}
|
|
35498
|
-
if (visited.has(taskId)) {
|
|
35499
|
-
return null;
|
|
35500
|
-
}
|
|
35501
|
-
visited.add(taskId);
|
|
35502
|
-
recStack.add(taskId);
|
|
35503
|
-
const task = taskMap.get(taskId);
|
|
35504
|
-
if (task) {
|
|
35505
|
-
for (const depId of task.dependsOn) {
|
|
35506
|
-
const cycle = dfs(depId, [...path6, taskId]);
|
|
35507
|
-
if (cycle) {
|
|
35508
|
-
return cycle;
|
|
35509
|
-
}
|
|
35510
|
-
}
|
|
35511
|
-
}
|
|
35512
|
-
recStack.delete(taskId);
|
|
35513
|
-
return null;
|
|
35514
|
-
}
|
|
35515
|
-
for (const task of tasks) {
|
|
35516
|
-
if (!visited.has(task.id)) {
|
|
35517
|
-
const cycle = dfs(task.id, []);
|
|
35518
|
-
if (cycle) {
|
|
35519
|
-
return cycle;
|
|
35520
|
-
}
|
|
35521
|
-
}
|
|
35522
|
-
}
|
|
35523
|
-
return null;
|
|
35524
|
-
}
|
|
35525
|
-
|
|
35526
36773
|
// src/capabilities/taskAgent/PlanningAgent.ts
|
|
35527
36774
|
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.
|
|
35528
36775
|
|
|
@@ -36318,7 +37565,7 @@ var InMemoryHistoryStorage = class {
|
|
|
36318
37565
|
this.summaries = state.summaries ? [...state.summaries] : [];
|
|
36319
37566
|
}
|
|
36320
37567
|
};
|
|
36321
|
-
function
|
|
37568
|
+
function getDefaultBaseDirectory3() {
|
|
36322
37569
|
const platform2 = process.platform;
|
|
36323
37570
|
if (platform2 === "win32") {
|
|
36324
37571
|
const appData = process.env.APPDATA || process.env.LOCALAPPDATA;
|
|
@@ -36341,7 +37588,7 @@ var FileContextStorage = class {
|
|
|
36341
37588
|
constructor(config) {
|
|
36342
37589
|
this.agentId = config.agentId;
|
|
36343
37590
|
const sanitizedAgentId = sanitizeId(config.agentId);
|
|
36344
|
-
const baseDir = config.baseDirectory ??
|
|
37591
|
+
const baseDir = config.baseDirectory ?? getDefaultBaseDirectory3();
|
|
36345
37592
|
this.prettyPrint = config.prettyPrint ?? true;
|
|
36346
37593
|
this.sessionsDirectory = join(baseDir, sanitizedAgentId, "sessions");
|
|
36347
37594
|
this.indexPath = join(this.sessionsDirectory, "_index.json");
|
|
@@ -36613,7 +37860,7 @@ var FileContextStorage = class {
|
|
|
36613
37860
|
function createFileContextStorage(agentId, options) {
|
|
36614
37861
|
return new FileContextStorage({ agentId, ...options });
|
|
36615
37862
|
}
|
|
36616
|
-
function
|
|
37863
|
+
function getDefaultBaseDirectory4() {
|
|
36617
37864
|
const platform2 = process.platform;
|
|
36618
37865
|
if (platform2 === "win32") {
|
|
36619
37866
|
const appData = process.env.APPDATA || process.env.LOCALAPPDATA;
|
|
@@ -36632,7 +37879,7 @@ var FileAgentDefinitionStorage = class {
|
|
|
36632
37879
|
prettyPrint;
|
|
36633
37880
|
index = null;
|
|
36634
37881
|
constructor(config = {}) {
|
|
36635
|
-
this.baseDirectory = config.baseDirectory ??
|
|
37882
|
+
this.baseDirectory = config.baseDirectory ?? getDefaultBaseDirectory4();
|
|
36636
37883
|
this.prettyPrint = config.prettyPrint ?? true;
|
|
36637
37884
|
this.indexPath = join(this.baseDirectory, "_agents_index.json");
|
|
36638
37885
|
}
|
|
@@ -36887,10 +38134,10 @@ var FileMediaStorage = class {
|
|
|
36887
38134
|
}
|
|
36888
38135
|
async save(data, metadata) {
|
|
36889
38136
|
const dir = metadata.userId ? path2.join(this.outputDir, metadata.userId) : this.outputDir;
|
|
36890
|
-
await
|
|
38137
|
+
await fs18.mkdir(dir, { recursive: true });
|
|
36891
38138
|
const filename = metadata.suggestedFilename ?? this.generateFilename(metadata);
|
|
36892
38139
|
const filePath = path2.join(dir, filename);
|
|
36893
|
-
await
|
|
38140
|
+
await fs18.writeFile(filePath, data);
|
|
36894
38141
|
const format = metadata.format.toLowerCase();
|
|
36895
38142
|
const mimeType = MIME_TYPES2[format] ?? "application/octet-stream";
|
|
36896
38143
|
return {
|
|
@@ -36901,7 +38148,7 @@ var FileMediaStorage = class {
|
|
|
36901
38148
|
}
|
|
36902
38149
|
async read(location) {
|
|
36903
38150
|
try {
|
|
36904
|
-
return await
|
|
38151
|
+
return await fs18.readFile(location);
|
|
36905
38152
|
} catch (err) {
|
|
36906
38153
|
if (err.code === "ENOENT") {
|
|
36907
38154
|
return null;
|
|
@@ -36911,7 +38158,7 @@ var FileMediaStorage = class {
|
|
|
36911
38158
|
}
|
|
36912
38159
|
async delete(location) {
|
|
36913
38160
|
try {
|
|
36914
|
-
await
|
|
38161
|
+
await fs18.unlink(location);
|
|
36915
38162
|
} catch (err) {
|
|
36916
38163
|
if (err.code === "ENOENT") {
|
|
36917
38164
|
return;
|
|
@@ -36921,7 +38168,7 @@ var FileMediaStorage = class {
|
|
|
36921
38168
|
}
|
|
36922
38169
|
async exists(location) {
|
|
36923
38170
|
try {
|
|
36924
|
-
await
|
|
38171
|
+
await fs18.access(location);
|
|
36925
38172
|
return true;
|
|
36926
38173
|
} catch {
|
|
36927
38174
|
return false;
|
|
@@ -36930,11 +38177,11 @@ var FileMediaStorage = class {
|
|
|
36930
38177
|
async list(options) {
|
|
36931
38178
|
await this.ensureDir();
|
|
36932
38179
|
let entries = [];
|
|
36933
|
-
const files = await
|
|
38180
|
+
const files = await fs18.readdir(this.outputDir);
|
|
36934
38181
|
for (const file of files) {
|
|
36935
38182
|
const filePath = path2.join(this.outputDir, file);
|
|
36936
38183
|
try {
|
|
36937
|
-
const stat6 = await
|
|
38184
|
+
const stat6 = await fs18.stat(filePath);
|
|
36938
38185
|
if (!stat6.isFile()) continue;
|
|
36939
38186
|
const ext = path2.extname(file).slice(1).toLowerCase();
|
|
36940
38187
|
const mimeType = MIME_TYPES2[ext] ?? "application/octet-stream";
|
|
@@ -36974,7 +38221,7 @@ var FileMediaStorage = class {
|
|
|
36974
38221
|
}
|
|
36975
38222
|
async ensureDir() {
|
|
36976
38223
|
if (!this.initialized) {
|
|
36977
|
-
await
|
|
38224
|
+
await fs18.mkdir(this.outputDir, { recursive: true });
|
|
36978
38225
|
this.initialized = true;
|
|
36979
38226
|
}
|
|
36980
38227
|
}
|
|
@@ -36982,36 +38229,60 @@ var FileMediaStorage = class {
|
|
|
36982
38229
|
function createFileMediaStorage(config) {
|
|
36983
38230
|
return new FileMediaStorage(config);
|
|
36984
38231
|
}
|
|
36985
|
-
function
|
|
38232
|
+
function getDefaultBaseDirectory5() {
|
|
36986
38233
|
const platform2 = process.platform;
|
|
36987
38234
|
if (platform2 === "win32") {
|
|
36988
38235
|
const appData = process.env.APPDATA || process.env.LOCALAPPDATA;
|
|
36989
38236
|
if (appData) {
|
|
36990
|
-
return join(appData, "oneringai", "
|
|
38237
|
+
return join(appData, "oneringai", "users");
|
|
36991
38238
|
}
|
|
36992
38239
|
}
|
|
36993
|
-
return join(homedir(), ".oneringai", "
|
|
38240
|
+
return join(homedir(), ".oneringai", "users");
|
|
38241
|
+
}
|
|
38242
|
+
var DEFAULT_USER_ID2 = "default";
|
|
38243
|
+
function sanitizeUserId2(userId) {
|
|
38244
|
+
if (!userId) {
|
|
38245
|
+
return DEFAULT_USER_ID2;
|
|
38246
|
+
}
|
|
38247
|
+
return userId.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase() || DEFAULT_USER_ID2;
|
|
36994
38248
|
}
|
|
36995
38249
|
function sanitizeName(name) {
|
|
36996
38250
|
return name.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase() || "default";
|
|
36997
38251
|
}
|
|
36998
38252
|
var FileCustomToolStorage = class {
|
|
36999
38253
|
baseDirectory;
|
|
37000
|
-
indexPath;
|
|
37001
38254
|
prettyPrint;
|
|
37002
|
-
index = null;
|
|
37003
38255
|
constructor(config = {}) {
|
|
37004
|
-
this.baseDirectory = config.baseDirectory ??
|
|
38256
|
+
this.baseDirectory = config.baseDirectory ?? getDefaultBaseDirectory5();
|
|
37005
38257
|
this.prettyPrint = config.prettyPrint ?? true;
|
|
37006
|
-
|
|
38258
|
+
}
|
|
38259
|
+
/**
|
|
38260
|
+
* Get the directory path for a specific user's custom tools
|
|
38261
|
+
*/
|
|
38262
|
+
getUserDirectory(userId) {
|
|
38263
|
+
const sanitizedId = sanitizeUserId2(userId);
|
|
38264
|
+
return join(this.baseDirectory, sanitizedId, "custom-tools");
|
|
38265
|
+
}
|
|
38266
|
+
/**
|
|
38267
|
+
* Get the index file path for a specific user
|
|
38268
|
+
*/
|
|
38269
|
+
getUserIndexPath(userId) {
|
|
38270
|
+
return join(this.getUserDirectory(userId), "_index.json");
|
|
38271
|
+
}
|
|
38272
|
+
/**
|
|
38273
|
+
* Get the tool file path for a specific user
|
|
38274
|
+
*/
|
|
38275
|
+
getToolPath(userId, sanitizedName) {
|
|
38276
|
+
return join(this.getUserDirectory(userId), `${sanitizedName}.json`);
|
|
37007
38277
|
}
|
|
37008
38278
|
/**
|
|
37009
38279
|
* Save a custom tool definition
|
|
37010
38280
|
*/
|
|
37011
|
-
async save(definition) {
|
|
38281
|
+
async save(userId, definition) {
|
|
38282
|
+
const directory = this.getUserDirectory(userId);
|
|
37012
38283
|
const sanitized = sanitizeName(definition.name);
|
|
37013
|
-
const filePath =
|
|
37014
|
-
await this.ensureDirectory(
|
|
38284
|
+
const filePath = this.getToolPath(userId, sanitized);
|
|
38285
|
+
await this.ensureDirectory(directory);
|
|
37015
38286
|
const data = this.prettyPrint ? JSON.stringify(definition, null, 2) : JSON.stringify(definition);
|
|
37016
38287
|
const tempPath = `${filePath}.tmp`;
|
|
37017
38288
|
try {
|
|
@@ -37024,14 +38295,14 @@ var FileCustomToolStorage = class {
|
|
|
37024
38295
|
}
|
|
37025
38296
|
throw error;
|
|
37026
38297
|
}
|
|
37027
|
-
await this.updateIndex(definition);
|
|
38298
|
+
await this.updateIndex(userId, definition);
|
|
37028
38299
|
}
|
|
37029
38300
|
/**
|
|
37030
38301
|
* Load a custom tool definition by name
|
|
37031
38302
|
*/
|
|
37032
|
-
async load(name) {
|
|
38303
|
+
async load(userId, name) {
|
|
37033
38304
|
const sanitized = sanitizeName(name);
|
|
37034
|
-
const filePath =
|
|
38305
|
+
const filePath = this.getToolPath(userId, sanitized);
|
|
37035
38306
|
try {
|
|
37036
38307
|
const data = await promises.readFile(filePath, "utf-8");
|
|
37037
38308
|
return JSON.parse(data);
|
|
@@ -37048,9 +38319,9 @@ var FileCustomToolStorage = class {
|
|
|
37048
38319
|
/**
|
|
37049
38320
|
* Delete a custom tool definition
|
|
37050
38321
|
*/
|
|
37051
|
-
async delete(name) {
|
|
38322
|
+
async delete(userId, name) {
|
|
37052
38323
|
const sanitized = sanitizeName(name);
|
|
37053
|
-
const filePath =
|
|
38324
|
+
const filePath = this.getToolPath(userId, sanitized);
|
|
37054
38325
|
try {
|
|
37055
38326
|
await promises.unlink(filePath);
|
|
37056
38327
|
} catch (error) {
|
|
@@ -37058,14 +38329,14 @@ var FileCustomToolStorage = class {
|
|
|
37058
38329
|
throw error;
|
|
37059
38330
|
}
|
|
37060
38331
|
}
|
|
37061
|
-
await this.removeFromIndex(name);
|
|
38332
|
+
await this.removeFromIndex(userId, name);
|
|
37062
38333
|
}
|
|
37063
38334
|
/**
|
|
37064
38335
|
* Check if a custom tool exists
|
|
37065
38336
|
*/
|
|
37066
|
-
async exists(name) {
|
|
38337
|
+
async exists(userId, name) {
|
|
37067
38338
|
const sanitized = sanitizeName(name);
|
|
37068
|
-
const filePath =
|
|
38339
|
+
const filePath = this.getToolPath(userId, sanitized);
|
|
37069
38340
|
try {
|
|
37070
38341
|
await promises.access(filePath);
|
|
37071
38342
|
return true;
|
|
@@ -37076,8 +38347,8 @@ var FileCustomToolStorage = class {
|
|
|
37076
38347
|
/**
|
|
37077
38348
|
* List custom tools (summaries only)
|
|
37078
38349
|
*/
|
|
37079
|
-
async list(options) {
|
|
37080
|
-
const index = await this.loadIndex();
|
|
38350
|
+
async list(userId, options) {
|
|
38351
|
+
const index = await this.loadIndex(userId);
|
|
37081
38352
|
let entries = [...index.tools];
|
|
37082
38353
|
if (options?.tags && options.tags.length > 0) {
|
|
37083
38354
|
entries = entries.filter((e) => {
|
|
@@ -37118,20 +38389,20 @@ var FileCustomToolStorage = class {
|
|
|
37118
38389
|
/**
|
|
37119
38390
|
* Update metadata without loading full definition
|
|
37120
38391
|
*/
|
|
37121
|
-
async updateMetadata(name, metadata) {
|
|
37122
|
-
const definition = await this.load(name);
|
|
38392
|
+
async updateMetadata(userId, name, metadata) {
|
|
38393
|
+
const definition = await this.load(userId, name);
|
|
37123
38394
|
if (!definition) {
|
|
37124
38395
|
throw new Error(`Custom tool '${name}' not found`);
|
|
37125
38396
|
}
|
|
37126
38397
|
definition.metadata = { ...definition.metadata, ...metadata };
|
|
37127
38398
|
definition.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
37128
|
-
await this.save(definition);
|
|
38399
|
+
await this.save(userId, definition);
|
|
37129
38400
|
}
|
|
37130
38401
|
/**
|
|
37131
|
-
* Get storage path
|
|
38402
|
+
* Get storage path for a specific user
|
|
37132
38403
|
*/
|
|
37133
|
-
getPath() {
|
|
37134
|
-
return this.
|
|
38404
|
+
getPath(userId) {
|
|
38405
|
+
return this.getUserDirectory(userId);
|
|
37135
38406
|
}
|
|
37136
38407
|
// ==========================================================================
|
|
37137
38408
|
// Private Helpers
|
|
@@ -37145,35 +38416,32 @@ var FileCustomToolStorage = class {
|
|
|
37145
38416
|
}
|
|
37146
38417
|
}
|
|
37147
38418
|
}
|
|
37148
|
-
async loadIndex() {
|
|
37149
|
-
|
|
37150
|
-
return this.index;
|
|
37151
|
-
}
|
|
38419
|
+
async loadIndex(userId) {
|
|
38420
|
+
const indexPath = this.getUserIndexPath(userId);
|
|
37152
38421
|
try {
|
|
37153
|
-
const data = await promises.readFile(
|
|
37154
|
-
|
|
37155
|
-
return this.index;
|
|
38422
|
+
const data = await promises.readFile(indexPath, "utf-8");
|
|
38423
|
+
return JSON.parse(data);
|
|
37156
38424
|
} catch (error) {
|
|
37157
38425
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
37158
|
-
|
|
38426
|
+
return {
|
|
37159
38427
|
version: 1,
|
|
37160
38428
|
tools: [],
|
|
37161
38429
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
37162
38430
|
};
|
|
37163
|
-
return this.index;
|
|
37164
38431
|
}
|
|
37165
38432
|
throw error;
|
|
37166
38433
|
}
|
|
37167
38434
|
}
|
|
37168
|
-
async saveIndex() {
|
|
37169
|
-
|
|
37170
|
-
|
|
37171
|
-
this.
|
|
37172
|
-
|
|
37173
|
-
|
|
38435
|
+
async saveIndex(userId, index) {
|
|
38436
|
+
const directory = this.getUserDirectory(userId);
|
|
38437
|
+
const indexPath = this.getUserIndexPath(userId);
|
|
38438
|
+
await this.ensureDirectory(directory);
|
|
38439
|
+
index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
38440
|
+
const data = this.prettyPrint ? JSON.stringify(index, null, 2) : JSON.stringify(index);
|
|
38441
|
+
await promises.writeFile(indexPath, data, "utf-8");
|
|
37174
38442
|
}
|
|
37175
|
-
async updateIndex(definition) {
|
|
37176
|
-
const index = await this.loadIndex();
|
|
38443
|
+
async updateIndex(userId, definition) {
|
|
38444
|
+
const index = await this.loadIndex(userId);
|
|
37177
38445
|
const entry = this.definitionToIndexEntry(definition);
|
|
37178
38446
|
const existingIdx = index.tools.findIndex((e) => e.name === definition.name);
|
|
37179
38447
|
if (existingIdx >= 0) {
|
|
@@ -37181,12 +38449,12 @@ var FileCustomToolStorage = class {
|
|
|
37181
38449
|
} else {
|
|
37182
38450
|
index.tools.push(entry);
|
|
37183
38451
|
}
|
|
37184
|
-
await this.saveIndex();
|
|
38452
|
+
await this.saveIndex(userId, index);
|
|
37185
38453
|
}
|
|
37186
|
-
async removeFromIndex(name) {
|
|
37187
|
-
const index = await this.loadIndex();
|
|
38454
|
+
async removeFromIndex(userId, name) {
|
|
38455
|
+
const index = await this.loadIndex(userId);
|
|
37188
38456
|
index.tools = index.tools.filter((e) => e.name !== name);
|
|
37189
|
-
await this.saveIndex();
|
|
38457
|
+
await this.saveIndex(userId, index);
|
|
37190
38458
|
}
|
|
37191
38459
|
definitionToIndexEntry(definition) {
|
|
37192
38460
|
return {
|
|
@@ -37203,6 +38471,234 @@ var FileCustomToolStorage = class {
|
|
|
37203
38471
|
function createFileCustomToolStorage(config) {
|
|
37204
38472
|
return new FileCustomToolStorage(config);
|
|
37205
38473
|
}
|
|
38474
|
+
var STORAGE_VERSION = 1;
|
|
38475
|
+
var DEFAULT_USER_ID3 = "default";
|
|
38476
|
+
function getDefaultBaseDirectory6() {
|
|
38477
|
+
const platform2 = process.platform;
|
|
38478
|
+
if (platform2 === "win32") {
|
|
38479
|
+
const appData = process.env.APPDATA || process.env.LOCALAPPDATA;
|
|
38480
|
+
if (appData) {
|
|
38481
|
+
return join(appData, "oneringai", "users");
|
|
38482
|
+
}
|
|
38483
|
+
}
|
|
38484
|
+
return join(homedir(), ".oneringai", "users");
|
|
38485
|
+
}
|
|
38486
|
+
function sanitizeUserId3(userId) {
|
|
38487
|
+
if (!userId) {
|
|
38488
|
+
return DEFAULT_USER_ID3;
|
|
38489
|
+
}
|
|
38490
|
+
return userId.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase() || DEFAULT_USER_ID3;
|
|
38491
|
+
}
|
|
38492
|
+
function sanitizeId2(id) {
|
|
38493
|
+
return id.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase() || "default";
|
|
38494
|
+
}
|
|
38495
|
+
var FileRoutineDefinitionStorage = class {
|
|
38496
|
+
baseDirectory;
|
|
38497
|
+
prettyPrint;
|
|
38498
|
+
constructor(config = {}) {
|
|
38499
|
+
this.baseDirectory = config.baseDirectory ?? getDefaultBaseDirectory6();
|
|
38500
|
+
this.prettyPrint = config.prettyPrint ?? true;
|
|
38501
|
+
}
|
|
38502
|
+
getUserDirectory(userId) {
|
|
38503
|
+
const sanitizedId = sanitizeUserId3(userId);
|
|
38504
|
+
return join(this.baseDirectory, sanitizedId, "routines");
|
|
38505
|
+
}
|
|
38506
|
+
getIndexPath(userId) {
|
|
38507
|
+
return join(this.getUserDirectory(userId), "_index.json");
|
|
38508
|
+
}
|
|
38509
|
+
getRoutinePath(userId, sanitizedId) {
|
|
38510
|
+
return join(this.getUserDirectory(userId), `${sanitizedId}.json`);
|
|
38511
|
+
}
|
|
38512
|
+
async save(userId, definition) {
|
|
38513
|
+
const directory = this.getUserDirectory(userId);
|
|
38514
|
+
const sanitized = sanitizeId2(definition.id);
|
|
38515
|
+
const filePath = this.getRoutinePath(userId, sanitized);
|
|
38516
|
+
await this.ensureDirectory(directory);
|
|
38517
|
+
const stored = { version: STORAGE_VERSION, definition };
|
|
38518
|
+
const data = this.prettyPrint ? JSON.stringify(stored, null, 2) : JSON.stringify(stored);
|
|
38519
|
+
const tempPath = `${filePath}.tmp`;
|
|
38520
|
+
try {
|
|
38521
|
+
await promises.writeFile(tempPath, data, "utf-8");
|
|
38522
|
+
await promises.rename(tempPath, filePath);
|
|
38523
|
+
} catch (error) {
|
|
38524
|
+
try {
|
|
38525
|
+
await promises.unlink(tempPath);
|
|
38526
|
+
} catch {
|
|
38527
|
+
}
|
|
38528
|
+
throw error;
|
|
38529
|
+
}
|
|
38530
|
+
await this.updateIndex(userId, definition);
|
|
38531
|
+
}
|
|
38532
|
+
async load(userId, id) {
|
|
38533
|
+
const sanitized = sanitizeId2(id);
|
|
38534
|
+
const filePath = this.getRoutinePath(userId, sanitized);
|
|
38535
|
+
try {
|
|
38536
|
+
const data = await promises.readFile(filePath, "utf-8");
|
|
38537
|
+
const stored = JSON.parse(data);
|
|
38538
|
+
return stored.definition;
|
|
38539
|
+
} catch (error) {
|
|
38540
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
38541
|
+
return null;
|
|
38542
|
+
}
|
|
38543
|
+
if (error instanceof SyntaxError) {
|
|
38544
|
+
return null;
|
|
38545
|
+
}
|
|
38546
|
+
throw error;
|
|
38547
|
+
}
|
|
38548
|
+
}
|
|
38549
|
+
async delete(userId, id) {
|
|
38550
|
+
const sanitized = sanitizeId2(id);
|
|
38551
|
+
const filePath = this.getRoutinePath(userId, sanitized);
|
|
38552
|
+
try {
|
|
38553
|
+
await promises.unlink(filePath);
|
|
38554
|
+
} catch (error) {
|
|
38555
|
+
if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
|
|
38556
|
+
throw error;
|
|
38557
|
+
}
|
|
38558
|
+
}
|
|
38559
|
+
await this.removeFromIndex(userId, id);
|
|
38560
|
+
}
|
|
38561
|
+
async exists(userId, id) {
|
|
38562
|
+
const sanitized = sanitizeId2(id);
|
|
38563
|
+
const filePath = this.getRoutinePath(userId, sanitized);
|
|
38564
|
+
try {
|
|
38565
|
+
await promises.access(filePath);
|
|
38566
|
+
return true;
|
|
38567
|
+
} catch {
|
|
38568
|
+
return false;
|
|
38569
|
+
}
|
|
38570
|
+
}
|
|
38571
|
+
async list(userId, options) {
|
|
38572
|
+
const index = await this.loadIndex(userId);
|
|
38573
|
+
let entries = [...index.routines];
|
|
38574
|
+
if (options?.tags && options.tags.length > 0) {
|
|
38575
|
+
entries = entries.filter((e) => {
|
|
38576
|
+
const entryTags = e.tags ?? [];
|
|
38577
|
+
return options.tags.some((t) => entryTags.includes(t));
|
|
38578
|
+
});
|
|
38579
|
+
}
|
|
38580
|
+
if (options?.search) {
|
|
38581
|
+
const searchLower = options.search.toLowerCase();
|
|
38582
|
+
entries = entries.filter(
|
|
38583
|
+
(e) => e.name.toLowerCase().includes(searchLower) || e.description.toLowerCase().includes(searchLower)
|
|
38584
|
+
);
|
|
38585
|
+
}
|
|
38586
|
+
entries.sort(
|
|
38587
|
+
(a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
|
|
38588
|
+
);
|
|
38589
|
+
if (options?.offset) {
|
|
38590
|
+
entries = entries.slice(options.offset);
|
|
38591
|
+
}
|
|
38592
|
+
if (options?.limit) {
|
|
38593
|
+
entries = entries.slice(0, options.limit);
|
|
38594
|
+
}
|
|
38595
|
+
const results = [];
|
|
38596
|
+
for (const entry of entries) {
|
|
38597
|
+
const def = await this.load(userId, entry.id);
|
|
38598
|
+
if (def) {
|
|
38599
|
+
results.push(def);
|
|
38600
|
+
}
|
|
38601
|
+
}
|
|
38602
|
+
return results;
|
|
38603
|
+
}
|
|
38604
|
+
getPath(userId) {
|
|
38605
|
+
return this.getUserDirectory(userId);
|
|
38606
|
+
}
|
|
38607
|
+
// ==========================================================================
|
|
38608
|
+
// Private Helpers
|
|
38609
|
+
// ==========================================================================
|
|
38610
|
+
async ensureDirectory(dir) {
|
|
38611
|
+
try {
|
|
38612
|
+
await promises.mkdir(dir, { recursive: true });
|
|
38613
|
+
} catch (error) {
|
|
38614
|
+
if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
|
|
38615
|
+
throw error;
|
|
38616
|
+
}
|
|
38617
|
+
}
|
|
38618
|
+
}
|
|
38619
|
+
async loadIndex(userId) {
|
|
38620
|
+
const indexPath = this.getIndexPath(userId);
|
|
38621
|
+
try {
|
|
38622
|
+
const data = await promises.readFile(indexPath, "utf-8");
|
|
38623
|
+
return JSON.parse(data);
|
|
38624
|
+
} catch (error) {
|
|
38625
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
38626
|
+
return await this.rebuildIndex(userId);
|
|
38627
|
+
}
|
|
38628
|
+
throw error;
|
|
38629
|
+
}
|
|
38630
|
+
}
|
|
38631
|
+
async saveIndex(userId, index) {
|
|
38632
|
+
const directory = this.getUserDirectory(userId);
|
|
38633
|
+
const indexPath = this.getIndexPath(userId);
|
|
38634
|
+
await this.ensureDirectory(directory);
|
|
38635
|
+
index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
38636
|
+
const data = this.prettyPrint ? JSON.stringify(index, null, 2) : JSON.stringify(index);
|
|
38637
|
+
await promises.writeFile(indexPath, data, "utf-8");
|
|
38638
|
+
}
|
|
38639
|
+
async updateIndex(userId, definition) {
|
|
38640
|
+
const index = await this.loadIndex(userId);
|
|
38641
|
+
const entry = this.definitionToIndexEntry(definition);
|
|
38642
|
+
const existingIdx = index.routines.findIndex((e) => e.id === definition.id);
|
|
38643
|
+
if (existingIdx >= 0) {
|
|
38644
|
+
index.routines[existingIdx] = entry;
|
|
38645
|
+
} else {
|
|
38646
|
+
index.routines.push(entry);
|
|
38647
|
+
}
|
|
38648
|
+
await this.saveIndex(userId, index);
|
|
38649
|
+
}
|
|
38650
|
+
async removeFromIndex(userId, id) {
|
|
38651
|
+
const index = await this.loadIndex(userId);
|
|
38652
|
+
index.routines = index.routines.filter((e) => e.id !== id);
|
|
38653
|
+
await this.saveIndex(userId, index);
|
|
38654
|
+
}
|
|
38655
|
+
definitionToIndexEntry(definition) {
|
|
38656
|
+
return {
|
|
38657
|
+
id: definition.id,
|
|
38658
|
+
name: definition.name,
|
|
38659
|
+
description: definition.description,
|
|
38660
|
+
tags: definition.tags,
|
|
38661
|
+
author: definition.author,
|
|
38662
|
+
updatedAt: definition.updatedAt
|
|
38663
|
+
};
|
|
38664
|
+
}
|
|
38665
|
+
/**
|
|
38666
|
+
* Rebuild index by scanning directory for .json files (excluding _index.json).
|
|
38667
|
+
* Returns empty index if directory doesn't exist.
|
|
38668
|
+
*/
|
|
38669
|
+
async rebuildIndex(userId) {
|
|
38670
|
+
const directory = this.getUserDirectory(userId);
|
|
38671
|
+
const index = {
|
|
38672
|
+
version: 1,
|
|
38673
|
+
routines: [],
|
|
38674
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
38675
|
+
};
|
|
38676
|
+
let files;
|
|
38677
|
+
try {
|
|
38678
|
+
files = await promises.readdir(directory);
|
|
38679
|
+
} catch {
|
|
38680
|
+
return index;
|
|
38681
|
+
}
|
|
38682
|
+
for (const file of files) {
|
|
38683
|
+
if (!file.endsWith(".json") || file === "_index.json") continue;
|
|
38684
|
+
try {
|
|
38685
|
+
const data = await promises.readFile(join(directory, file), "utf-8");
|
|
38686
|
+
const stored = JSON.parse(data);
|
|
38687
|
+
if (stored.definition) {
|
|
38688
|
+
index.routines.push(this.definitionToIndexEntry(stored.definition));
|
|
38689
|
+
}
|
|
38690
|
+
} catch {
|
|
38691
|
+
}
|
|
38692
|
+
}
|
|
38693
|
+
if (index.routines.length > 0) {
|
|
38694
|
+
await this.saveIndex(userId, index);
|
|
38695
|
+
}
|
|
38696
|
+
return index;
|
|
38697
|
+
}
|
|
38698
|
+
};
|
|
38699
|
+
function createFileRoutineDefinitionStorage(config) {
|
|
38700
|
+
return new FileRoutineDefinitionStorage(config);
|
|
38701
|
+
}
|
|
37206
38702
|
|
|
37207
38703
|
// src/domain/entities/CustomToolDefinition.ts
|
|
37208
38704
|
var CUSTOM_TOOL_DEFINITION_VERSION = 1;
|
|
@@ -38303,8 +39799,8 @@ var FileStorage = class {
|
|
|
38303
39799
|
}
|
|
38304
39800
|
async ensureDirectory() {
|
|
38305
39801
|
try {
|
|
38306
|
-
await
|
|
38307
|
-
await
|
|
39802
|
+
await fs18.mkdir(this.directory, { recursive: true });
|
|
39803
|
+
await fs18.chmod(this.directory, 448);
|
|
38308
39804
|
} catch (error) {
|
|
38309
39805
|
}
|
|
38310
39806
|
}
|
|
@@ -38320,13 +39816,13 @@ var FileStorage = class {
|
|
|
38320
39816
|
const filePath = this.getFilePath(key);
|
|
38321
39817
|
const plaintext = JSON.stringify(token);
|
|
38322
39818
|
const encrypted = encrypt(plaintext, this.encryptionKey);
|
|
38323
|
-
await
|
|
38324
|
-
await
|
|
39819
|
+
await fs18.writeFile(filePath, encrypted, "utf8");
|
|
39820
|
+
await fs18.chmod(filePath, 384);
|
|
38325
39821
|
}
|
|
38326
39822
|
async getToken(key) {
|
|
38327
39823
|
const filePath = this.getFilePath(key);
|
|
38328
39824
|
try {
|
|
38329
|
-
const encrypted = await
|
|
39825
|
+
const encrypted = await fs18.readFile(filePath, "utf8");
|
|
38330
39826
|
const decrypted = decrypt(encrypted, this.encryptionKey);
|
|
38331
39827
|
return JSON.parse(decrypted);
|
|
38332
39828
|
} catch (error) {
|
|
@@ -38335,7 +39831,7 @@ var FileStorage = class {
|
|
|
38335
39831
|
}
|
|
38336
39832
|
console.error("Failed to read/decrypt token file:", error);
|
|
38337
39833
|
try {
|
|
38338
|
-
await
|
|
39834
|
+
await fs18.unlink(filePath);
|
|
38339
39835
|
} catch {
|
|
38340
39836
|
}
|
|
38341
39837
|
return null;
|
|
@@ -38344,7 +39840,7 @@ var FileStorage = class {
|
|
|
38344
39840
|
async deleteToken(key) {
|
|
38345
39841
|
const filePath = this.getFilePath(key);
|
|
38346
39842
|
try {
|
|
38347
|
-
await
|
|
39843
|
+
await fs18.unlink(filePath);
|
|
38348
39844
|
} catch (error) {
|
|
38349
39845
|
if (error.code !== "ENOENT") {
|
|
38350
39846
|
throw error;
|
|
@@ -38354,7 +39850,7 @@ var FileStorage = class {
|
|
|
38354
39850
|
async hasToken(key) {
|
|
38355
39851
|
const filePath = this.getFilePath(key);
|
|
38356
39852
|
try {
|
|
38357
|
-
await
|
|
39853
|
+
await fs18.access(filePath);
|
|
38358
39854
|
return true;
|
|
38359
39855
|
} catch {
|
|
38360
39856
|
return false;
|
|
@@ -38365,7 +39861,7 @@ var FileStorage = class {
|
|
|
38365
39861
|
*/
|
|
38366
39862
|
async listTokens() {
|
|
38367
39863
|
try {
|
|
38368
|
-
const files = await
|
|
39864
|
+
const files = await fs18.readdir(this.directory);
|
|
38369
39865
|
return files.filter((f) => f.endsWith(".token")).map((f) => f.replace(".token", ""));
|
|
38370
39866
|
} catch {
|
|
38371
39867
|
return [];
|
|
@@ -38376,10 +39872,10 @@ var FileStorage = class {
|
|
|
38376
39872
|
*/
|
|
38377
39873
|
async clearAll() {
|
|
38378
39874
|
try {
|
|
38379
|
-
const files = await
|
|
39875
|
+
const files = await fs18.readdir(this.directory);
|
|
38380
39876
|
const tokenFiles = files.filter((f) => f.endsWith(".token"));
|
|
38381
39877
|
await Promise.all(
|
|
38382
|
-
tokenFiles.map((f) =>
|
|
39878
|
+
tokenFiles.map((f) => fs18.unlink(path2.join(this.directory, f)).catch(() => {
|
|
38383
39879
|
}))
|
|
38384
39880
|
);
|
|
38385
39881
|
} catch {
|
|
@@ -38827,14 +40323,14 @@ var FileConnectorStorage = class {
|
|
|
38827
40323
|
await this.ensureDirectory();
|
|
38828
40324
|
const filePath = this.getFilePath(name);
|
|
38829
40325
|
const json = JSON.stringify(stored, null, 2);
|
|
38830
|
-
await
|
|
38831
|
-
await
|
|
40326
|
+
await fs18.writeFile(filePath, json, "utf8");
|
|
40327
|
+
await fs18.chmod(filePath, 384);
|
|
38832
40328
|
await this.updateIndex(name, "add");
|
|
38833
40329
|
}
|
|
38834
40330
|
async get(name) {
|
|
38835
40331
|
const filePath = this.getFilePath(name);
|
|
38836
40332
|
try {
|
|
38837
|
-
const json = await
|
|
40333
|
+
const json = await fs18.readFile(filePath, "utf8");
|
|
38838
40334
|
return JSON.parse(json);
|
|
38839
40335
|
} catch (error) {
|
|
38840
40336
|
const err = error;
|
|
@@ -38847,7 +40343,7 @@ var FileConnectorStorage = class {
|
|
|
38847
40343
|
async delete(name) {
|
|
38848
40344
|
const filePath = this.getFilePath(name);
|
|
38849
40345
|
try {
|
|
38850
|
-
await
|
|
40346
|
+
await fs18.unlink(filePath);
|
|
38851
40347
|
await this.updateIndex(name, "remove");
|
|
38852
40348
|
return true;
|
|
38853
40349
|
} catch (error) {
|
|
@@ -38861,7 +40357,7 @@ var FileConnectorStorage = class {
|
|
|
38861
40357
|
async has(name) {
|
|
38862
40358
|
const filePath = this.getFilePath(name);
|
|
38863
40359
|
try {
|
|
38864
|
-
await
|
|
40360
|
+
await fs18.access(filePath);
|
|
38865
40361
|
return true;
|
|
38866
40362
|
} catch {
|
|
38867
40363
|
return false;
|
|
@@ -38887,13 +40383,13 @@ var FileConnectorStorage = class {
|
|
|
38887
40383
|
*/
|
|
38888
40384
|
async clear() {
|
|
38889
40385
|
try {
|
|
38890
|
-
const files = await
|
|
40386
|
+
const files = await fs18.readdir(this.directory);
|
|
38891
40387
|
const connectorFiles = files.filter(
|
|
38892
40388
|
(f) => f.endsWith(".connector.json") || f === "_index.json"
|
|
38893
40389
|
);
|
|
38894
40390
|
await Promise.all(
|
|
38895
40391
|
connectorFiles.map(
|
|
38896
|
-
(f) =>
|
|
40392
|
+
(f) => fs18.unlink(path2.join(this.directory, f)).catch(() => {
|
|
38897
40393
|
})
|
|
38898
40394
|
)
|
|
38899
40395
|
);
|
|
@@ -38920,8 +40416,8 @@ var FileConnectorStorage = class {
|
|
|
38920
40416
|
async ensureDirectory() {
|
|
38921
40417
|
if (this.initialized) return;
|
|
38922
40418
|
try {
|
|
38923
|
-
await
|
|
38924
|
-
await
|
|
40419
|
+
await fs18.mkdir(this.directory, { recursive: true });
|
|
40420
|
+
await fs18.chmod(this.directory, 448);
|
|
38925
40421
|
this.initialized = true;
|
|
38926
40422
|
} catch {
|
|
38927
40423
|
this.initialized = true;
|
|
@@ -38932,7 +40428,7 @@ var FileConnectorStorage = class {
|
|
|
38932
40428
|
*/
|
|
38933
40429
|
async loadIndex() {
|
|
38934
40430
|
try {
|
|
38935
|
-
const json = await
|
|
40431
|
+
const json = await fs18.readFile(this.indexPath, "utf8");
|
|
38936
40432
|
return JSON.parse(json);
|
|
38937
40433
|
} catch {
|
|
38938
40434
|
return { connectors: {} };
|
|
@@ -38950,8 +40446,8 @@ var FileConnectorStorage = class {
|
|
|
38950
40446
|
delete index.connectors[hash];
|
|
38951
40447
|
}
|
|
38952
40448
|
const json = JSON.stringify(index, null, 2);
|
|
38953
|
-
await
|
|
38954
|
-
await
|
|
40449
|
+
await fs18.writeFile(this.indexPath, json, "utf8");
|
|
40450
|
+
await fs18.chmod(this.indexPath, 384);
|
|
38955
40451
|
}
|
|
38956
40452
|
};
|
|
38957
40453
|
|
|
@@ -41406,8 +42902,8 @@ function createMessageWithImages(text, imageUrls, role = "user" /* USER */) {
|
|
|
41406
42902
|
var execAsync = promisify(exec);
|
|
41407
42903
|
function cleanupTempFile(filePath) {
|
|
41408
42904
|
try {
|
|
41409
|
-
if (
|
|
41410
|
-
|
|
42905
|
+
if (fs19.existsSync(filePath)) {
|
|
42906
|
+
fs19.unlinkSync(filePath);
|
|
41411
42907
|
}
|
|
41412
42908
|
} catch {
|
|
41413
42909
|
}
|
|
@@ -41458,7 +42954,7 @@ async function readClipboardImageMac() {
|
|
|
41458
42954
|
end try
|
|
41459
42955
|
`;
|
|
41460
42956
|
const { stdout } = await execAsync(`osascript -e '${script}'`);
|
|
41461
|
-
if (stdout.includes("success") ||
|
|
42957
|
+
if (stdout.includes("success") || fs19.existsSync(tempFile)) {
|
|
41462
42958
|
return await convertFileToDataUri(tempFile);
|
|
41463
42959
|
}
|
|
41464
42960
|
return {
|
|
@@ -41475,14 +42971,14 @@ async function readClipboardImageLinux() {
|
|
|
41475
42971
|
try {
|
|
41476
42972
|
try {
|
|
41477
42973
|
await execAsync(`xclip -selection clipboard -t image/png -o > "${tempFile}"`);
|
|
41478
|
-
if (
|
|
42974
|
+
if (fs19.existsSync(tempFile) && fs19.statSync(tempFile).size > 0) {
|
|
41479
42975
|
return await convertFileToDataUri(tempFile);
|
|
41480
42976
|
}
|
|
41481
42977
|
} catch {
|
|
41482
42978
|
}
|
|
41483
42979
|
try {
|
|
41484
42980
|
await execAsync(`wl-paste -t image/png > "${tempFile}"`);
|
|
41485
|
-
if (
|
|
42981
|
+
if (fs19.existsSync(tempFile) && fs19.statSync(tempFile).size > 0) {
|
|
41486
42982
|
return await convertFileToDataUri(tempFile);
|
|
41487
42983
|
}
|
|
41488
42984
|
} catch {
|
|
@@ -41509,7 +43005,7 @@ async function readClipboardImageWindows() {
|
|
|
41509
43005
|
}
|
|
41510
43006
|
`;
|
|
41511
43007
|
await execAsync(`powershell -Command "${psScript}"`);
|
|
41512
|
-
if (
|
|
43008
|
+
if (fs19.existsSync(tempFile) && fs19.statSync(tempFile).size > 0) {
|
|
41513
43009
|
return await convertFileToDataUri(tempFile);
|
|
41514
43010
|
}
|
|
41515
43011
|
return {
|
|
@@ -41522,7 +43018,7 @@ async function readClipboardImageWindows() {
|
|
|
41522
43018
|
}
|
|
41523
43019
|
async function convertFileToDataUri(filePath) {
|
|
41524
43020
|
try {
|
|
41525
|
-
const imageBuffer =
|
|
43021
|
+
const imageBuffer = fs19.readFileSync(filePath);
|
|
41526
43022
|
const base64Image = imageBuffer.toString("base64");
|
|
41527
43023
|
const magic = imageBuffer.slice(0, 4).toString("hex");
|
|
41528
43024
|
let mimeType = "image/png";
|
|
@@ -41581,186 +43077,6 @@ async function hasClipboardImage() {
|
|
|
41581
43077
|
}
|
|
41582
43078
|
}
|
|
41583
43079
|
|
|
41584
|
-
// src/utils/jsonExtractor.ts
|
|
41585
|
-
function extractJSON(text) {
|
|
41586
|
-
if (!text || typeof text !== "string") {
|
|
41587
|
-
return {
|
|
41588
|
-
success: false,
|
|
41589
|
-
error: "Input is empty or not a string"
|
|
41590
|
-
};
|
|
41591
|
-
}
|
|
41592
|
-
const trimmedText = text.trim();
|
|
41593
|
-
const codeBlockResult = extractFromCodeBlock(trimmedText);
|
|
41594
|
-
if (codeBlockResult.success) {
|
|
41595
|
-
return codeBlockResult;
|
|
41596
|
-
}
|
|
41597
|
-
const inlineResult = extractInlineJSON(trimmedText);
|
|
41598
|
-
if (inlineResult.success) {
|
|
41599
|
-
return inlineResult;
|
|
41600
|
-
}
|
|
41601
|
-
try {
|
|
41602
|
-
const data = JSON.parse(trimmedText);
|
|
41603
|
-
return {
|
|
41604
|
-
success: true,
|
|
41605
|
-
data,
|
|
41606
|
-
rawJson: trimmedText,
|
|
41607
|
-
method: "raw"
|
|
41608
|
-
};
|
|
41609
|
-
} catch (e) {
|
|
41610
|
-
return {
|
|
41611
|
-
success: false,
|
|
41612
|
-
error: `Could not extract JSON from text: ${e instanceof Error ? e.message : String(e)}`
|
|
41613
|
-
};
|
|
41614
|
-
}
|
|
41615
|
-
}
|
|
41616
|
-
function extractFromCodeBlock(text) {
|
|
41617
|
-
const codeBlockRegex = /```(?:json)?\s*([\s\S]*?)```/g;
|
|
41618
|
-
let match;
|
|
41619
|
-
while ((match = codeBlockRegex.exec(text)) !== null) {
|
|
41620
|
-
const content = match[1];
|
|
41621
|
-
if (content) {
|
|
41622
|
-
const trimmed = content.trim();
|
|
41623
|
-
try {
|
|
41624
|
-
const data = JSON.parse(trimmed);
|
|
41625
|
-
return {
|
|
41626
|
-
success: true,
|
|
41627
|
-
data,
|
|
41628
|
-
rawJson: trimmed,
|
|
41629
|
-
method: "code_block"
|
|
41630
|
-
};
|
|
41631
|
-
} catch {
|
|
41632
|
-
continue;
|
|
41633
|
-
}
|
|
41634
|
-
}
|
|
41635
|
-
}
|
|
41636
|
-
return { success: false };
|
|
41637
|
-
}
|
|
41638
|
-
function extractInlineJSON(text) {
|
|
41639
|
-
const objectMatch = findJSONObject(text);
|
|
41640
|
-
if (objectMatch) {
|
|
41641
|
-
try {
|
|
41642
|
-
const data = JSON.parse(objectMatch);
|
|
41643
|
-
return {
|
|
41644
|
-
success: true,
|
|
41645
|
-
data,
|
|
41646
|
-
rawJson: objectMatch,
|
|
41647
|
-
method: "inline"
|
|
41648
|
-
};
|
|
41649
|
-
} catch {
|
|
41650
|
-
}
|
|
41651
|
-
}
|
|
41652
|
-
const arrayMatch = findJSONArray(text);
|
|
41653
|
-
if (arrayMatch) {
|
|
41654
|
-
try {
|
|
41655
|
-
const data = JSON.parse(arrayMatch);
|
|
41656
|
-
return {
|
|
41657
|
-
success: true,
|
|
41658
|
-
data,
|
|
41659
|
-
rawJson: arrayMatch,
|
|
41660
|
-
method: "inline"
|
|
41661
|
-
};
|
|
41662
|
-
} catch {
|
|
41663
|
-
}
|
|
41664
|
-
}
|
|
41665
|
-
return { success: false };
|
|
41666
|
-
}
|
|
41667
|
-
function findJSONObject(text) {
|
|
41668
|
-
const startIndex = text.indexOf("{");
|
|
41669
|
-
if (startIndex === -1) return null;
|
|
41670
|
-
let depth = 0;
|
|
41671
|
-
let inString = false;
|
|
41672
|
-
let escaped = false;
|
|
41673
|
-
for (let i = startIndex; i < text.length; i++) {
|
|
41674
|
-
const char = text[i];
|
|
41675
|
-
if (escaped) {
|
|
41676
|
-
escaped = false;
|
|
41677
|
-
continue;
|
|
41678
|
-
}
|
|
41679
|
-
if (char === "\\" && inString) {
|
|
41680
|
-
escaped = true;
|
|
41681
|
-
continue;
|
|
41682
|
-
}
|
|
41683
|
-
if (char === '"') {
|
|
41684
|
-
inString = !inString;
|
|
41685
|
-
continue;
|
|
41686
|
-
}
|
|
41687
|
-
if (inString) continue;
|
|
41688
|
-
if (char === "{") {
|
|
41689
|
-
depth++;
|
|
41690
|
-
} else if (char === "}") {
|
|
41691
|
-
depth--;
|
|
41692
|
-
if (depth === 0) {
|
|
41693
|
-
return text.slice(startIndex, i + 1);
|
|
41694
|
-
}
|
|
41695
|
-
}
|
|
41696
|
-
}
|
|
41697
|
-
return null;
|
|
41698
|
-
}
|
|
41699
|
-
function findJSONArray(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 extractJSONField(text, field, defaultValue) {
|
|
41732
|
-
const result = extractJSON(text);
|
|
41733
|
-
if (result.success && result.data && field in result.data) {
|
|
41734
|
-
return result.data[field];
|
|
41735
|
-
}
|
|
41736
|
-
return defaultValue;
|
|
41737
|
-
}
|
|
41738
|
-
function extractNumber(text, patterns = [
|
|
41739
|
-
/(\d{1,3})%?\s*(?:complete|score|percent)/i,
|
|
41740
|
-
/(?:score|completion|rating)[:\s]+(\d{1,3})/i,
|
|
41741
|
-
/(\d{1,3})\s*(?:out of|\/)\s*100/i
|
|
41742
|
-
], defaultValue = 0) {
|
|
41743
|
-
const jsonResult = extractJSON(text);
|
|
41744
|
-
if (jsonResult.success && jsonResult.data) {
|
|
41745
|
-
const scoreFields = ["score", "completionScore", "completion_score", "rating", "percent", "value"];
|
|
41746
|
-
for (const field of scoreFields) {
|
|
41747
|
-
if (field in jsonResult.data && typeof jsonResult.data[field] === "number") {
|
|
41748
|
-
return jsonResult.data[field];
|
|
41749
|
-
}
|
|
41750
|
-
}
|
|
41751
|
-
}
|
|
41752
|
-
for (const pattern of patterns) {
|
|
41753
|
-
const match = text.match(pattern);
|
|
41754
|
-
if (match && match[1]) {
|
|
41755
|
-
const num = parseInt(match[1], 10);
|
|
41756
|
-
if (!isNaN(num)) {
|
|
41757
|
-
return num;
|
|
41758
|
-
}
|
|
41759
|
-
}
|
|
41760
|
-
}
|
|
41761
|
-
return defaultValue;
|
|
41762
|
-
}
|
|
41763
|
-
|
|
41764
43080
|
// src/tools/index.ts
|
|
41765
43081
|
var tools_exports = {};
|
|
41766
43082
|
__export(tools_exports, {
|
|
@@ -41796,19 +43112,25 @@ __export(tools_exports, {
|
|
|
41796
43112
|
createDesktopScreenshotTool: () => createDesktopScreenshotTool,
|
|
41797
43113
|
createDesktopWindowFocusTool: () => createDesktopWindowFocusTool,
|
|
41798
43114
|
createDesktopWindowListTool: () => createDesktopWindowListTool,
|
|
43115
|
+
createDraftEmailTool: () => createDraftEmailTool,
|
|
41799
43116
|
createEditFileTool: () => createEditFileTool,
|
|
43117
|
+
createEditMeetingTool: () => createEditMeetingTool,
|
|
41800
43118
|
createExecuteJavaScriptTool: () => createExecuteJavaScriptTool,
|
|
43119
|
+
createFindMeetingSlotsTool: () => createFindMeetingSlotsTool,
|
|
43120
|
+
createGetMeetingTranscriptTool: () => createGetMeetingTranscriptTool,
|
|
41801
43121
|
createGetPRTool: () => createGetPRTool,
|
|
41802
43122
|
createGitHubReadFileTool: () => createGitHubReadFileTool,
|
|
41803
43123
|
createGlobTool: () => createGlobTool,
|
|
41804
43124
|
createGrepTool: () => createGrepTool,
|
|
41805
43125
|
createImageGenerationTool: () => createImageGenerationTool,
|
|
41806
43126
|
createListDirectoryTool: () => createListDirectoryTool,
|
|
43127
|
+
createMeetingTool: () => createMeetingTool,
|
|
41807
43128
|
createPRCommentsTool: () => createPRCommentsTool,
|
|
41808
43129
|
createPRFilesTool: () => createPRFilesTool,
|
|
41809
43130
|
createReadFileTool: () => createReadFileTool,
|
|
41810
43131
|
createSearchCodeTool: () => createSearchCodeTool,
|
|
41811
43132
|
createSearchFilesTool: () => createSearchFilesTool,
|
|
43133
|
+
createSendEmailTool: () => createSendEmailTool,
|
|
41812
43134
|
createSpeechToTextTool: () => createSpeechToTextTool,
|
|
41813
43135
|
createTextToSpeechTool: () => createTextToSpeechTool,
|
|
41814
43136
|
createVideoTools: () => createVideoTools,
|
|
@@ -41838,6 +43160,8 @@ __export(tools_exports, {
|
|
|
41838
43160
|
executeInVM: () => executeInVM,
|
|
41839
43161
|
executeJavaScript: () => executeJavaScript,
|
|
41840
43162
|
expandTilde: () => expandTilde,
|
|
43163
|
+
formatAttendees: () => formatAttendees,
|
|
43164
|
+
formatRecipients: () => formatRecipients,
|
|
41841
43165
|
getAllBuiltInTools: () => getAllBuiltInTools,
|
|
41842
43166
|
getBackgroundOutput: () => getBackgroundOutput,
|
|
41843
43167
|
getDesktopDriver: () => getDesktopDriver,
|
|
@@ -41848,19 +43172,24 @@ __export(tools_exports, {
|
|
|
41848
43172
|
getToolRegistry: () => getToolRegistry,
|
|
41849
43173
|
getToolsByCategory: () => getToolsByCategory,
|
|
41850
43174
|
getToolsRequiringConnector: () => getToolsRequiringConnector,
|
|
43175
|
+
getUserPathPrefix: () => getUserPathPrefix,
|
|
41851
43176
|
glob: () => glob,
|
|
41852
43177
|
grep: () => grep,
|
|
41853
43178
|
hydrateCustomTool: () => hydrateCustomTool,
|
|
41854
43179
|
isBlockedCommand: () => isBlockedCommand,
|
|
41855
43180
|
isExcludedExtension: () => isExcludedExtension,
|
|
43181
|
+
isTeamsMeetingUrl: () => isTeamsMeetingUrl,
|
|
41856
43182
|
jsonManipulator: () => jsonManipulator,
|
|
41857
43183
|
killBackgroundProcess: () => killBackgroundProcess,
|
|
41858
43184
|
listDirectory: () => listDirectory,
|
|
41859
43185
|
mergeTextPieces: () => mergeTextPieces,
|
|
43186
|
+
microsoftFetch: () => microsoftFetch,
|
|
43187
|
+
normalizeEmails: () => normalizeEmails,
|
|
41860
43188
|
parseKeyCombo: () => parseKeyCombo,
|
|
41861
43189
|
parseRepository: () => parseRepository,
|
|
41862
43190
|
readFile: () => readFile5,
|
|
41863
43191
|
resetDefaultDriver: () => resetDefaultDriver,
|
|
43192
|
+
resolveMeetingId: () => resolveMeetingId,
|
|
41864
43193
|
resolveRepository: () => resolveRepository,
|
|
41865
43194
|
setMediaOutputHandler: () => setMediaOutputHandler,
|
|
41866
43195
|
setMediaStorage: () => setMediaStorage,
|
|
@@ -45942,6 +47271,840 @@ function registerGitHubTools() {
|
|
|
45942
47271
|
// src/tools/github/index.ts
|
|
45943
47272
|
registerGitHubTools();
|
|
45944
47273
|
|
|
47274
|
+
// src/tools/microsoft/types.ts
|
|
47275
|
+
function getUserPathPrefix(connector, targetUser) {
|
|
47276
|
+
const auth2 = connector.config.auth;
|
|
47277
|
+
if (auth2.type === "oauth" && auth2.flow === "client_credentials") {
|
|
47278
|
+
if (!targetUser) {
|
|
47279
|
+
throw new Error(
|
|
47280
|
+
'targetUser is required when using client_credentials (application) flow. Provide a user ID or UPN (e.g., "user@domain.com").'
|
|
47281
|
+
);
|
|
47282
|
+
}
|
|
47283
|
+
return `/users/${targetUser}`;
|
|
47284
|
+
}
|
|
47285
|
+
return "/me";
|
|
47286
|
+
}
|
|
47287
|
+
var MicrosoftAPIError = class extends Error {
|
|
47288
|
+
constructor(status, statusText, body) {
|
|
47289
|
+
const msg = typeof body === "object" && body !== null && "error" in body ? body.error?.message ?? statusText : statusText;
|
|
47290
|
+
super(`Microsoft Graph API error ${status}: ${msg}`);
|
|
47291
|
+
this.status = status;
|
|
47292
|
+
this.statusText = statusText;
|
|
47293
|
+
this.body = body;
|
|
47294
|
+
this.name = "MicrosoftAPIError";
|
|
47295
|
+
}
|
|
47296
|
+
};
|
|
47297
|
+
async function microsoftFetch(connector, endpoint, options) {
|
|
47298
|
+
let url2 = endpoint;
|
|
47299
|
+
if (options?.queryParams && Object.keys(options.queryParams).length > 0) {
|
|
47300
|
+
const params = new URLSearchParams();
|
|
47301
|
+
for (const [key, value] of Object.entries(options.queryParams)) {
|
|
47302
|
+
params.append(key, String(value));
|
|
47303
|
+
}
|
|
47304
|
+
url2 += (url2.includes("?") ? "&" : "?") + params.toString();
|
|
47305
|
+
}
|
|
47306
|
+
const headers = {
|
|
47307
|
+
"Accept": options?.accept ?? "application/json"
|
|
47308
|
+
};
|
|
47309
|
+
if (options?.body) {
|
|
47310
|
+
headers["Content-Type"] = "application/json";
|
|
47311
|
+
}
|
|
47312
|
+
const response = await connector.fetch(
|
|
47313
|
+
url2,
|
|
47314
|
+
{
|
|
47315
|
+
method: options?.method ?? "GET",
|
|
47316
|
+
headers,
|
|
47317
|
+
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
47318
|
+
},
|
|
47319
|
+
options?.userId
|
|
47320
|
+
);
|
|
47321
|
+
const text = await response.text();
|
|
47322
|
+
if (!response.ok) {
|
|
47323
|
+
let data;
|
|
47324
|
+
try {
|
|
47325
|
+
data = JSON.parse(text);
|
|
47326
|
+
} catch {
|
|
47327
|
+
data = text;
|
|
47328
|
+
}
|
|
47329
|
+
throw new MicrosoftAPIError(response.status, response.statusText, data);
|
|
47330
|
+
}
|
|
47331
|
+
if (!text || text.trim().length === 0) {
|
|
47332
|
+
return void 0;
|
|
47333
|
+
}
|
|
47334
|
+
try {
|
|
47335
|
+
return JSON.parse(text);
|
|
47336
|
+
} catch {
|
|
47337
|
+
return text;
|
|
47338
|
+
}
|
|
47339
|
+
}
|
|
47340
|
+
function normalizeEmails(input) {
|
|
47341
|
+
return input.map((item) => {
|
|
47342
|
+
if (typeof item === "string") return item;
|
|
47343
|
+
if (typeof item === "object" && item !== null) {
|
|
47344
|
+
const obj = item;
|
|
47345
|
+
if (obj.emailAddress && typeof obj.emailAddress === "object") {
|
|
47346
|
+
const ea = obj.emailAddress;
|
|
47347
|
+
if (typeof ea.address === "string") return ea.address;
|
|
47348
|
+
}
|
|
47349
|
+
if (typeof obj.address === "string") return obj.address;
|
|
47350
|
+
if (typeof obj.email === "string") return obj.email;
|
|
47351
|
+
}
|
|
47352
|
+
return String(item);
|
|
47353
|
+
});
|
|
47354
|
+
}
|
|
47355
|
+
function formatRecipients(emails) {
|
|
47356
|
+
return normalizeEmails(emails).map((address) => ({ emailAddress: { address } }));
|
|
47357
|
+
}
|
|
47358
|
+
function formatAttendees(emails) {
|
|
47359
|
+
return normalizeEmails(emails).map((address) => ({
|
|
47360
|
+
emailAddress: { address },
|
|
47361
|
+
type: "required"
|
|
47362
|
+
}));
|
|
47363
|
+
}
|
|
47364
|
+
function isTeamsMeetingUrl(input) {
|
|
47365
|
+
try {
|
|
47366
|
+
const url2 = new URL(input.trim());
|
|
47367
|
+
return (url2.hostname === "teams.microsoft.com" || url2.hostname === "teams.live.com") && url2.pathname.includes("meetup-join");
|
|
47368
|
+
} catch {
|
|
47369
|
+
return false;
|
|
47370
|
+
}
|
|
47371
|
+
}
|
|
47372
|
+
async function resolveMeetingId(connector, input, prefix, effectiveUserId) {
|
|
47373
|
+
if (!input || input.trim().length === 0) {
|
|
47374
|
+
throw new Error("Meeting ID cannot be empty");
|
|
47375
|
+
}
|
|
47376
|
+
const trimmed = input.trim();
|
|
47377
|
+
if (!isTeamsMeetingUrl(trimmed)) {
|
|
47378
|
+
return { meetingId: trimmed };
|
|
47379
|
+
}
|
|
47380
|
+
const meetings = await microsoftFetch(
|
|
47381
|
+
connector,
|
|
47382
|
+
`${prefix}/onlineMeetings`,
|
|
47383
|
+
{
|
|
47384
|
+
userId: effectiveUserId,
|
|
47385
|
+
queryParams: { "$filter": `JoinWebUrl eq '${trimmed}'` }
|
|
47386
|
+
}
|
|
47387
|
+
);
|
|
47388
|
+
if (!meetings.value || meetings.value.length === 0) {
|
|
47389
|
+
throw new Error(
|
|
47390
|
+
`Could not find an online meeting matching the provided Teams URL. Make sure the URL is correct and you have access to this meeting.`
|
|
47391
|
+
);
|
|
47392
|
+
}
|
|
47393
|
+
return {
|
|
47394
|
+
meetingId: meetings.value[0].id,
|
|
47395
|
+
subject: meetings.value[0].subject
|
|
47396
|
+
};
|
|
47397
|
+
}
|
|
47398
|
+
|
|
47399
|
+
// src/tools/microsoft/createDraftEmail.ts
|
|
47400
|
+
function createDraftEmailTool(connector, userId) {
|
|
47401
|
+
return {
|
|
47402
|
+
definition: {
|
|
47403
|
+
type: "function",
|
|
47404
|
+
function: {
|
|
47405
|
+
name: "create_draft_email",
|
|
47406
|
+
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.
|
|
47407
|
+
|
|
47408
|
+
PARAMETER FORMATS:
|
|
47409
|
+
- to/cc: plain string array of email addresses. Example: ["alice@contoso.com", "bob@contoso.com"]. Do NOT use objects.
|
|
47410
|
+
- subject: plain string. Example: "Project update" or "Re: Project update" for replies.
|
|
47411
|
+
- body: HTML string. Example: "<p>Hi Alice,</p><p>Here is the update.</p>". Use <p>, <br>, <b>, <ul> tags for formatting.
|
|
47412
|
+
- replyToMessageId: Graph message ID string (starts with "AAMk..."). Only set when replying to an existing email.
|
|
47413
|
+
|
|
47414
|
+
EXAMPLES:
|
|
47415
|
+
- New draft: { "to": ["alice@contoso.com"], "subject": "Project update", "body": "<p>Hi Alice,</p><p>Here is the update.</p>" }
|
|
47416
|
+
- Reply draft: { "to": ["alice@contoso.com"], "subject": "Re: Project update", "body": "<p>Thanks!</p>", "replyToMessageId": "AAMkADI1..." }
|
|
47417
|
+
- With CC: { "to": ["alice@contoso.com"], "subject": "Notes", "body": "<p>See attached.</p>", "cc": ["bob@contoso.com"] }`,
|
|
47418
|
+
parameters: {
|
|
47419
|
+
type: "object",
|
|
47420
|
+
properties: {
|
|
47421
|
+
to: {
|
|
47422
|
+
type: "array",
|
|
47423
|
+
items: { type: "string" },
|
|
47424
|
+
description: 'Recipient email addresses as plain strings. Example: ["alice@contoso.com", "bob@contoso.com"]'
|
|
47425
|
+
},
|
|
47426
|
+
subject: {
|
|
47427
|
+
type: "string",
|
|
47428
|
+
description: 'Email subject as plain string. Example: "Project update" or "Re: Original subject" for replies.'
|
|
47429
|
+
},
|
|
47430
|
+
body: {
|
|
47431
|
+
type: "string",
|
|
47432
|
+
description: 'Email body as an HTML string. Example: "<p>Hello!</p><p>See you tomorrow.</p>"'
|
|
47433
|
+
},
|
|
47434
|
+
cc: {
|
|
47435
|
+
type: "array",
|
|
47436
|
+
items: { type: "string" },
|
|
47437
|
+
description: 'CC email addresses as plain strings. Example: ["bob@contoso.com"]. Optional.'
|
|
47438
|
+
},
|
|
47439
|
+
replyToMessageId: {
|
|
47440
|
+
type: "string",
|
|
47441
|
+
description: 'Graph message ID of the email to reply to. Example: "AAMkADI1M2I3YzgtODg...". When set, creates a threaded reply draft.'
|
|
47442
|
+
},
|
|
47443
|
+
targetUser: {
|
|
47444
|
+
type: "string",
|
|
47445
|
+
description: 'User ID or email (UPN) for app-only auth. Example: "alice@contoso.com". Ignored in delegated auth.'
|
|
47446
|
+
}
|
|
47447
|
+
},
|
|
47448
|
+
required: ["to", "subject", "body"]
|
|
47449
|
+
}
|
|
47450
|
+
}
|
|
47451
|
+
},
|
|
47452
|
+
describeCall: (args) => {
|
|
47453
|
+
const action = args.replyToMessageId ? "Reply draft" : "Draft";
|
|
47454
|
+
return `${action} to ${args.to.join(", ")}: ${args.subject}`;
|
|
47455
|
+
},
|
|
47456
|
+
permission: {
|
|
47457
|
+
scope: "session",
|
|
47458
|
+
riskLevel: "medium",
|
|
47459
|
+
approvalMessage: `Create a draft email via ${connector.displayName}`
|
|
47460
|
+
},
|
|
47461
|
+
execute: async (args, context) => {
|
|
47462
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
47463
|
+
try {
|
|
47464
|
+
const prefix = getUserPathPrefix(connector, args.targetUser);
|
|
47465
|
+
if (args.replyToMessageId) {
|
|
47466
|
+
const replyDraft = await microsoftFetch(
|
|
47467
|
+
connector,
|
|
47468
|
+
`${prefix}/messages/${args.replyToMessageId}/createReply`,
|
|
47469
|
+
{ method: "POST", userId: effectiveUserId, body: {} }
|
|
47470
|
+
);
|
|
47471
|
+
const updated = await microsoftFetch(
|
|
47472
|
+
connector,
|
|
47473
|
+
`${prefix}/messages/${replyDraft.id}`,
|
|
47474
|
+
{
|
|
47475
|
+
method: "PATCH",
|
|
47476
|
+
userId: effectiveUserId,
|
|
47477
|
+
body: {
|
|
47478
|
+
subject: args.subject,
|
|
47479
|
+
body: { contentType: "HTML", content: args.body },
|
|
47480
|
+
toRecipients: formatRecipients(args.to),
|
|
47481
|
+
...args.cc && { ccRecipients: formatRecipients(args.cc) }
|
|
47482
|
+
}
|
|
47483
|
+
}
|
|
47484
|
+
);
|
|
47485
|
+
return {
|
|
47486
|
+
success: true,
|
|
47487
|
+
draftId: updated.id,
|
|
47488
|
+
webLink: updated.webLink
|
|
47489
|
+
};
|
|
47490
|
+
}
|
|
47491
|
+
const draft = await microsoftFetch(
|
|
47492
|
+
connector,
|
|
47493
|
+
`${prefix}/messages`,
|
|
47494
|
+
{
|
|
47495
|
+
method: "POST",
|
|
47496
|
+
userId: effectiveUserId,
|
|
47497
|
+
body: {
|
|
47498
|
+
isDraft: true,
|
|
47499
|
+
subject: args.subject,
|
|
47500
|
+
body: { contentType: "HTML", content: args.body },
|
|
47501
|
+
toRecipients: formatRecipients(args.to),
|
|
47502
|
+
...args.cc && { ccRecipients: formatRecipients(args.cc) }
|
|
47503
|
+
}
|
|
47504
|
+
}
|
|
47505
|
+
);
|
|
47506
|
+
return {
|
|
47507
|
+
success: true,
|
|
47508
|
+
draftId: draft.id,
|
|
47509
|
+
webLink: draft.webLink
|
|
47510
|
+
};
|
|
47511
|
+
} catch (error) {
|
|
47512
|
+
return {
|
|
47513
|
+
success: false,
|
|
47514
|
+
error: `Failed to create draft email: ${error instanceof Error ? error.message : String(error)}`
|
|
47515
|
+
};
|
|
47516
|
+
}
|
|
47517
|
+
}
|
|
47518
|
+
};
|
|
47519
|
+
}
|
|
47520
|
+
|
|
47521
|
+
// src/tools/microsoft/sendEmail.ts
|
|
47522
|
+
function createSendEmailTool(connector, userId) {
|
|
47523
|
+
return {
|
|
47524
|
+
definition: {
|
|
47525
|
+
type: "function",
|
|
47526
|
+
function: {
|
|
47527
|
+
name: "send_email",
|
|
47528
|
+
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.
|
|
47529
|
+
|
|
47530
|
+
PARAMETER FORMATS:
|
|
47531
|
+
- to/cc: plain string array of email addresses. Example: ["alice@contoso.com", "bob@contoso.com"]. Do NOT use objects.
|
|
47532
|
+
- subject: plain string. Example: "Meeting tomorrow" or "Re: Meeting tomorrow" for replies.
|
|
47533
|
+
- body: HTML string. Example: "<p>Hi Alice,</p><p>Can we meet at 2pm?</p>". Use <p>, <br>, <b>, <ul> tags.
|
|
47534
|
+
- replyToMessageId: Graph message ID string (starts with "AAMk..."). Only set when replying to an existing email.
|
|
47535
|
+
|
|
47536
|
+
EXAMPLES:
|
|
47537
|
+
- Send email: { "to": ["alice@contoso.com"], "subject": "Meeting tomorrow", "body": "<p>Can we meet at 2pm?</p>" }
|
|
47538
|
+
- Reply: { "to": ["alice@contoso.com"], "subject": "Re: Meeting", "body": "<p>Confirmed!</p>", "replyToMessageId": "AAMkADI1..." }
|
|
47539
|
+
- With CC: { "to": ["alice@contoso.com"], "subject": "Update", "body": "<p>FYI</p>", "cc": ["bob@contoso.com"] }`,
|
|
47540
|
+
parameters: {
|
|
47541
|
+
type: "object",
|
|
47542
|
+
properties: {
|
|
47543
|
+
to: {
|
|
47544
|
+
type: "array",
|
|
47545
|
+
items: { type: "string" },
|
|
47546
|
+
description: 'Recipient email addresses as plain strings. Example: ["alice@contoso.com", "bob@contoso.com"]'
|
|
47547
|
+
},
|
|
47548
|
+
subject: {
|
|
47549
|
+
type: "string",
|
|
47550
|
+
description: 'Email subject as plain string. Example: "Meeting tomorrow" or "Re: Original subject" for replies.'
|
|
47551
|
+
},
|
|
47552
|
+
body: {
|
|
47553
|
+
type: "string",
|
|
47554
|
+
description: 'Email body as an HTML string. Example: "<p>Hi!</p><p>Can we meet at 2pm?</p>"'
|
|
47555
|
+
},
|
|
47556
|
+
cc: {
|
|
47557
|
+
type: "array",
|
|
47558
|
+
items: { type: "string" },
|
|
47559
|
+
description: 'CC email addresses as plain strings. Example: ["bob@contoso.com"]. Optional.'
|
|
47560
|
+
},
|
|
47561
|
+
replyToMessageId: {
|
|
47562
|
+
type: "string",
|
|
47563
|
+
description: 'Graph message ID of the email to reply to. Example: "AAMkADI1M2I3YzgtODg...". When set, sends a threaded reply.'
|
|
47564
|
+
},
|
|
47565
|
+
targetUser: {
|
|
47566
|
+
type: "string",
|
|
47567
|
+
description: 'User ID or email (UPN) for app-only auth. Example: "alice@contoso.com". Ignored in delegated auth.'
|
|
47568
|
+
}
|
|
47569
|
+
},
|
|
47570
|
+
required: ["to", "subject", "body"]
|
|
47571
|
+
}
|
|
47572
|
+
}
|
|
47573
|
+
},
|
|
47574
|
+
describeCall: (args) => {
|
|
47575
|
+
const action = args.replyToMessageId ? "Reply" : "Send";
|
|
47576
|
+
return `${action} to ${args.to.join(", ")}: ${args.subject}`;
|
|
47577
|
+
},
|
|
47578
|
+
permission: {
|
|
47579
|
+
scope: "session",
|
|
47580
|
+
riskLevel: "medium",
|
|
47581
|
+
approvalMessage: `Send an email via ${connector.displayName}`
|
|
47582
|
+
},
|
|
47583
|
+
execute: async (args, context) => {
|
|
47584
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
47585
|
+
try {
|
|
47586
|
+
const prefix = getUserPathPrefix(connector, args.targetUser);
|
|
47587
|
+
if (args.replyToMessageId) {
|
|
47588
|
+
await microsoftFetch(
|
|
47589
|
+
connector,
|
|
47590
|
+
`${prefix}/messages/${args.replyToMessageId}/reply`,
|
|
47591
|
+
{
|
|
47592
|
+
method: "POST",
|
|
47593
|
+
userId: effectiveUserId,
|
|
47594
|
+
body: {
|
|
47595
|
+
message: {
|
|
47596
|
+
toRecipients: formatRecipients(args.to),
|
|
47597
|
+
...args.cc && { ccRecipients: formatRecipients(args.cc) }
|
|
47598
|
+
},
|
|
47599
|
+
comment: args.body
|
|
47600
|
+
}
|
|
47601
|
+
}
|
|
47602
|
+
);
|
|
47603
|
+
} else {
|
|
47604
|
+
await microsoftFetch(
|
|
47605
|
+
connector,
|
|
47606
|
+
`${prefix}/sendMail`,
|
|
47607
|
+
{
|
|
47608
|
+
method: "POST",
|
|
47609
|
+
userId: effectiveUserId,
|
|
47610
|
+
body: {
|
|
47611
|
+
message: {
|
|
47612
|
+
subject: args.subject,
|
|
47613
|
+
body: { contentType: "HTML", content: args.body },
|
|
47614
|
+
toRecipients: formatRecipients(args.to),
|
|
47615
|
+
...args.cc && { ccRecipients: formatRecipients(args.cc) }
|
|
47616
|
+
},
|
|
47617
|
+
saveToSentItems: true
|
|
47618
|
+
}
|
|
47619
|
+
}
|
|
47620
|
+
);
|
|
47621
|
+
}
|
|
47622
|
+
return { success: true };
|
|
47623
|
+
} catch (error) {
|
|
47624
|
+
return {
|
|
47625
|
+
success: false,
|
|
47626
|
+
error: `Failed to send email: ${error instanceof Error ? error.message : String(error)}`
|
|
47627
|
+
};
|
|
47628
|
+
}
|
|
47629
|
+
}
|
|
47630
|
+
};
|
|
47631
|
+
}
|
|
47632
|
+
|
|
47633
|
+
// src/tools/microsoft/createMeeting.ts
|
|
47634
|
+
function createMeetingTool(connector, userId) {
|
|
47635
|
+
return {
|
|
47636
|
+
definition: {
|
|
47637
|
+
type: "function",
|
|
47638
|
+
function: {
|
|
47639
|
+
name: "create_meeting",
|
|
47640
|
+
description: `Create a calendar event on the user's Outlook calendar via Microsoft Graph, optionally with a Teams online meeting link.
|
|
47641
|
+
|
|
47642
|
+
PARAMETER FORMATS:
|
|
47643
|
+
- subject: plain string. Example: "Sprint Review"
|
|
47644
|
+
- startDateTime/endDateTime: ISO 8601 string WITHOUT timezone suffix (timezone is a separate param). Example: "2025-01-15T09:00:00"
|
|
47645
|
+
- attendees: plain string array of email addresses. Example: ["alice@contoso.com", "bob@contoso.com"]. Do NOT use objects.
|
|
47646
|
+
- body: HTML string for the invitation body. Example: "<p>Agenda: discuss Q1 goals</p>". Optional.
|
|
47647
|
+
- timeZone: IANA timezone string. Example: "America/New_York", "Europe/Zurich". Default: "UTC".
|
|
47648
|
+
- isOnlineMeeting: boolean. Set true to auto-generate a Teams meeting link.
|
|
47649
|
+
- location: plain string. Example: "Conference Room A". Optional.
|
|
47650
|
+
|
|
47651
|
+
EXAMPLES:
|
|
47652
|
+
- Simple: { "subject": "Standup", "startDateTime": "2025-01-15T09:00:00", "endDateTime": "2025-01-15T09:30:00", "attendees": ["alice@contoso.com"], "timeZone": "America/New_York" }
|
|
47653
|
+
- 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 }`,
|
|
47654
|
+
parameters: {
|
|
47655
|
+
type: "object",
|
|
47656
|
+
properties: {
|
|
47657
|
+
subject: {
|
|
47658
|
+
type: "string",
|
|
47659
|
+
description: 'Meeting title as plain string. Example: "Sprint Review"'
|
|
47660
|
+
},
|
|
47661
|
+
startDateTime: {
|
|
47662
|
+
type: "string",
|
|
47663
|
+
description: 'Start date/time as ISO 8601 string without timezone suffix. Example: "2025-01-15T09:00:00"'
|
|
47664
|
+
},
|
|
47665
|
+
endDateTime: {
|
|
47666
|
+
type: "string",
|
|
47667
|
+
description: 'End date/time as ISO 8601 string without timezone suffix. Example: "2025-01-15T09:30:00"'
|
|
47668
|
+
},
|
|
47669
|
+
attendees: {
|
|
47670
|
+
type: "array",
|
|
47671
|
+
items: { type: "string" },
|
|
47672
|
+
description: 'Attendee email addresses as plain strings. Example: ["alice@contoso.com", "bob@contoso.com"]'
|
|
47673
|
+
},
|
|
47674
|
+
body: {
|
|
47675
|
+
type: "string",
|
|
47676
|
+
description: 'Meeting description as HTML string. Example: "<p>Agenda: discuss Q1 goals</p>". Optional.'
|
|
47677
|
+
},
|
|
47678
|
+
isOnlineMeeting: {
|
|
47679
|
+
type: "boolean",
|
|
47680
|
+
description: "Set to true to generate a Teams online meeting link. Default: false."
|
|
47681
|
+
},
|
|
47682
|
+
location: {
|
|
47683
|
+
type: "string",
|
|
47684
|
+
description: 'Physical location as plain string. Example: "Conference Room A". Optional.'
|
|
47685
|
+
},
|
|
47686
|
+
timeZone: {
|
|
47687
|
+
type: "string",
|
|
47688
|
+
description: 'IANA timezone string for start/end times. Example: "America/New_York", "Europe/Zurich". Default: "UTC".'
|
|
47689
|
+
},
|
|
47690
|
+
targetUser: {
|
|
47691
|
+
type: "string",
|
|
47692
|
+
description: 'User ID or email (UPN) for app-only auth. Example: "alice@contoso.com". Ignored in delegated auth.'
|
|
47693
|
+
}
|
|
47694
|
+
},
|
|
47695
|
+
required: ["subject", "startDateTime", "endDateTime", "attendees"]
|
|
47696
|
+
}
|
|
47697
|
+
}
|
|
47698
|
+
},
|
|
47699
|
+
describeCall: (args) => {
|
|
47700
|
+
return `Create meeting: ${args.subject} (${args.attendees.length} attendees)`;
|
|
47701
|
+
},
|
|
47702
|
+
permission: {
|
|
47703
|
+
scope: "session",
|
|
47704
|
+
riskLevel: "medium",
|
|
47705
|
+
approvalMessage: `Create a calendar event via ${connector.displayName}`
|
|
47706
|
+
},
|
|
47707
|
+
execute: async (args, context) => {
|
|
47708
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
47709
|
+
try {
|
|
47710
|
+
const prefix = getUserPathPrefix(connector, args.targetUser);
|
|
47711
|
+
const tz = args.timeZone ?? "UTC";
|
|
47712
|
+
const eventBody = {
|
|
47713
|
+
subject: args.subject,
|
|
47714
|
+
start: { dateTime: args.startDateTime, timeZone: tz },
|
|
47715
|
+
end: { dateTime: args.endDateTime, timeZone: tz },
|
|
47716
|
+
attendees: formatAttendees(args.attendees)
|
|
47717
|
+
};
|
|
47718
|
+
if (args.body) {
|
|
47719
|
+
eventBody.body = { contentType: "HTML", content: args.body };
|
|
47720
|
+
}
|
|
47721
|
+
if (args.isOnlineMeeting) {
|
|
47722
|
+
eventBody.isOnlineMeeting = true;
|
|
47723
|
+
eventBody.onlineMeetingProvider = "teamsForBusiness";
|
|
47724
|
+
}
|
|
47725
|
+
if (args.location) {
|
|
47726
|
+
eventBody.location = { displayName: args.location };
|
|
47727
|
+
}
|
|
47728
|
+
const event = await microsoftFetch(
|
|
47729
|
+
connector,
|
|
47730
|
+
`${prefix}/events`,
|
|
47731
|
+
{ method: "POST", userId: effectiveUserId, body: eventBody }
|
|
47732
|
+
);
|
|
47733
|
+
return {
|
|
47734
|
+
success: true,
|
|
47735
|
+
eventId: event.id,
|
|
47736
|
+
webLink: event.webLink,
|
|
47737
|
+
onlineMeetingUrl: event.onlineMeeting?.joinUrl
|
|
47738
|
+
};
|
|
47739
|
+
} catch (error) {
|
|
47740
|
+
return {
|
|
47741
|
+
success: false,
|
|
47742
|
+
error: `Failed to create meeting: ${error instanceof Error ? error.message : String(error)}`
|
|
47743
|
+
};
|
|
47744
|
+
}
|
|
47745
|
+
}
|
|
47746
|
+
};
|
|
47747
|
+
}
|
|
47748
|
+
|
|
47749
|
+
// src/tools/microsoft/editMeeting.ts
|
|
47750
|
+
function createEditMeetingTool(connector, userId) {
|
|
47751
|
+
return {
|
|
47752
|
+
definition: {
|
|
47753
|
+
type: "function",
|
|
47754
|
+
function: {
|
|
47755
|
+
name: "edit_meeting",
|
|
47756
|
+
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.
|
|
47757
|
+
|
|
47758
|
+
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.
|
|
47759
|
+
|
|
47760
|
+
PARAMETER FORMATS:
|
|
47761
|
+
- eventId: Graph event ID string (starts with "AAMk..."). Get this from a previous create_meeting result.
|
|
47762
|
+
- subject: plain string. Example: "Updated: Sprint Review"
|
|
47763
|
+
- startDateTime/endDateTime: ISO 8601 string without timezone suffix. Example: "2025-01-15T10:00:00"
|
|
47764
|
+
- attendees: plain string array of email addresses. Example: ["alice@contoso.com", "charlie@contoso.com"]. Do NOT use objects. REPLACES all attendees.
|
|
47765
|
+
- body: HTML string. Example: "<p>Updated agenda</p>"
|
|
47766
|
+
- timeZone: IANA timezone string. Example: "Europe/Zurich". Default: "UTC".
|
|
47767
|
+
- isOnlineMeeting: boolean. true = add Teams link, false = remove it.
|
|
47768
|
+
- location: plain string. Example: "Room 201"
|
|
47769
|
+
|
|
47770
|
+
EXAMPLES:
|
|
47771
|
+
- Reschedule: { "eventId": "AAMkADI1...", "startDateTime": "2025-01-15T10:00:00", "endDateTime": "2025-01-15T10:30:00", "timeZone": "America/New_York" }
|
|
47772
|
+
- Change attendees: { "eventId": "AAMkADI1...", "attendees": ["alice@contoso.com", "charlie@contoso.com"] }
|
|
47773
|
+
- Add Teams link: { "eventId": "AAMkADI1...", "isOnlineMeeting": true }`,
|
|
47774
|
+
parameters: {
|
|
47775
|
+
type: "object",
|
|
47776
|
+
properties: {
|
|
47777
|
+
eventId: {
|
|
47778
|
+
type: "string",
|
|
47779
|
+
description: 'Calendar event ID string from create_meeting result. Example: "AAMkADI1M2I3YzgtODg..."'
|
|
47780
|
+
},
|
|
47781
|
+
subject: {
|
|
47782
|
+
type: "string",
|
|
47783
|
+
description: 'New meeting title as plain string. Example: "Updated: Sprint Review"'
|
|
47784
|
+
},
|
|
47785
|
+
startDateTime: {
|
|
47786
|
+
type: "string",
|
|
47787
|
+
description: 'New start date/time as ISO 8601 string without timezone suffix. Example: "2025-01-15T10:00:00"'
|
|
47788
|
+
},
|
|
47789
|
+
endDateTime: {
|
|
47790
|
+
type: "string",
|
|
47791
|
+
description: 'New end date/time as ISO 8601 string without timezone suffix. Example: "2025-01-15T10:30:00"'
|
|
47792
|
+
},
|
|
47793
|
+
attendees: {
|
|
47794
|
+
type: "array",
|
|
47795
|
+
items: { type: "string" },
|
|
47796
|
+
description: 'FULL replacement attendee list as plain email strings. Example: ["alice@contoso.com", "charlie@contoso.com"]. Include ALL attendees.'
|
|
47797
|
+
},
|
|
47798
|
+
body: {
|
|
47799
|
+
type: "string",
|
|
47800
|
+
description: 'New meeting description as HTML string. Example: "<p>Updated agenda</p>"'
|
|
47801
|
+
},
|
|
47802
|
+
isOnlineMeeting: {
|
|
47803
|
+
type: "boolean",
|
|
47804
|
+
description: "true to add Teams meeting link, false to remove it."
|
|
47805
|
+
},
|
|
47806
|
+
location: {
|
|
47807
|
+
type: "string",
|
|
47808
|
+
description: 'New location as plain string. Example: "Conference Room A"'
|
|
47809
|
+
},
|
|
47810
|
+
timeZone: {
|
|
47811
|
+
type: "string",
|
|
47812
|
+
description: 'IANA timezone string for start/end times. Example: "Europe/Zurich". Default: "UTC".'
|
|
47813
|
+
},
|
|
47814
|
+
targetUser: {
|
|
47815
|
+
type: "string",
|
|
47816
|
+
description: 'User ID or email (UPN) for app-only auth. Example: "alice@contoso.com". Ignored in delegated auth.'
|
|
47817
|
+
}
|
|
47818
|
+
},
|
|
47819
|
+
required: ["eventId"]
|
|
47820
|
+
}
|
|
47821
|
+
}
|
|
47822
|
+
},
|
|
47823
|
+
describeCall: (args) => {
|
|
47824
|
+
const fields = ["subject", "startDateTime", "endDateTime", "attendees", "body", "location"];
|
|
47825
|
+
const changed = fields.filter((f) => args[f] !== void 0);
|
|
47826
|
+
return `Edit meeting ${args.eventId.slice(0, 12)}... (${changed.join(", ") || "no changes"})`;
|
|
47827
|
+
},
|
|
47828
|
+
permission: {
|
|
47829
|
+
scope: "session",
|
|
47830
|
+
riskLevel: "medium",
|
|
47831
|
+
approvalMessage: `Update a calendar event via ${connector.displayName}`
|
|
47832
|
+
},
|
|
47833
|
+
execute: async (args, context) => {
|
|
47834
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
47835
|
+
try {
|
|
47836
|
+
const prefix = getUserPathPrefix(connector, args.targetUser);
|
|
47837
|
+
const tz = args.timeZone ?? "UTC";
|
|
47838
|
+
const patchBody = {};
|
|
47839
|
+
if (args.subject !== void 0) patchBody.subject = args.subject;
|
|
47840
|
+
if (args.body !== void 0) patchBody.body = { contentType: "HTML", content: args.body };
|
|
47841
|
+
if (args.startDateTime !== void 0) patchBody.start = { dateTime: args.startDateTime, timeZone: tz };
|
|
47842
|
+
if (args.endDateTime !== void 0) patchBody.end = { dateTime: args.endDateTime, timeZone: tz };
|
|
47843
|
+
if (args.attendees !== void 0) {
|
|
47844
|
+
patchBody.attendees = formatAttendees(args.attendees);
|
|
47845
|
+
}
|
|
47846
|
+
if (args.isOnlineMeeting !== void 0) {
|
|
47847
|
+
patchBody.isOnlineMeeting = args.isOnlineMeeting;
|
|
47848
|
+
if (args.isOnlineMeeting) {
|
|
47849
|
+
patchBody.onlineMeetingProvider = "teamsForBusiness";
|
|
47850
|
+
}
|
|
47851
|
+
}
|
|
47852
|
+
if (args.location !== void 0) {
|
|
47853
|
+
patchBody.location = { displayName: args.location };
|
|
47854
|
+
}
|
|
47855
|
+
const event = await microsoftFetch(
|
|
47856
|
+
connector,
|
|
47857
|
+
`${prefix}/events/${args.eventId}`,
|
|
47858
|
+
{ method: "PATCH", userId: effectiveUserId, body: patchBody }
|
|
47859
|
+
);
|
|
47860
|
+
return {
|
|
47861
|
+
success: true,
|
|
47862
|
+
eventId: event.id,
|
|
47863
|
+
webLink: event.webLink
|
|
47864
|
+
};
|
|
47865
|
+
} catch (error) {
|
|
47866
|
+
return {
|
|
47867
|
+
success: false,
|
|
47868
|
+
error: `Failed to edit meeting: ${error instanceof Error ? error.message : String(error)}`
|
|
47869
|
+
};
|
|
47870
|
+
}
|
|
47871
|
+
}
|
|
47872
|
+
};
|
|
47873
|
+
}
|
|
47874
|
+
|
|
47875
|
+
// src/tools/microsoft/getMeetingTranscript.ts
|
|
47876
|
+
function parseVttToText(vtt) {
|
|
47877
|
+
const lines = vtt.split("\n");
|
|
47878
|
+
const textLines = [];
|
|
47879
|
+
for (const line of lines) {
|
|
47880
|
+
const trimmed = line.trim();
|
|
47881
|
+
if (trimmed === "" || trimmed === "WEBVTT" || trimmed.startsWith("NOTE") || /^\d+$/.test(trimmed) || /^\d{2}:\d{2}/.test(trimmed)) {
|
|
47882
|
+
continue;
|
|
47883
|
+
}
|
|
47884
|
+
textLines.push(trimmed);
|
|
47885
|
+
}
|
|
47886
|
+
return textLines.join("\n");
|
|
47887
|
+
}
|
|
47888
|
+
function createGetMeetingTranscriptTool(connector, userId) {
|
|
47889
|
+
return {
|
|
47890
|
+
definition: {
|
|
47891
|
+
type: "function",
|
|
47892
|
+
function: {
|
|
47893
|
+
name: "get_meeting_transcript",
|
|
47894
|
+
description: `Retrieve the transcript from a Teams online meeting via Microsoft Graph. Returns plain text with speaker labels (VTT timestamps are stripped).
|
|
47895
|
+
|
|
47896
|
+
NOTE: Requires the OnlineMeetingTranscript.Read.All permission. Transcription must have been enabled during the meeting.
|
|
47897
|
+
|
|
47898
|
+
USAGE:
|
|
47899
|
+
- Provide the Teams online meeting ID (NOT the calendar event ID \u2014 this is different) or a Teams meeting join URL
|
|
47900
|
+
- The meetingId can be found in the Teams meeting details or extracted from the join URL
|
|
47901
|
+
|
|
47902
|
+
EXAMPLES:
|
|
47903
|
+
- By meeting ID: { "meetingId": "MSo1N2Y5ZGFjYy03MWJmLTQ3NDMtYjQxMy01M2EdFGkdRWHJlQ" }
|
|
47904
|
+
- By Teams join URL: { "meetingId": "https://teams.microsoft.com/l/meetup-join/19%3ameeting_MjA5YjFi..." }`,
|
|
47905
|
+
parameters: {
|
|
47906
|
+
type: "object",
|
|
47907
|
+
properties: {
|
|
47908
|
+
meetingId: {
|
|
47909
|
+
type: "string",
|
|
47910
|
+
description: 'Teams online meeting ID (e.g. "MSo1N2Y5...") or Teams meeting join URL. This is NOT the calendar event ID.'
|
|
47911
|
+
},
|
|
47912
|
+
targetUser: {
|
|
47913
|
+
type: "string",
|
|
47914
|
+
description: "User ID or email (UPN) to act on behalf of. Only needed for app-only (client_credentials) auth. Ignored in delegated auth."
|
|
47915
|
+
}
|
|
47916
|
+
},
|
|
47917
|
+
required: ["meetingId"]
|
|
47918
|
+
}
|
|
47919
|
+
}
|
|
47920
|
+
},
|
|
47921
|
+
describeCall: (args) => {
|
|
47922
|
+
return `Get transcript for meeting ${args.meetingId.slice(0, 20)}...`;
|
|
47923
|
+
},
|
|
47924
|
+
permission: {
|
|
47925
|
+
scope: "session",
|
|
47926
|
+
riskLevel: "low",
|
|
47927
|
+
approvalMessage: `Get a meeting transcript via ${connector.displayName}`
|
|
47928
|
+
},
|
|
47929
|
+
execute: async (args, context) => {
|
|
47930
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
47931
|
+
try {
|
|
47932
|
+
const prefix = getUserPathPrefix(connector, args.targetUser);
|
|
47933
|
+
const resolved = await resolveMeetingId(connector, args.meetingId, prefix, effectiveUserId);
|
|
47934
|
+
const meetingId = resolved.meetingId;
|
|
47935
|
+
const transcriptList = await microsoftFetch(
|
|
47936
|
+
connector,
|
|
47937
|
+
`${prefix}/onlineMeetings/${meetingId}/transcripts`,
|
|
47938
|
+
{ userId: effectiveUserId }
|
|
47939
|
+
);
|
|
47940
|
+
if (!transcriptList.value || transcriptList.value.length === 0) {
|
|
47941
|
+
return {
|
|
47942
|
+
success: false,
|
|
47943
|
+
error: "No transcripts found for this meeting. The meeting may not have had transcription enabled."
|
|
47944
|
+
};
|
|
47945
|
+
}
|
|
47946
|
+
const transcriptId = transcriptList.value[0].id;
|
|
47947
|
+
const contentUrl = `${prefix}/onlineMeetings/${meetingId}/transcripts/${transcriptId}/content`;
|
|
47948
|
+
const response = await connector.fetch(
|
|
47949
|
+
contentUrl + "?$format=text/vtt",
|
|
47950
|
+
{ method: "GET", headers: { "Accept": "text/vtt" } },
|
|
47951
|
+
effectiveUserId
|
|
47952
|
+
);
|
|
47953
|
+
if (!response.ok) {
|
|
47954
|
+
const errorText = await response.text();
|
|
47955
|
+
return {
|
|
47956
|
+
success: false,
|
|
47957
|
+
error: `Failed to fetch transcript content: ${response.status} ${errorText}`
|
|
47958
|
+
};
|
|
47959
|
+
}
|
|
47960
|
+
const vttContent = await response.text();
|
|
47961
|
+
const transcript = parseVttToText(vttContent);
|
|
47962
|
+
return {
|
|
47963
|
+
success: true,
|
|
47964
|
+
transcript,
|
|
47965
|
+
meetingSubject: resolved.subject
|
|
47966
|
+
};
|
|
47967
|
+
} catch (error) {
|
|
47968
|
+
return {
|
|
47969
|
+
success: false,
|
|
47970
|
+
error: `Failed to get meeting transcript: ${error instanceof Error ? error.message : String(error)}`
|
|
47971
|
+
};
|
|
47972
|
+
}
|
|
47973
|
+
}
|
|
47974
|
+
};
|
|
47975
|
+
}
|
|
47976
|
+
|
|
47977
|
+
// src/tools/microsoft/findMeetingSlots.ts
|
|
47978
|
+
function createFindMeetingSlotsTool(connector, userId) {
|
|
47979
|
+
return {
|
|
47980
|
+
definition: {
|
|
47981
|
+
type: "function",
|
|
47982
|
+
function: {
|
|
47983
|
+
name: "find_meeting_slots",
|
|
47984
|
+
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.
|
|
47985
|
+
|
|
47986
|
+
PARAMETER FORMATS:
|
|
47987
|
+
- attendees: plain string array of email addresses. Example: ["alice@contoso.com", "bob@contoso.com"]. Do NOT use objects \u2014 just plain email strings.
|
|
47988
|
+
- startDateTime/endDateTime: ISO 8601 string without timezone suffix. Example: "2025-01-15T08:00:00". Can span multiple days.
|
|
47989
|
+
- duration: number of minutes as integer. Example: 30 or 60.
|
|
47990
|
+
- timeZone: IANA timezone string. Example: "America/New_York", "Europe/Zurich". Default: "UTC".
|
|
47991
|
+
- maxResults: integer. Default: 5.
|
|
47992
|
+
|
|
47993
|
+
EXAMPLES:
|
|
47994
|
+
- 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" }
|
|
47995
|
+
- 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 }`,
|
|
47996
|
+
parameters: {
|
|
47997
|
+
type: "object",
|
|
47998
|
+
properties: {
|
|
47999
|
+
attendees: {
|
|
48000
|
+
type: "array",
|
|
48001
|
+
items: { type: "string" },
|
|
48002
|
+
description: 'Attendee email addresses as plain strings. Example: ["alice@contoso.com", "bob@contoso.com"]. Do NOT pass objects.'
|
|
48003
|
+
},
|
|
48004
|
+
startDateTime: {
|
|
48005
|
+
type: "string",
|
|
48006
|
+
description: 'Search window start as ISO 8601 string without timezone suffix. Example: "2025-01-15T08:00:00"'
|
|
48007
|
+
},
|
|
48008
|
+
endDateTime: {
|
|
48009
|
+
type: "string",
|
|
48010
|
+
description: 'Search window end as ISO 8601 string without timezone suffix. Example: "2025-01-15T18:00:00". Can span multiple days.'
|
|
48011
|
+
},
|
|
48012
|
+
duration: {
|
|
48013
|
+
type: "number",
|
|
48014
|
+
description: "Meeting duration in minutes as integer. Example: 30 or 60."
|
|
48015
|
+
},
|
|
48016
|
+
timeZone: {
|
|
48017
|
+
type: "string",
|
|
48018
|
+
description: 'IANA timezone string for start/end times. Example: "America/New_York", "Europe/Zurich". Default: "UTC".'
|
|
48019
|
+
},
|
|
48020
|
+
maxResults: {
|
|
48021
|
+
type: "number",
|
|
48022
|
+
description: "Maximum number of time slot suggestions as integer. Default: 5."
|
|
48023
|
+
},
|
|
48024
|
+
targetUser: {
|
|
48025
|
+
type: "string",
|
|
48026
|
+
description: 'User ID or email (UPN) for app-only auth. Example: "alice@contoso.com". Ignored in delegated auth.'
|
|
48027
|
+
}
|
|
48028
|
+
},
|
|
48029
|
+
required: ["attendees", "startDateTime", "endDateTime", "duration"]
|
|
48030
|
+
}
|
|
48031
|
+
}
|
|
48032
|
+
},
|
|
48033
|
+
describeCall: (args) => {
|
|
48034
|
+
return `Find ${args.duration}min slots for ${args.attendees.length} attendees`;
|
|
48035
|
+
},
|
|
48036
|
+
permission: {
|
|
48037
|
+
scope: "session",
|
|
48038
|
+
riskLevel: "low",
|
|
48039
|
+
approvalMessage: `Find meeting time slots via ${connector.displayName}`
|
|
48040
|
+
},
|
|
48041
|
+
execute: async (args, context) => {
|
|
48042
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
48043
|
+
try {
|
|
48044
|
+
const prefix = getUserPathPrefix(connector, args.targetUser);
|
|
48045
|
+
const tz = args.timeZone ?? "UTC";
|
|
48046
|
+
const result = await microsoftFetch(
|
|
48047
|
+
connector,
|
|
48048
|
+
`${prefix}/findMeetingTimes`,
|
|
48049
|
+
{
|
|
48050
|
+
method: "POST",
|
|
48051
|
+
userId: effectiveUserId,
|
|
48052
|
+
body: {
|
|
48053
|
+
attendees: formatAttendees(args.attendees),
|
|
48054
|
+
timeConstraint: {
|
|
48055
|
+
timeslots: [
|
|
48056
|
+
{
|
|
48057
|
+
start: { dateTime: args.startDateTime, timeZone: tz },
|
|
48058
|
+
end: { dateTime: args.endDateTime, timeZone: tz }
|
|
48059
|
+
}
|
|
48060
|
+
]
|
|
48061
|
+
},
|
|
48062
|
+
meetingDuration: `PT${args.duration}M`,
|
|
48063
|
+
maxCandidates: args.maxResults ?? 5
|
|
48064
|
+
}
|
|
48065
|
+
}
|
|
48066
|
+
);
|
|
48067
|
+
const slots = (result.meetingTimeSuggestions ?? []).map((s) => ({
|
|
48068
|
+
start: s.meetingTimeSlot.start.dateTime,
|
|
48069
|
+
end: s.meetingTimeSlot.end.dateTime,
|
|
48070
|
+
confidence: String(s.confidence),
|
|
48071
|
+
attendeeAvailability: (s.attendeeAvailability ?? []).map((a) => ({
|
|
48072
|
+
attendee: a.attendee.emailAddress.address,
|
|
48073
|
+
availability: a.availability
|
|
48074
|
+
}))
|
|
48075
|
+
}));
|
|
48076
|
+
return {
|
|
48077
|
+
success: true,
|
|
48078
|
+
slots,
|
|
48079
|
+
emptySuggestionsReason: result.emptySuggestionsReason
|
|
48080
|
+
};
|
|
48081
|
+
} catch (error) {
|
|
48082
|
+
return {
|
|
48083
|
+
success: false,
|
|
48084
|
+
error: `Failed to find meeting slots: ${error instanceof Error ? error.message : String(error)}`
|
|
48085
|
+
};
|
|
48086
|
+
}
|
|
48087
|
+
}
|
|
48088
|
+
};
|
|
48089
|
+
}
|
|
48090
|
+
|
|
48091
|
+
// src/tools/microsoft/register.ts
|
|
48092
|
+
function registerMicrosoftTools() {
|
|
48093
|
+
ConnectorTools.registerService("microsoft", (connector, userId) => {
|
|
48094
|
+
return [
|
|
48095
|
+
createDraftEmailTool(connector, userId),
|
|
48096
|
+
createSendEmailTool(connector, userId),
|
|
48097
|
+
createMeetingTool(connector, userId),
|
|
48098
|
+
createEditMeetingTool(connector, userId),
|
|
48099
|
+
createGetMeetingTranscriptTool(connector, userId),
|
|
48100
|
+
createFindMeetingSlotsTool(connector, userId)
|
|
48101
|
+
];
|
|
48102
|
+
});
|
|
48103
|
+
}
|
|
48104
|
+
|
|
48105
|
+
// src/tools/microsoft/index.ts
|
|
48106
|
+
registerMicrosoftTools();
|
|
48107
|
+
|
|
45945
48108
|
// src/tools/desktop/types.ts
|
|
45946
48109
|
var DEFAULT_DESKTOP_CONFIG = {
|
|
45947
48110
|
driver: null,
|
|
@@ -46757,7 +48920,7 @@ var desktopTools = [
|
|
|
46757
48920
|
|
|
46758
48921
|
// src/tools/custom-tools/resolveStorage.ts
|
|
46759
48922
|
init_StorageRegistry();
|
|
46760
|
-
function
|
|
48923
|
+
function buildStorageContext2(toolContext) {
|
|
46761
48924
|
const global2 = StorageRegistry.getContext();
|
|
46762
48925
|
if (global2) return global2;
|
|
46763
48926
|
if (toolContext?.userId) return { userId: toolContext.userId };
|
|
@@ -46767,7 +48930,7 @@ function resolveCustomToolStorage(explicit, toolContext) {
|
|
|
46767
48930
|
if (explicit) return explicit;
|
|
46768
48931
|
const factory = StorageRegistry.get("customTools");
|
|
46769
48932
|
if (factory) {
|
|
46770
|
-
return factory(
|
|
48933
|
+
return factory(buildStorageContext2(toolContext));
|
|
46771
48934
|
}
|
|
46772
48935
|
return new FileCustomToolStorage();
|
|
46773
48936
|
}
|
|
@@ -46795,12 +48958,13 @@ function createCustomToolDelete(storage) {
|
|
|
46795
48958
|
permission: { scope: "session", riskLevel: "medium" },
|
|
46796
48959
|
execute: async (args, context) => {
|
|
46797
48960
|
try {
|
|
48961
|
+
const userId = context?.userId;
|
|
46798
48962
|
const s = resolveCustomToolStorage(storage, context);
|
|
46799
|
-
const exists = await s.exists(args.name);
|
|
48963
|
+
const exists = await s.exists(userId, args.name);
|
|
46800
48964
|
if (!exists) {
|
|
46801
48965
|
return { success: false, name: args.name, error: `Custom tool '${args.name}' not found` };
|
|
46802
48966
|
}
|
|
46803
|
-
await s.delete(args.name);
|
|
48967
|
+
await s.delete(userId, args.name);
|
|
46804
48968
|
return { success: true, name: args.name };
|
|
46805
48969
|
} catch (error) {
|
|
46806
48970
|
return { success: false, name: args.name, error: error.message };
|
|
@@ -47041,8 +49205,9 @@ function createCustomToolList(storage) {
|
|
|
47041
49205
|
},
|
|
47042
49206
|
permission: { scope: "always", riskLevel: "low" },
|
|
47043
49207
|
execute: async (args, context) => {
|
|
49208
|
+
const userId = context?.userId;
|
|
47044
49209
|
const s = resolveCustomToolStorage(storage, context);
|
|
47045
|
-
const tools = await s.list({
|
|
49210
|
+
const tools = await s.list(userId, {
|
|
47046
49211
|
search: args.search,
|
|
47047
49212
|
tags: args.tags,
|
|
47048
49213
|
category: args.category,
|
|
@@ -47078,8 +49243,9 @@ function createCustomToolLoad(storage) {
|
|
|
47078
49243
|
},
|
|
47079
49244
|
permission: { scope: "always", riskLevel: "low" },
|
|
47080
49245
|
execute: async (args, context) => {
|
|
49246
|
+
const userId = context?.userId;
|
|
47081
49247
|
const s = resolveCustomToolStorage(storage, context);
|
|
47082
|
-
const tool = await s.load(args.name);
|
|
49248
|
+
const tool = await s.load(userId, args.name);
|
|
47083
49249
|
if (!tool) {
|
|
47084
49250
|
return { success: false, error: `Custom tool '${args.name}' not found` };
|
|
47085
49251
|
}
|
|
@@ -47151,9 +49317,10 @@ function createCustomToolSave(storage) {
|
|
|
47151
49317
|
permission: { scope: "session", riskLevel: "medium" },
|
|
47152
49318
|
execute: async (args, context) => {
|
|
47153
49319
|
try {
|
|
49320
|
+
const userId = context?.userId;
|
|
47154
49321
|
const s = resolveCustomToolStorage(storage, context);
|
|
47155
49322
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
47156
|
-
const existing = await s.load(args.name);
|
|
49323
|
+
const existing = await s.load(userId, args.name);
|
|
47157
49324
|
const definition = {
|
|
47158
49325
|
version: CUSTOM_TOOL_DEFINITION_VERSION,
|
|
47159
49326
|
name: args.name,
|
|
@@ -47172,17 +49339,17 @@ function createCustomToolSave(storage) {
|
|
|
47172
49339
|
requiresConnector: (args.connectorNames?.length ?? 0) > 0
|
|
47173
49340
|
}
|
|
47174
49341
|
};
|
|
47175
|
-
await s.save(definition);
|
|
49342
|
+
await s.save(userId, definition);
|
|
47176
49343
|
return {
|
|
47177
49344
|
success: true,
|
|
47178
49345
|
name: args.name,
|
|
47179
|
-
storagePath: s.getPath()
|
|
49346
|
+
storagePath: s.getPath(userId)
|
|
47180
49347
|
};
|
|
47181
49348
|
} catch (error) {
|
|
47182
49349
|
return {
|
|
47183
49350
|
success: false,
|
|
47184
49351
|
name: args.name,
|
|
47185
|
-
storagePath: resolveCustomToolStorage(storage, context).getPath(),
|
|
49352
|
+
storagePath: resolveCustomToolStorage(storage, context).getPath(context?.userId),
|
|
47186
49353
|
error: error.message
|
|
47187
49354
|
};
|
|
47188
49355
|
}
|
|
@@ -47924,6 +50091,6 @@ REMEMBER: Keep it conversational, ask one question at a time, and only output th
|
|
|
47924
50091
|
}
|
|
47925
50092
|
};
|
|
47926
50093
|
|
|
47927
|
-
export { AGENT_DEFINITION_FORMAT_VERSION, AIError, APPROVAL_STATE_VERSION, Agent, AgentContextNextGen, ApproximateTokenEstimator, BaseMediaProvider, BasePluginNextGen, BaseProvider, BaseTextProvider, BraveProvider, CONNECTOR_CONFIG_VERSION, CONTEXT_SESSION_FORMAT_VERSION, CUSTOM_TOOL_DEFINITION_VERSION, CheckpointManager, CircuitBreaker, CircuitOpenError, Connector, ConnectorConfigStore, ConnectorTools, ConsoleMetrics, ContentType, ContextOverflowError, DEFAULT_ALLOWLIST, DEFAULT_BACKOFF_CONFIG, DEFAULT_BASE_DELAY_MS, DEFAULT_CHECKPOINT_STRATEGY, DEFAULT_CIRCUIT_BREAKER_CONFIG, DEFAULT_CONFIG2 as DEFAULT_CONFIG, DEFAULT_CONNECTOR_TIMEOUT, DEFAULT_CONTEXT_CONFIG, DEFAULT_DESKTOP_CONFIG, DEFAULT_FEATURES, DEFAULT_FILESYSTEM_CONFIG, DEFAULT_HISTORY_MANAGER_CONFIG, DEFAULT_MAX_DELAY_MS, DEFAULT_MAX_RETRIES, DEFAULT_MEMORY_CONFIG, DEFAULT_PERMISSION_CONFIG, DEFAULT_RATE_LIMITER_CONFIG, DEFAULT_RETRYABLE_STATUSES, DEFAULT_SHELL_CONFIG, DESKTOP_TOOL_NAMES, DefaultCompactionStrategy, DependencyCycleError, DocumentReader, ErrorHandler, ExecutionContext, ExternalDependencyHandler, FileAgentDefinitionStorage, FileConnectorStorage, FileContextStorage, FileCustomToolStorage, FileMediaStorage as FileMediaOutputHandler, FileMediaStorage, FilePersistentInstructionsStorage, FileStorage, FormatDetector, FrameworkLogger, HookManager, IMAGE_MODELS, IMAGE_MODEL_REGISTRY, ImageGeneration, InContextMemoryPluginNextGen, InMemoryAgentStateStorage, InMemoryHistoryStorage, InMemoryMetrics, InMemoryPlanStorage, InMemoryStorage, InvalidConfigError, InvalidToolArgumentsError, LLM_MODELS, LoggingPlugin, MCPClient, MCPConnectionError, MCPError, MCPProtocolError, MCPRegistry, MCPResourceError, MCPTimeoutError, MCPToolError, MEMORY_PRIORITY_VALUES, MODEL_REGISTRY, MemoryConnectorStorage, MemoryEvictionCompactor, MemoryStorage, MessageBuilder, MessageRole, ModelNotSupportedError, NoOpMetrics, NutTreeDriver, OAuthManager, ParallelTasksError, PersistentInstructionsPluginNextGen, PlanningAgent, ProviderAuthError, ProviderConfigAgent, ProviderContextLengthError, ProviderError, ProviderErrorMapper, ProviderNotFoundError, ProviderRateLimitError, RapidAPIProvider, RateLimitError, SERVICE_DEFINITIONS, SERVICE_INFO, SERVICE_URL_PATTERNS, SIMPLE_ICONS_CDN, STT_MODELS, STT_MODEL_REGISTRY, ScopedConnectorRegistry, ScrapeProvider, SearchProvider, SerperProvider, Services, SpeechToText, StorageRegistry, StrategyRegistry, StreamEventType, StreamHelpers, StreamState, SummarizeCompactor, TERMINAL_TASK_STATUSES, TTS_MODELS, TTS_MODEL_REGISTRY, TaskTimeoutError, TaskValidationError, TavilyProvider, TextToSpeech, TokenBucketRateLimiter, ToolCallState, ToolExecutionError, ToolExecutionPipeline, ToolManager, ToolNotFoundError, ToolPermissionManager, ToolRegistry, ToolTimeoutError, TruncateCompactor, VENDORS, VENDOR_ICON_MAP, VIDEO_MODELS, VIDEO_MODEL_REGISTRY, Vendor, VideoGeneration, WorkingMemory, WorkingMemoryPluginNextGen, addJitter, allVendorTemplates, assertNotDestroyed, authenticatedFetch, backoffSequence, backoffWait, bash, buildAuthConfig, buildEndpointWithQuery, buildQueryString, calculateBackoff, calculateCost, calculateEntrySize, calculateImageCost, calculateSTTCost, calculateTTSCost, calculateVideoCost, canTaskExecute, createAgentStorage, createAuthenticatedFetch, createBashTool, createConnectorFromTemplate, createCreatePRTool, createCustomToolDelete, createCustomToolDraft, createCustomToolList, createCustomToolLoad, createCustomToolMetaTools, createCustomToolSave, createCustomToolTest, createDesktopGetCursorTool, createDesktopGetScreenSizeTool, createDesktopKeyboardKeyTool, createDesktopKeyboardTypeTool, createDesktopMouseClickTool, createDesktopMouseDragTool, createDesktopMouseMoveTool, createDesktopMouseScrollTool, createDesktopScreenshotTool, createDesktopWindowFocusTool, createDesktopWindowListTool, createEditFileTool, createEstimator, createExecuteJavaScriptTool, createFileAgentDefinitionStorage, createFileContextStorage, createFileCustomToolStorage, createFileMediaStorage, createGetPRTool, createGitHubReadFileTool, createGlobTool, createGrepTool, createImageGenerationTool, createImageProvider, createListDirectoryTool, createMessageWithImages, createMetricsCollector, createPRCommentsTool, createPRFilesTool, createPlan, createProvider, createReadFileTool, createSearchCodeTool, createSearchFilesTool, createSpeechToTextTool, createTask, createTextMessage, createTextToSpeechTool, createVideoProvider, createVideoTools, createWriteFileTool, customToolDelete, customToolDraft, customToolList, customToolLoad, customToolSave, customToolTest, defaultDescribeCall, desktopGetCursor, desktopGetScreenSize, desktopKeyboardKey, desktopKeyboardType, desktopMouseClick, desktopMouseDrag, desktopMouseMove, desktopMouseScroll, desktopScreenshot, desktopTools, desktopWindowFocus, desktopWindowList, detectDependencyCycle, detectServiceFromURL, developerTools, documentToContent, editFile, evaluateCondition, extractJSON, extractJSONField, extractNumber, findConnectorByServiceTypes, forPlan, forTasks, generateEncryptionKey, generateSimplePlan, generateWebAPITool, getActiveImageModels, getActiveModels, getActiveSTTModels, getActiveTTSModels, getActiveVideoModels, getAllBuiltInTools, getAllServiceIds, getAllVendorLogos, getAllVendorTemplates, getBackgroundOutput, getConnectorTools, getCredentialsSetupURL, getDesktopDriver, getDocsURL, getImageModelInfo, getImageModelsByVendor, getImageModelsWithFeature, getMediaOutputHandler, getMediaStorage, getModelInfo, getModelsByVendor, getNextExecutableTasks, getRegisteredScrapeProviders, getSTTModelInfo, getSTTModelsByVendor, getSTTModelsWithFeature, getServiceDefinition, getServiceInfo, getServicesByCategory, getTTSModelInfo, getTTSModelsByVendor, getTTSModelsWithFeature, getTaskDependencies, getToolByName, getToolCallDescription, getToolCategories, getToolRegistry, getToolsByCategory, getToolsRequiringConnector, getVendorAuthTemplate, getVendorColor, getVendorDefaultBaseURL, getVendorInfo, getVendorLogo, getVendorLogoCdnUrl, getVendorLogoSvg, getVendorTemplate, getVideoModelInfo, getVideoModelsByVendor, getVideoModelsWithAudio, getVideoModelsWithFeature, glob, globalErrorHandler, grep, hasClipboardImage, hasVendorLogo, hydrateCustomTool, isBlockedCommand, isErrorEvent, isExcludedExtension, isKnownService, isOutputTextDelta, isResponseComplete, isSimpleScope, isStreamEvent, isTaskAwareScope, isTaskBlocked, isTerminalMemoryStatus, isTerminalStatus, isToolCallArgumentsDelta, isToolCallArgumentsDone, isToolCallStart, isVendor, killBackgroundProcess, listConnectorsByServiceTypes, listDirectory, listVendorIds, listVendors, listVendorsByAuthType, listVendorsByCategory, listVendorsWithLogos, logger, mergeTextPieces, metrics, parseKeyCombo, parseRepository, readClipboardImage, readDocumentAsContent, readFile5 as readFile, registerScrapeProvider, resetDefaultDriver, resolveConnector, resolveDependencies, resolveMaxContextTokens, resolveModelCapabilities, resolveRepository, retryWithBackoff, sanitizeToolName, scopeEquals, scopeMatches, setMediaOutputHandler, setMediaStorage, setMetricsCollector, simpleTokenEstimator, toConnectorOptions, toolRegistry, tools_exports as tools, updateTaskStatus, validatePath, writeFile5 as writeFile };
|
|
50094
|
+
export { AGENT_DEFINITION_FORMAT_VERSION, AIError, APPROVAL_STATE_VERSION, Agent, AgentContextNextGen, ApproximateTokenEstimator, BaseMediaProvider, BasePluginNextGen, BaseProvider, BaseTextProvider, BraveProvider, CONNECTOR_CONFIG_VERSION, CONTEXT_SESSION_FORMAT_VERSION, CUSTOM_TOOL_DEFINITION_VERSION, CheckpointManager, CircuitBreaker, CircuitOpenError, Connector, ConnectorConfigStore, ConnectorTools, ConsoleMetrics, ContentType, ContextOverflowError, DEFAULT_ALLOWLIST, DEFAULT_BACKOFF_CONFIG, DEFAULT_BASE_DELAY_MS, DEFAULT_CHECKPOINT_STRATEGY, DEFAULT_CIRCUIT_BREAKER_CONFIG, DEFAULT_CONFIG2 as DEFAULT_CONFIG, DEFAULT_CONNECTOR_TIMEOUT, DEFAULT_CONTEXT_CONFIG, DEFAULT_DESKTOP_CONFIG, DEFAULT_FEATURES, DEFAULT_FILESYSTEM_CONFIG, DEFAULT_HISTORY_MANAGER_CONFIG, DEFAULT_MAX_DELAY_MS, DEFAULT_MAX_RETRIES, DEFAULT_MEMORY_CONFIG, DEFAULT_PERMISSION_CONFIG, DEFAULT_RATE_LIMITER_CONFIG, DEFAULT_RETRYABLE_STATUSES, DEFAULT_SHELL_CONFIG, DESKTOP_TOOL_NAMES, DefaultCompactionStrategy, DependencyCycleError, DocumentReader, ErrorHandler, ExecutionContext, ExternalDependencyHandler, FileAgentDefinitionStorage, FileConnectorStorage, FileContextStorage, FileCustomToolStorage, FileMediaStorage as FileMediaOutputHandler, FileMediaStorage, FilePersistentInstructionsStorage, FileRoutineDefinitionStorage, FileStorage, FileUserInfoStorage, FormatDetector, FrameworkLogger, HookManager, IMAGE_MODELS, IMAGE_MODEL_REGISTRY, ImageGeneration, InContextMemoryPluginNextGen, InMemoryAgentStateStorage, InMemoryHistoryStorage, InMemoryMetrics, InMemoryPlanStorage, InMemoryStorage, InvalidConfigError, InvalidToolArgumentsError, LLM_MODELS, LoggingPlugin, MCPClient, MCPConnectionError, MCPError, MCPProtocolError, MCPRegistry, MCPResourceError, MCPTimeoutError, MCPToolError, MEMORY_PRIORITY_VALUES, MODEL_REGISTRY, MemoryConnectorStorage, MemoryEvictionCompactor, MemoryStorage, MessageBuilder, MessageRole, ModelNotSupportedError, NoOpMetrics, NutTreeDriver, OAuthManager, ParallelTasksError, PersistentInstructionsPluginNextGen, PlanningAgent, ProviderAuthError, ProviderConfigAgent, ProviderContextLengthError, ProviderError, ProviderErrorMapper, ProviderNotFoundError, ProviderRateLimitError, RapidAPIProvider, RateLimitError, SERVICE_DEFINITIONS, SERVICE_INFO, SERVICE_URL_PATTERNS, SIMPLE_ICONS_CDN, STT_MODELS, STT_MODEL_REGISTRY, ScopedConnectorRegistry, ScrapeProvider, SearchProvider, SerperProvider, Services, SpeechToText, StorageRegistry, StrategyRegistry, StreamEventType, StreamHelpers, StreamState, SummarizeCompactor, TERMINAL_TASK_STATUSES, TTS_MODELS, TTS_MODEL_REGISTRY, TaskTimeoutError, TaskValidationError, TavilyProvider, TextToSpeech, TokenBucketRateLimiter, ToolCallState, ToolExecutionError, ToolExecutionPipeline, ToolManager, ToolNotFoundError, ToolPermissionManager, ToolRegistry, ToolTimeoutError, TruncateCompactor, UserInfoPluginNextGen, VENDORS, VENDOR_ICON_MAP, VIDEO_MODELS, VIDEO_MODEL_REGISTRY, Vendor, VideoGeneration, WorkingMemory, WorkingMemoryPluginNextGen, addJitter, allVendorTemplates, assertNotDestroyed, authenticatedFetch, backoffSequence, backoffWait, bash, buildAuthConfig, buildEndpointWithQuery, buildQueryString, calculateBackoff, calculateCost, calculateEntrySize, calculateImageCost, calculateSTTCost, calculateTTSCost, calculateVideoCost, canTaskExecute, createAgentStorage, createAuthenticatedFetch, createBashTool, createConnectorFromTemplate, createCreatePRTool, createCustomToolDelete, createCustomToolDraft, createCustomToolList, createCustomToolLoad, createCustomToolMetaTools, createCustomToolSave, createCustomToolTest, createDesktopGetCursorTool, createDesktopGetScreenSizeTool, createDesktopKeyboardKeyTool, createDesktopKeyboardTypeTool, createDesktopMouseClickTool, createDesktopMouseDragTool, createDesktopMouseMoveTool, createDesktopMouseScrollTool, createDesktopScreenshotTool, createDesktopWindowFocusTool, createDesktopWindowListTool, createDraftEmailTool, createEditFileTool, createEditMeetingTool, createEstimator, createExecuteJavaScriptTool, createFileAgentDefinitionStorage, createFileContextStorage, createFileCustomToolStorage, createFileMediaStorage, createFileRoutineDefinitionStorage, createFindMeetingSlotsTool, createGetMeetingTranscriptTool, createGetPRTool, createGitHubReadFileTool, createGlobTool, createGrepTool, createImageGenerationTool, createImageProvider, createListDirectoryTool, createMeetingTool, createMessageWithImages, createMetricsCollector, createPRCommentsTool, createPRFilesTool, createPlan, createProvider, createReadFileTool, createRoutineDefinition, createRoutineExecution, createSearchCodeTool, createSearchFilesTool, createSendEmailTool, createSpeechToTextTool, createTask, createTextMessage, createTextToSpeechTool, createVideoProvider, createVideoTools, createWriteFileTool, customToolDelete, customToolDraft, customToolList, customToolLoad, customToolSave, customToolTest, defaultDescribeCall, desktopGetCursor, desktopGetScreenSize, desktopKeyboardKey, desktopKeyboardType, desktopMouseClick, desktopMouseDrag, desktopMouseMove, desktopMouseScroll, desktopScreenshot, desktopTools, desktopWindowFocus, desktopWindowList, detectDependencyCycle, detectServiceFromURL, developerTools, documentToContent, editFile, evaluateCondition, executeRoutine, extractJSON, extractJSONField, extractNumber, findConnectorByServiceTypes, forPlan, forTasks, formatAttendees, formatRecipients, generateEncryptionKey, generateSimplePlan, generateWebAPITool, getActiveImageModels, getActiveModels, getActiveSTTModels, getActiveTTSModels, getActiveVideoModels, getAllBuiltInTools, getAllServiceIds, getAllVendorLogos, getAllVendorTemplates, getBackgroundOutput, getConnectorTools, getCredentialsSetupURL, getDesktopDriver, getDocsURL, getImageModelInfo, getImageModelsByVendor, getImageModelsWithFeature, getMediaOutputHandler, getMediaStorage, getModelInfo, getModelsByVendor, getNextExecutableTasks, getRegisteredScrapeProviders, getRoutineProgress, getSTTModelInfo, getSTTModelsByVendor, getSTTModelsWithFeature, getServiceDefinition, getServiceInfo, getServicesByCategory, getTTSModelInfo, getTTSModelsByVendor, getTTSModelsWithFeature, getTaskDependencies, getToolByName, getToolCallDescription, getToolCategories, getToolRegistry, getToolsByCategory, getToolsRequiringConnector, getUserPathPrefix, getVendorAuthTemplate, getVendorColor, getVendorDefaultBaseURL, getVendorInfo, getVendorLogo, getVendorLogoCdnUrl, getVendorLogoSvg, getVendorTemplate, getVideoModelInfo, getVideoModelsByVendor, getVideoModelsWithAudio, getVideoModelsWithFeature, glob, globalErrorHandler, grep, hasClipboardImage, hasVendorLogo, hydrateCustomTool, isBlockedCommand, isErrorEvent, isExcludedExtension, isKnownService, isOutputTextDelta, isResponseComplete, isSimpleScope, isStreamEvent, isTaskAwareScope, isTaskBlocked, isTeamsMeetingUrl, isTerminalMemoryStatus, isTerminalStatus, isToolCallArgumentsDelta, isToolCallArgumentsDone, isToolCallStart, isVendor, killBackgroundProcess, listConnectorsByServiceTypes, listDirectory, listVendorIds, listVendors, listVendorsByAuthType, listVendorsByCategory, listVendorsWithLogos, logger, mergeTextPieces, metrics, microsoftFetch, normalizeEmails, parseKeyCombo, parseRepository, readClipboardImage, readDocumentAsContent, readFile5 as readFile, registerScrapeProvider, resetDefaultDriver, resolveConnector, resolveDependencies, resolveMaxContextTokens, resolveMeetingId, resolveModelCapabilities, resolveRepository, retryWithBackoff, sanitizeToolName, scopeEquals, scopeMatches, setMediaOutputHandler, setMediaStorage, setMetricsCollector, simpleTokenEstimator, toConnectorOptions, toolRegistry, tools_exports as tools, updateTaskStatus, validatePath, writeFile5 as writeFile };
|
|
47928
50095
|
//# sourceMappingURL=index.js.map
|
|
47929
50096
|
//# sourceMappingURL=index.js.map
|