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