@everworker/oneringai 0.3.2 → 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 fs18 = 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 fs17 = 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 fs18__namespace = /*#__PURE__*/_interopNamespace(fs18);
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 fs17__namespace = /*#__PURE__*/_interopNamespace(fs17);
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 = fs18__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 (!fs18__namespace.existsSync(dir)) {
1436
- fs18__namespace.mkdirSync(dir, { recursive: true });
1435
+ if (!fs19__namespace.existsSync(dir)) {
1436
+ fs19__namespace.mkdirSync(dir, { recursive: true });
1437
1437
  }
1438
- this.fileStream = fs18__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, fs19, 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, fs19[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 });
@@ -14902,7 +14902,7 @@ var FilePersistentInstructionsStorage = class {
14902
14902
  */
14903
14903
  async load() {
14904
14904
  try {
14905
- const raw = await fs18.promises.readFile(this.filePath, "utf-8");
14905
+ const raw = await fs19.promises.readFile(this.filePath, "utf-8");
14906
14906
  const data = JSON.parse(raw);
14907
14907
  if (data.version === 2 && Array.isArray(data.entries)) {
14908
14908
  return data.entries.length > 0 ? data.entries : null;
@@ -14914,7 +14914,7 @@ var FilePersistentInstructionsStorage = class {
14914
14914
  }
14915
14915
  }
14916
14916
  try {
14917
- const content = await fs18.promises.readFile(this.legacyFilePath, "utf-8");
14917
+ const content = await fs19.promises.readFile(this.legacyFilePath, "utf-8");
14918
14918
  const trimmed = content.trim();
14919
14919
  if (!trimmed) return null;
14920
14920
  const now = Date.now();
@@ -14944,11 +14944,11 @@ var FilePersistentInstructionsStorage = class {
14944
14944
  };
14945
14945
  const tempPath = `${this.filePath}.tmp`;
14946
14946
  try {
14947
- await fs18.promises.writeFile(tempPath, JSON.stringify(data, null, 2), "utf-8");
14948
- await fs18.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);
14949
14949
  } catch (error) {
14950
14950
  try {
14951
- await fs18.promises.unlink(tempPath);
14951
+ await fs19.promises.unlink(tempPath);
14952
14952
  } catch {
14953
14953
  }
14954
14954
  throw error;
@@ -14960,7 +14960,7 @@ var FilePersistentInstructionsStorage = class {
14960
14960
  */
14961
14961
  async delete() {
14962
14962
  try {
14963
- await fs18.promises.unlink(this.filePath);
14963
+ await fs19.promises.unlink(this.filePath);
14964
14964
  } catch (error) {
14965
14965
  if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
14966
14966
  throw error;
@@ -14973,11 +14973,11 @@ var FilePersistentInstructionsStorage = class {
14973
14973
  */
14974
14974
  async exists() {
14975
14975
  try {
14976
- await fs18.promises.access(this.filePath);
14976
+ await fs19.promises.access(this.filePath);
14977
14977
  return true;
14978
14978
  } catch {
14979
14979
  try {
14980
- await fs18.promises.access(this.legacyFilePath);
14980
+ await fs19.promises.access(this.legacyFilePath);
14981
14981
  return true;
14982
14982
  } catch {
14983
14983
  return false;
@@ -15001,7 +15001,7 @@ var FilePersistentInstructionsStorage = class {
15001
15001
  */
15002
15002
  async ensureDirectory() {
15003
15003
  try {
15004
- await fs18.promises.mkdir(this.directory, { recursive: true });
15004
+ await fs19.promises.mkdir(this.directory, { recursive: true });
15005
15005
  } catch (error) {
15006
15006
  if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
15007
15007
  throw error;
@@ -15013,7 +15013,7 @@ var FilePersistentInstructionsStorage = class {
15013
15013
  */
15014
15014
  async removeLegacyFile() {
15015
15015
  try {
15016
- await fs18.promises.unlink(this.legacyFilePath);
15016
+ await fs19.promises.unlink(this.legacyFilePath);
15017
15017
  } catch (error) {
15018
15018
  if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
15019
15019
  console.warn(`Failed to remove legacy instructions file: ${this.legacyFilePath}`);
@@ -15518,7 +15518,7 @@ var FileUserInfoStorage = class {
15518
15518
  async load(userId) {
15519
15519
  const filePath = this.getUserFilePath(userId);
15520
15520
  try {
15521
- const raw = await fs18.promises.readFile(filePath, "utf-8");
15521
+ const raw = await fs19.promises.readFile(filePath, "utf-8");
15522
15522
  const data = JSON.parse(raw);
15523
15523
  if (data.version === 1 && Array.isArray(data.entries)) {
15524
15524
  return data.entries.length > 0 ? data.entries : null;
@@ -15546,11 +15546,11 @@ var FileUserInfoStorage = class {
15546
15546
  };
15547
15547
  const tempPath = `${filePath}.tmp`;
15548
15548
  try {
15549
- await fs18.promises.writeFile(tempPath, JSON.stringify(data, null, 2), "utf-8");
15550
- await fs18.promises.rename(tempPath, filePath);
15549
+ await fs19.promises.writeFile(tempPath, JSON.stringify(data, null, 2), "utf-8");
15550
+ await fs19.promises.rename(tempPath, filePath);
15551
15551
  } catch (error) {
15552
15552
  try {
15553
- await fs18.promises.unlink(tempPath);
15553
+ await fs19.promises.unlink(tempPath);
15554
15554
  } catch {
15555
15555
  }
15556
15556
  throw error;
@@ -15562,7 +15562,7 @@ var FileUserInfoStorage = class {
15562
15562
  async delete(userId) {
15563
15563
  const filePath = this.getUserFilePath(userId);
15564
15564
  try {
15565
- await fs18.promises.unlink(filePath);
15565
+ await fs19.promises.unlink(filePath);
15566
15566
  } catch (error) {
15567
15567
  if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
15568
15568
  throw error;
@@ -15575,7 +15575,7 @@ var FileUserInfoStorage = class {
15575
15575
  async exists(userId) {
15576
15576
  const filePath = this.getUserFilePath(userId);
15577
15577
  try {
15578
- await fs18.promises.access(filePath);
15578
+ await fs19.promises.access(filePath);
15579
15579
  return true;
15580
15580
  } catch {
15581
15581
  return false;
@@ -15592,7 +15592,7 @@ var FileUserInfoStorage = class {
15592
15592
  */
15593
15593
  async ensureDirectory(directory) {
15594
15594
  try {
15595
- await fs18.promises.mkdir(directory, { recursive: true });
15595
+ await fs19.promises.mkdir(directory, { recursive: true });
15596
15596
  } catch (error) {
15597
15597
  if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
15598
15598
  throw error;
@@ -17370,6 +17370,8 @@ ${content}`);
17370
17370
  breakdown.pluginContents[plugin.name] = plugin.getTokenSize();
17371
17371
  }
17372
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 })}`);
17373
17375
  const systemText = parts.join("\n\n---\n\n");
17374
17376
  const systemTokens = this._estimator.estimateTokens(systemText);
17375
17377
  const systemMessage = {
@@ -20828,6 +20830,7 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
20828
20830
  _agentContext;
20829
20831
  // SINGLE SOURCE OF TRUTH for tools and sessions
20830
20832
  _permissionManager;
20833
+ _ownsContext = true;
20831
20834
  _isDestroyed = false;
20832
20835
  _cleanupCallbacks = [];
20833
20836
  _logger;
@@ -20880,8 +20883,10 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
20880
20883
  */
20881
20884
  initializeAgentContext(config) {
20882
20885
  if (config.context instanceof AgentContextNextGen) {
20886
+ this._ownsContext = false;
20883
20887
  return config.context;
20884
20888
  }
20889
+ this._ownsContext = true;
20885
20890
  const contextConfig = {
20886
20891
  model: config.model,
20887
20892
  agentId: config.name,
@@ -21393,7 +21398,9 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
21393
21398
  clearInterval(this._autoSaveInterval);
21394
21399
  this._autoSaveInterval = null;
21395
21400
  }
21396
- this._agentContext.destroy();
21401
+ if (this._ownsContext) {
21402
+ this._agentContext.destroy();
21403
+ }
21397
21404
  this._permissionManager.removeAllListeners();
21398
21405
  this.removeAllListeners();
21399
21406
  }
@@ -21734,6 +21741,18 @@ var HookManager = class {
21734
21741
  }
21735
21742
  existing.push(hook);
21736
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
+ }
21737
21756
  /**
21738
21757
  * Execute hooks for a given name
21739
21758
  */
@@ -21756,7 +21775,7 @@ var HookManager = class {
21756
21775
  const hook = hooks[i];
21757
21776
  const hookKey = this.getHookKey(hook, i);
21758
21777
  const hookResult = await this.executeHookSafely(hook, context, hookKey);
21759
- if (hookResult === null) {
21778
+ if (hookResult == null) {
21760
21779
  continue;
21761
21780
  }
21762
21781
  result = { ...result, ...hookResult };
@@ -21776,7 +21795,7 @@ var HookManager = class {
21776
21795
  return this.executeHookSafely(hook, context, hookKey);
21777
21796
  })
21778
21797
  );
21779
- const validResults = results.filter((r) => r !== null);
21798
+ const validResults = results.filter((r) => r != null);
21780
21799
  return validResults.reduce(
21781
21800
  (acc, hookResult) => ({ ...acc, ...hookResult }),
21782
21801
  defaultResult
@@ -22452,7 +22471,6 @@ var Agent = class _Agent extends BaseAgent {
22452
22471
  _cleanupExecution(streamState) {
22453
22472
  streamState?.clear();
22454
22473
  this.executionContext?.cleanup();
22455
- this.hookManager.clear();
22456
22474
  }
22457
22475
  /**
22458
22476
  * Emit iteration complete event (helper for run loop)
@@ -23337,6 +23355,27 @@ var Agent = class _Agent extends BaseAgent {
23337
23355
  isCancelled() {
23338
23356
  return this._cancelled;
23339
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
+ }
23340
23379
  // ===== Cleanup =====
23341
23380
  destroy() {
23342
23381
  if (this._isDestroyed) {
@@ -23366,6 +23405,874 @@ var Agent = class _Agent extends BaseAgent {
23366
23405
  }
23367
23406
  };
23368
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
+
23369
24276
  // src/core/index.ts
23370
24277
  init_constants();
23371
24278
  (class {
@@ -23382,7 +24289,7 @@ init_constants();
23382
24289
  throw new Error("Configuration file not found. Searched: " + this.DEFAULT_PATHS.join(", "));
23383
24290
  }
23384
24291
  try {
23385
- const content = await fs18.promises.readFile(configPath, "utf-8");
24292
+ const content = await fs19.promises.readFile(configPath, "utf-8");
23386
24293
  let config = JSON.parse(content);
23387
24294
  config = this.interpolateEnvVars(config);
23388
24295
  this.validate(config);
@@ -23403,8 +24310,8 @@ init_constants();
23403
24310
  throw new Error("Configuration file not found. Searched: " + this.DEFAULT_PATHS.join(", "));
23404
24311
  }
23405
24312
  try {
23406
- const fs19 = __require("fs");
23407
- const content = fs19.readFileSync(configPath, "utf-8");
24313
+ const fs20 = __require("fs");
24314
+ const content = fs20.readFileSync(configPath, "utf-8");
23408
24315
  let config = JSON.parse(content);
23409
24316
  config = this.interpolateEnvVars(config);
23410
24317
  this.validate(config);
@@ -23422,7 +24329,7 @@ init_constants();
23422
24329
  static async findConfig() {
23423
24330
  for (const path6 of this.DEFAULT_PATHS) {
23424
24331
  try {
23425
- await fs18.promises.access(path2.resolve(path6));
24332
+ await fs19.promises.access(path2.resolve(path6));
23426
24333
  return path2.resolve(path6);
23427
24334
  } catch {
23428
24335
  }
@@ -23433,10 +24340,10 @@ init_constants();
23433
24340
  * Find configuration file synchronously
23434
24341
  */
23435
24342
  static findConfigSync() {
23436
- const fs19 = __require("fs");
24343
+ const fs20 = __require("fs");
23437
24344
  for (const path6 of this.DEFAULT_PATHS) {
23438
24345
  try {
23439
- fs19.accessSync(path2.resolve(path6));
24346
+ fs20.accessSync(path2.resolve(path6));
23440
24347
  return path2.resolve(path6);
23441
24348
  } catch {
23442
24349
  }
@@ -29010,7 +29917,7 @@ var MCPRegistry = class {
29010
29917
  static async loadFromConfigFile(path6) {
29011
29918
  try {
29012
29919
  const configPath = path2.resolve(path6);
29013
- const content = await fs18.promises.readFile(configPath, "utf-8");
29920
+ const content = await fs19.promises.readFile(configPath, "utf-8");
29014
29921
  const config = JSON.parse(content);
29015
29922
  if (!config.mcp) {
29016
29923
  throw new MCPError("Configuration file does not contain MCP section");
@@ -29615,7 +30522,7 @@ var OpenAISTTProvider = class extends BaseMediaProvider {
29615
30522
  if (Buffer.isBuffer(audio)) {
29616
30523
  return new File([new Uint8Array(audio)], "audio.wav", { type: "audio/wav" });
29617
30524
  } else if (typeof audio === "string") {
29618
- return fs18__namespace.createReadStream(audio);
30525
+ return fs19__namespace.createReadStream(audio);
29619
30526
  } else {
29620
30527
  throw new Error("Invalid audio input: must be Buffer or file path");
29621
30528
  }
@@ -30168,7 +31075,7 @@ var TextToSpeech = class _TextToSpeech {
30168
31075
  */
30169
31076
  async toFile(text, filePath, options) {
30170
31077
  const response = await this.synthesize(text, options);
30171
- await fs17__namespace.writeFile(filePath, response.audio);
31078
+ await fs18__namespace.writeFile(filePath, response.audio);
30172
31079
  }
30173
31080
  // ======================== Introspection Methods ========================
30174
31081
  /**
@@ -30516,7 +31423,7 @@ var SpeechToText = class _SpeechToText {
30516
31423
  * @param options - Optional transcription parameters
30517
31424
  */
30518
31425
  async transcribeFile(filePath, options) {
30519
- const audio = await fs17__namespace.readFile(filePath);
31426
+ const audio = await fs18__namespace.readFile(filePath);
30520
31427
  return this.transcribe(audio, options);
30521
31428
  }
30522
31429
  /**
@@ -30842,7 +31749,7 @@ var OpenAIImageProvider = class extends BaseMediaProvider {
30842
31749
  if (Buffer.isBuffer(image)) {
30843
31750
  return new File([new Uint8Array(image)], "image.png", { type: "image/png" });
30844
31751
  }
30845
- return fs18__namespace.createReadStream(image);
31752
+ return fs19__namespace.createReadStream(image);
30846
31753
  }
30847
31754
  /**
30848
31755
  * Handle OpenAI API errors
@@ -30989,8 +31896,8 @@ var GoogleImageProvider = class extends BaseMediaProvider {
30989
31896
  if (Buffer.isBuffer(image)) {
30990
31897
  imageBytes = image.toString("base64");
30991
31898
  } else {
30992
- const fs19 = await import('fs');
30993
- const buffer = fs19.readFileSync(image);
31899
+ const fs20 = await import('fs');
31900
+ const buffer = fs20.readFileSync(image);
30994
31901
  imageBytes = buffer.toString("base64");
30995
31902
  }
30996
31903
  return {
@@ -31151,7 +32058,7 @@ var GrokImageProvider = class extends BaseMediaProvider {
31151
32058
  if (Buffer.isBuffer(image)) {
31152
32059
  return new File([new Uint8Array(image)], "image.png", { type: "image/png" });
31153
32060
  }
31154
- return fs18__namespace.createReadStream(image);
32061
+ return fs19__namespace.createReadStream(image);
31155
32062
  }
31156
32063
  /**
31157
32064
  * Handle API errors
@@ -32601,8 +33508,8 @@ var OpenAISoraProvider = class extends BaseMediaProvider {
32601
33508
  return new File([new Uint8Array(image)], "input.png", { type: "image/png" });
32602
33509
  }
32603
33510
  if (!image.startsWith("http")) {
32604
- const fs19 = await import('fs');
32605
- const data = fs19.readFileSync(image);
33511
+ const fs20 = await import('fs');
33512
+ const data = fs20.readFileSync(image);
32606
33513
  return new File([new Uint8Array(data)], "input.png", { type: "image/png" });
32607
33514
  }
32608
33515
  const response = await fetch(image);
@@ -32780,7 +33687,7 @@ var GoogleVeoProvider = class extends BaseMediaProvider {
32780
33687
  if (video.videoBytes) {
32781
33688
  buffer = Buffer.from(video.videoBytes, "base64");
32782
33689
  } else if (video.uri) {
32783
- const fs19 = await import('fs/promises');
33690
+ const fs20 = await import('fs/promises');
32784
33691
  const os3 = await import('os');
32785
33692
  const path6 = await import('path');
32786
33693
  const tempDir = os3.tmpdir();
@@ -32791,11 +33698,11 @@ var GoogleVeoProvider = class extends BaseMediaProvider {
32791
33698
  // Pass as GeneratedVideo
32792
33699
  downloadPath: tempFile
32793
33700
  });
32794
- buffer = await fs19.readFile(tempFile);
32795
- await fs19.unlink(tempFile).catch(() => {
33701
+ buffer = await fs20.readFile(tempFile);
33702
+ await fs20.unlink(tempFile).catch(() => {
32796
33703
  });
32797
33704
  } catch (downloadError) {
32798
- await fs19.unlink(tempFile).catch(() => {
33705
+ await fs20.unlink(tempFile).catch(() => {
32799
33706
  });
32800
33707
  throw new ProviderError(
32801
33708
  "google",
@@ -32917,8 +33824,8 @@ var GoogleVeoProvider = class extends BaseMediaProvider {
32917
33824
  if (image.startsWith("http://") || image.startsWith("https://")) {
32918
33825
  return { imageUri: image };
32919
33826
  }
32920
- const fs19 = await import('fs/promises');
32921
- const data = await fs19.readFile(image);
33827
+ const fs20 = await import('fs/promises');
33828
+ const data = await fs20.readFile(image);
32922
33829
  return {
32923
33830
  imageBytes: data.toString("base64")
32924
33831
  };
@@ -33225,8 +34132,8 @@ var GrokImagineProvider = class extends BaseMediaProvider {
33225
34132
  if (image.startsWith("http") || image.startsWith("data:")) {
33226
34133
  return image;
33227
34134
  }
33228
- const fs19 = await import('fs');
33229
- const data = fs19.readFileSync(image);
34135
+ const fs20 = await import('fs');
34136
+ const data = fs20.readFileSync(image);
33230
34137
  const base64 = data.toString("base64");
33231
34138
  const ext = image.split(".").pop()?.toLowerCase() || "png";
33232
34139
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext}`;
@@ -34662,7 +35569,7 @@ var DocumentReader = class _DocumentReader {
34662
35569
  async resolveSource(source) {
34663
35570
  switch (source.type) {
34664
35571
  case "file": {
34665
- const buffer = await fs17.readFile(source.path);
35572
+ const buffer = await fs18.readFile(source.path);
34666
35573
  const filename = source.path.split("/").pop() || source.path;
34667
35574
  return { buffer, filename };
34668
35575
  }
@@ -35895,245 +36802,6 @@ var CheckpointManager = class {
35895
36802
  }
35896
36803
  };
35897
36804
 
35898
- // src/domain/entities/Task.ts
35899
- var TERMINAL_TASK_STATUSES = ["completed", "failed", "skipped", "cancelled"];
35900
- function isTerminalStatus(status) {
35901
- return TERMINAL_TASK_STATUSES.includes(status);
35902
- }
35903
- function createTask(input) {
35904
- const now = Date.now();
35905
- const id = input.id ?? `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
35906
- return {
35907
- id,
35908
- name: input.name,
35909
- description: input.description,
35910
- status: "pending",
35911
- dependsOn: input.dependsOn ?? [],
35912
- externalDependency: input.externalDependency,
35913
- condition: input.condition,
35914
- execution: input.execution,
35915
- validation: input.validation,
35916
- expectedOutput: input.expectedOutput,
35917
- attempts: 0,
35918
- maxAttempts: input.maxAttempts ?? 3,
35919
- createdAt: now,
35920
- lastUpdatedAt: now,
35921
- metadata: input.metadata
35922
- };
35923
- }
35924
- function createPlan(input) {
35925
- const now = Date.now();
35926
- const id = `plan-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
35927
- const tasks = input.tasks.map((taskInput) => createTask(taskInput));
35928
- const nameToId = /* @__PURE__ */ new Map();
35929
- for (const task of tasks) {
35930
- nameToId.set(task.name, task.id);
35931
- }
35932
- for (let i = 0; i < tasks.length; i++) {
35933
- const taskInput = input.tasks[i];
35934
- const task = tasks[i];
35935
- if (taskInput.dependsOn && taskInput.dependsOn.length > 0) {
35936
- task.dependsOn = taskInput.dependsOn.map((dep) => {
35937
- if (dep.startsWith("task-")) {
35938
- return dep;
35939
- }
35940
- const resolvedId = nameToId.get(dep);
35941
- if (!resolvedId) {
35942
- throw new Error(`Task dependency "${dep}" not found in plan`);
35943
- }
35944
- return resolvedId;
35945
- });
35946
- }
35947
- }
35948
- if (!input.skipCycleCheck) {
35949
- const cycle = detectDependencyCycle(tasks);
35950
- if (cycle) {
35951
- const cycleNames = cycle.map((taskId) => {
35952
- const task = tasks.find((t) => t.id === taskId);
35953
- return task ? task.name : taskId;
35954
- });
35955
- throw new DependencyCycleError(cycleNames, id);
35956
- }
35957
- }
35958
- return {
35959
- id,
35960
- goal: input.goal,
35961
- context: input.context,
35962
- tasks,
35963
- concurrency: input.concurrency,
35964
- allowDynamicTasks: input.allowDynamicTasks ?? true,
35965
- status: "pending",
35966
- createdAt: now,
35967
- lastUpdatedAt: now,
35968
- metadata: input.metadata
35969
- };
35970
- }
35971
- function canTaskExecute(task, allTasks) {
35972
- if (task.status !== "pending") {
35973
- return false;
35974
- }
35975
- if (task.dependsOn.length > 0) {
35976
- for (const depId of task.dependsOn) {
35977
- const depTask = allTasks.find((t) => t.id === depId);
35978
- if (!depTask || depTask.status !== "completed") {
35979
- return false;
35980
- }
35981
- }
35982
- }
35983
- return true;
35984
- }
35985
- function getNextExecutableTasks(plan) {
35986
- const executable = plan.tasks.filter((task) => canTaskExecute(task, plan.tasks));
35987
- if (executable.length === 0) {
35988
- return [];
35989
- }
35990
- if (!plan.concurrency) {
35991
- return [executable[0]];
35992
- }
35993
- const runningCount = plan.tasks.filter((t) => t.status === "in_progress").length;
35994
- const availableSlots = plan.concurrency.maxParallelTasks - runningCount;
35995
- if (availableSlots <= 0) {
35996
- return [];
35997
- }
35998
- const parallelTasks = executable.filter((task) => task.execution?.parallel === true);
35999
- if (parallelTasks.length === 0) {
36000
- return [executable[0]];
36001
- }
36002
- let sortedTasks = [...parallelTasks];
36003
- if (plan.concurrency.strategy === "priority") {
36004
- sortedTasks.sort((a, b) => (b.execution?.priority ?? 0) - (a.execution?.priority ?? 0));
36005
- }
36006
- return sortedTasks.slice(0, availableSlots);
36007
- }
36008
- async function evaluateCondition(condition, memory) {
36009
- const value = await memory.get(condition.memoryKey);
36010
- switch (condition.operator) {
36011
- case "exists":
36012
- return value !== void 0;
36013
- case "not_exists":
36014
- return value === void 0;
36015
- case "equals":
36016
- return value === condition.value;
36017
- case "contains":
36018
- if (Array.isArray(value)) {
36019
- return value.includes(condition.value);
36020
- }
36021
- if (typeof value === "string" && typeof condition.value === "string") {
36022
- return value.includes(condition.value);
36023
- }
36024
- return false;
36025
- case "truthy":
36026
- return !!value;
36027
- case "greater_than":
36028
- if (typeof value === "number" && typeof condition.value === "number") {
36029
- return value > condition.value;
36030
- }
36031
- return false;
36032
- case "less_than":
36033
- if (typeof value === "number" && typeof condition.value === "number") {
36034
- return value < condition.value;
36035
- }
36036
- return false;
36037
- default:
36038
- return false;
36039
- }
36040
- }
36041
- function updateTaskStatus(task, status) {
36042
- const now = Date.now();
36043
- const updated = {
36044
- ...task,
36045
- status,
36046
- lastUpdatedAt: now
36047
- };
36048
- if (status === "in_progress") {
36049
- if (!updated.startedAt) {
36050
- updated.startedAt = now;
36051
- }
36052
- updated.attempts += 1;
36053
- }
36054
- if ((status === "completed" || status === "failed") && !updated.completedAt) {
36055
- updated.completedAt = now;
36056
- }
36057
- return updated;
36058
- }
36059
- function isTaskBlocked(task, allTasks) {
36060
- if (task.dependsOn.length === 0) {
36061
- return false;
36062
- }
36063
- for (const depId of task.dependsOn) {
36064
- const depTask = allTasks.find((t) => t.id === depId);
36065
- if (!depTask) {
36066
- return true;
36067
- }
36068
- if (depTask.status !== "completed") {
36069
- return true;
36070
- }
36071
- }
36072
- return false;
36073
- }
36074
- function getTaskDependencies(task, allTasks) {
36075
- if (task.dependsOn.length === 0) {
36076
- return [];
36077
- }
36078
- return task.dependsOn.map((depId) => allTasks.find((t) => t.id === depId)).filter((t) => t !== void 0);
36079
- }
36080
- function resolveDependencies(taskInputs, tasks) {
36081
- const nameToId = /* @__PURE__ */ new Map();
36082
- for (const task of tasks) {
36083
- nameToId.set(task.name, task.id);
36084
- }
36085
- for (const input of taskInputs) {
36086
- if (input.dependsOn && input.dependsOn.length > 0) {
36087
- input.dependsOn = input.dependsOn.map((dep) => {
36088
- if (dep.startsWith("task-")) {
36089
- return dep;
36090
- }
36091
- const resolvedId = nameToId.get(dep);
36092
- if (!resolvedId) {
36093
- throw new Error(`Task dependency "${dep}" not found`);
36094
- }
36095
- return resolvedId;
36096
- });
36097
- }
36098
- }
36099
- }
36100
- function detectDependencyCycle(tasks) {
36101
- const visited = /* @__PURE__ */ new Set();
36102
- const recStack = /* @__PURE__ */ new Set();
36103
- const taskMap = new Map(tasks.map((t) => [t.id, t]));
36104
- function dfs(taskId, path6) {
36105
- if (recStack.has(taskId)) {
36106
- const cycleStart = path6.indexOf(taskId);
36107
- return [...path6.slice(cycleStart), taskId];
36108
- }
36109
- if (visited.has(taskId)) {
36110
- return null;
36111
- }
36112
- visited.add(taskId);
36113
- recStack.add(taskId);
36114
- const task = taskMap.get(taskId);
36115
- if (task) {
36116
- for (const depId of task.dependsOn) {
36117
- const cycle = dfs(depId, [...path6, taskId]);
36118
- if (cycle) {
36119
- return cycle;
36120
- }
36121
- }
36122
- }
36123
- recStack.delete(taskId);
36124
- return null;
36125
- }
36126
- for (const task of tasks) {
36127
- if (!visited.has(task.id)) {
36128
- const cycle = dfs(task.id, []);
36129
- if (cycle) {
36130
- return cycle;
36131
- }
36132
- }
36133
- }
36134
- return null;
36135
- }
36136
-
36137
36805
  // src/capabilities/taskAgent/PlanningAgent.ts
36138
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.
36139
36807
 
@@ -36981,11 +37649,11 @@ var FileContextStorage = class {
36981
37649
  const data = this.prettyPrint ? JSON.stringify(storedSession, null, 2) : JSON.stringify(storedSession);
36982
37650
  const tempPath = `${filePath}.tmp`;
36983
37651
  try {
36984
- await fs18.promises.writeFile(tempPath, data, "utf-8");
36985
- await fs18.promises.rename(tempPath, filePath);
37652
+ await fs19.promises.writeFile(tempPath, data, "utf-8");
37653
+ await fs19.promises.rename(tempPath, filePath);
36986
37654
  } catch (error) {
36987
37655
  try {
36988
- await fs18.promises.unlink(tempPath);
37656
+ await fs19.promises.unlink(tempPath);
36989
37657
  } catch {
36990
37658
  }
36991
37659
  throw error;
@@ -37006,7 +37674,7 @@ var FileContextStorage = class {
37006
37674
  const sanitizedSessionId = sanitizeId(sessionId);
37007
37675
  const filePath = this.getFilePath(sanitizedSessionId);
37008
37676
  try {
37009
- await fs18.promises.unlink(filePath);
37677
+ await fs19.promises.unlink(filePath);
37010
37678
  } catch (error) {
37011
37679
  if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
37012
37680
  throw error;
@@ -37021,7 +37689,7 @@ var FileContextStorage = class {
37021
37689
  const sanitizedSessionId = sanitizeId(sessionId);
37022
37690
  const filePath = this.getFilePath(sanitizedSessionId);
37023
37691
  try {
37024
- await fs18.promises.access(filePath);
37692
+ await fs19.promises.access(filePath);
37025
37693
  return true;
37026
37694
  } catch {
37027
37695
  return false;
@@ -37086,7 +37754,7 @@ var FileContextStorage = class {
37086
37754
  const sanitizedSessionId = sanitizeId(sessionId);
37087
37755
  const filePath = this.getFilePath(sanitizedSessionId);
37088
37756
  const data = this.prettyPrint ? JSON.stringify(stored, null, 2) : JSON.stringify(stored);
37089
- await fs18.promises.writeFile(filePath, data, "utf-8");
37757
+ await fs19.promises.writeFile(filePath, data, "utf-8");
37090
37758
  await this.updateIndex(stored);
37091
37759
  }
37092
37760
  /**
@@ -37114,13 +37782,13 @@ var FileContextStorage = class {
37114
37782
  */
37115
37783
  async rebuildIndex() {
37116
37784
  await this.ensureDirectory();
37117
- const files = await fs18.promises.readdir(this.sessionsDirectory);
37785
+ const files = await fs19.promises.readdir(this.sessionsDirectory);
37118
37786
  const sessionFiles = files.filter((f) => f.endsWith(".json") && !f.startsWith("_"));
37119
37787
  const entries = [];
37120
37788
  for (const file of sessionFiles) {
37121
37789
  try {
37122
37790
  const filePath = path2.join(this.sessionsDirectory, file);
37123
- const data = await fs18.promises.readFile(filePath, "utf-8");
37791
+ const data = await fs19.promises.readFile(filePath, "utf-8");
37124
37792
  const stored = JSON.parse(data);
37125
37793
  entries.push(this.storedToIndexEntry(stored));
37126
37794
  } catch {
@@ -37142,7 +37810,7 @@ var FileContextStorage = class {
37142
37810
  }
37143
37811
  async ensureDirectory() {
37144
37812
  try {
37145
- await fs18.promises.mkdir(this.sessionsDirectory, { recursive: true });
37813
+ await fs19.promises.mkdir(this.sessionsDirectory, { recursive: true });
37146
37814
  } catch (error) {
37147
37815
  if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
37148
37816
  throw error;
@@ -37152,7 +37820,7 @@ var FileContextStorage = class {
37152
37820
  async loadRaw(sanitizedSessionId) {
37153
37821
  const filePath = this.getFilePath(sanitizedSessionId);
37154
37822
  try {
37155
- const data = await fs18.promises.readFile(filePath, "utf-8");
37823
+ const data = await fs19.promises.readFile(filePath, "utf-8");
37156
37824
  return JSON.parse(data);
37157
37825
  } catch (error) {
37158
37826
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
@@ -37170,7 +37838,7 @@ var FileContextStorage = class {
37170
37838
  return this.index;
37171
37839
  }
37172
37840
  try {
37173
- const data = await fs18.promises.readFile(this.indexPath, "utf-8");
37841
+ const data = await fs19.promises.readFile(this.indexPath, "utf-8");
37174
37842
  this.index = JSON.parse(data);
37175
37843
  return this.index;
37176
37844
  } catch (error) {
@@ -37191,7 +37859,7 @@ var FileContextStorage = class {
37191
37859
  await this.ensureDirectory();
37192
37860
  this.index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
37193
37861
  const data = this.prettyPrint ? JSON.stringify(this.index, null, 2) : JSON.stringify(this.index);
37194
- await fs18.promises.writeFile(this.indexPath, data, "utf-8");
37862
+ await fs19.promises.writeFile(this.indexPath, data, "utf-8");
37195
37863
  }
37196
37864
  async updateIndex(stored) {
37197
37865
  const index = await this.loadIndex();
@@ -37265,11 +37933,11 @@ var FileAgentDefinitionStorage = class {
37265
37933
  const data = this.prettyPrint ? JSON.stringify(definition, null, 2) : JSON.stringify(definition);
37266
37934
  const tempPath = `${filePath}.tmp`;
37267
37935
  try {
37268
- await fs18.promises.writeFile(tempPath, data, "utf-8");
37269
- await fs18.promises.rename(tempPath, filePath);
37936
+ await fs19.promises.writeFile(tempPath, data, "utf-8");
37937
+ await fs19.promises.rename(tempPath, filePath);
37270
37938
  } catch (error) {
37271
37939
  try {
37272
- await fs18.promises.unlink(tempPath);
37940
+ await fs19.promises.unlink(tempPath);
37273
37941
  } catch {
37274
37942
  }
37275
37943
  throw error;
@@ -37291,7 +37959,7 @@ var FileAgentDefinitionStorage = class {
37291
37959
  const agentDir = path2.join(this.baseDirectory, sanitizedId);
37292
37960
  const filePath = path2.join(agentDir, "definition.json");
37293
37961
  try {
37294
- await fs18.promises.unlink(filePath);
37962
+ await fs19.promises.unlink(filePath);
37295
37963
  } catch (error) {
37296
37964
  if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
37297
37965
  throw error;
@@ -37306,7 +37974,7 @@ var FileAgentDefinitionStorage = class {
37306
37974
  const sanitizedId = sanitizeAgentId2(agentId);
37307
37975
  const filePath = path2.join(this.baseDirectory, sanitizedId, "definition.json");
37308
37976
  try {
37309
- await fs18.promises.access(filePath);
37977
+ await fs19.promises.access(filePath);
37310
37978
  return true;
37311
37979
  } catch {
37312
37980
  return false;
@@ -37368,13 +38036,13 @@ var FileAgentDefinitionStorage = class {
37368
38036
  */
37369
38037
  async rebuildIndex() {
37370
38038
  await this.ensureDirectory(this.baseDirectory);
37371
- const entries = await fs18.promises.readdir(this.baseDirectory, { withFileTypes: true });
38039
+ const entries = await fs19.promises.readdir(this.baseDirectory, { withFileTypes: true });
37372
38040
  const agentDirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith("_"));
37373
38041
  const indexEntries = [];
37374
38042
  for (const dir of agentDirs) {
37375
38043
  try {
37376
38044
  const filePath = path2.join(this.baseDirectory, dir.name, "definition.json");
37377
- const data = await fs18.promises.readFile(filePath, "utf-8");
38045
+ const data = await fs19.promises.readFile(filePath, "utf-8");
37378
38046
  const definition = JSON.parse(data);
37379
38047
  indexEntries.push(this.definitionToIndexEntry(definition));
37380
38048
  } catch {
@@ -37392,7 +38060,7 @@ var FileAgentDefinitionStorage = class {
37392
38060
  // ==========================================================================
37393
38061
  async ensureDirectory(dir) {
37394
38062
  try {
37395
- await fs18.promises.mkdir(dir, { recursive: true });
38063
+ await fs19.promises.mkdir(dir, { recursive: true });
37396
38064
  } catch (error) {
37397
38065
  if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
37398
38066
  throw error;
@@ -37402,7 +38070,7 @@ var FileAgentDefinitionStorage = class {
37402
38070
  async loadRaw(sanitizedId) {
37403
38071
  const filePath = path2.join(this.baseDirectory, sanitizedId, "definition.json");
37404
38072
  try {
37405
- const data = await fs18.promises.readFile(filePath, "utf-8");
38073
+ const data = await fs19.promises.readFile(filePath, "utf-8");
37406
38074
  return JSON.parse(data);
37407
38075
  } catch (error) {
37408
38076
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
@@ -37420,7 +38088,7 @@ var FileAgentDefinitionStorage = class {
37420
38088
  return this.index;
37421
38089
  }
37422
38090
  try {
37423
- const data = await fs18.promises.readFile(this.indexPath, "utf-8");
38091
+ const data = await fs19.promises.readFile(this.indexPath, "utf-8");
37424
38092
  this.index = JSON.parse(data);
37425
38093
  return this.index;
37426
38094
  } catch (error) {
@@ -37440,7 +38108,7 @@ var FileAgentDefinitionStorage = class {
37440
38108
  await this.ensureDirectory(this.baseDirectory);
37441
38109
  this.index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
37442
38110
  const data = this.prettyPrint ? JSON.stringify(this.index, null, 2) : JSON.stringify(this.index);
37443
- await fs18.promises.writeFile(this.indexPath, data, "utf-8");
38111
+ await fs19.promises.writeFile(this.indexPath, data, "utf-8");
37444
38112
  }
37445
38113
  async updateIndex(definition) {
37446
38114
  const index = await this.loadIndex();
@@ -37498,10 +38166,10 @@ var FileMediaStorage = class {
37498
38166
  }
37499
38167
  async save(data, metadata) {
37500
38168
  const dir = metadata.userId ? path2__namespace.join(this.outputDir, metadata.userId) : this.outputDir;
37501
- await fs17__namespace.mkdir(dir, { recursive: true });
38169
+ await fs18__namespace.mkdir(dir, { recursive: true });
37502
38170
  const filename = metadata.suggestedFilename ?? this.generateFilename(metadata);
37503
38171
  const filePath = path2__namespace.join(dir, filename);
37504
- await fs17__namespace.writeFile(filePath, data);
38172
+ await fs18__namespace.writeFile(filePath, data);
37505
38173
  const format = metadata.format.toLowerCase();
37506
38174
  const mimeType = MIME_TYPES2[format] ?? "application/octet-stream";
37507
38175
  return {
@@ -37512,7 +38180,7 @@ var FileMediaStorage = class {
37512
38180
  }
37513
38181
  async read(location) {
37514
38182
  try {
37515
- return await fs17__namespace.readFile(location);
38183
+ return await fs18__namespace.readFile(location);
37516
38184
  } catch (err) {
37517
38185
  if (err.code === "ENOENT") {
37518
38186
  return null;
@@ -37522,7 +38190,7 @@ var FileMediaStorage = class {
37522
38190
  }
37523
38191
  async delete(location) {
37524
38192
  try {
37525
- await fs17__namespace.unlink(location);
38193
+ await fs18__namespace.unlink(location);
37526
38194
  } catch (err) {
37527
38195
  if (err.code === "ENOENT") {
37528
38196
  return;
@@ -37532,7 +38200,7 @@ var FileMediaStorage = class {
37532
38200
  }
37533
38201
  async exists(location) {
37534
38202
  try {
37535
- await fs17__namespace.access(location);
38203
+ await fs18__namespace.access(location);
37536
38204
  return true;
37537
38205
  } catch {
37538
38206
  return false;
@@ -37541,11 +38209,11 @@ var FileMediaStorage = class {
37541
38209
  async list(options) {
37542
38210
  await this.ensureDir();
37543
38211
  let entries = [];
37544
- const files = await fs17__namespace.readdir(this.outputDir);
38212
+ const files = await fs18__namespace.readdir(this.outputDir);
37545
38213
  for (const file of files) {
37546
38214
  const filePath = path2__namespace.join(this.outputDir, file);
37547
38215
  try {
37548
- const stat6 = await fs17__namespace.stat(filePath);
38216
+ const stat6 = await fs18__namespace.stat(filePath);
37549
38217
  if (!stat6.isFile()) continue;
37550
38218
  const ext = path2__namespace.extname(file).slice(1).toLowerCase();
37551
38219
  const mimeType = MIME_TYPES2[ext] ?? "application/octet-stream";
@@ -37585,7 +38253,7 @@ var FileMediaStorage = class {
37585
38253
  }
37586
38254
  async ensureDir() {
37587
38255
  if (!this.initialized) {
37588
- await fs17__namespace.mkdir(this.outputDir, { recursive: true });
38256
+ await fs18__namespace.mkdir(this.outputDir, { recursive: true });
37589
38257
  this.initialized = true;
37590
38258
  }
37591
38259
  }
@@ -37650,11 +38318,11 @@ var FileCustomToolStorage = class {
37650
38318
  const data = this.prettyPrint ? JSON.stringify(definition, null, 2) : JSON.stringify(definition);
37651
38319
  const tempPath = `${filePath}.tmp`;
37652
38320
  try {
37653
- await fs18.promises.writeFile(tempPath, data, "utf-8");
37654
- await fs18.promises.rename(tempPath, filePath);
38321
+ await fs19.promises.writeFile(tempPath, data, "utf-8");
38322
+ await fs19.promises.rename(tempPath, filePath);
37655
38323
  } catch (error) {
37656
38324
  try {
37657
- await fs18.promises.unlink(tempPath);
38325
+ await fs19.promises.unlink(tempPath);
37658
38326
  } catch {
37659
38327
  }
37660
38328
  throw error;
@@ -37668,7 +38336,7 @@ var FileCustomToolStorage = class {
37668
38336
  const sanitized = sanitizeName(name);
37669
38337
  const filePath = this.getToolPath(userId, sanitized);
37670
38338
  try {
37671
- const data = await fs18.promises.readFile(filePath, "utf-8");
38339
+ const data = await fs19.promises.readFile(filePath, "utf-8");
37672
38340
  return JSON.parse(data);
37673
38341
  } catch (error) {
37674
38342
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
@@ -37687,7 +38355,7 @@ var FileCustomToolStorage = class {
37687
38355
  const sanitized = sanitizeName(name);
37688
38356
  const filePath = this.getToolPath(userId, sanitized);
37689
38357
  try {
37690
- await fs18.promises.unlink(filePath);
38358
+ await fs19.promises.unlink(filePath);
37691
38359
  } catch (error) {
37692
38360
  if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
37693
38361
  throw error;
@@ -37702,7 +38370,7 @@ var FileCustomToolStorage = class {
37702
38370
  const sanitized = sanitizeName(name);
37703
38371
  const filePath = this.getToolPath(userId, sanitized);
37704
38372
  try {
37705
- await fs18.promises.access(filePath);
38373
+ await fs19.promises.access(filePath);
37706
38374
  return true;
37707
38375
  } catch {
37708
38376
  return false;
@@ -37773,7 +38441,7 @@ var FileCustomToolStorage = class {
37773
38441
  // ==========================================================================
37774
38442
  async ensureDirectory(dir) {
37775
38443
  try {
37776
- await fs18.promises.mkdir(dir, { recursive: true });
38444
+ await fs19.promises.mkdir(dir, { recursive: true });
37777
38445
  } catch (error) {
37778
38446
  if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
37779
38447
  throw error;
@@ -37783,7 +38451,7 @@ var FileCustomToolStorage = class {
37783
38451
  async loadIndex(userId) {
37784
38452
  const indexPath = this.getUserIndexPath(userId);
37785
38453
  try {
37786
- const data = await fs18.promises.readFile(indexPath, "utf-8");
38454
+ const data = await fs19.promises.readFile(indexPath, "utf-8");
37787
38455
  return JSON.parse(data);
37788
38456
  } catch (error) {
37789
38457
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
@@ -37802,7 +38470,7 @@ var FileCustomToolStorage = class {
37802
38470
  await this.ensureDirectory(directory);
37803
38471
  index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
37804
38472
  const data = this.prettyPrint ? JSON.stringify(index, null, 2) : JSON.stringify(index);
37805
- await fs18.promises.writeFile(indexPath, data, "utf-8");
38473
+ await fs19.promises.writeFile(indexPath, data, "utf-8");
37806
38474
  }
37807
38475
  async updateIndex(userId, definition) {
37808
38476
  const index = await this.loadIndex(userId);
@@ -37835,6 +38503,234 @@ var FileCustomToolStorage = class {
37835
38503
  function createFileCustomToolStorage(config) {
37836
38504
  return new FileCustomToolStorage(config);
37837
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
+ }
37838
38734
 
37839
38735
  // src/domain/entities/CustomToolDefinition.ts
37840
38736
  var CUSTOM_TOOL_DEFINITION_VERSION = 1;
@@ -38935,8 +39831,8 @@ var FileStorage = class {
38935
39831
  }
38936
39832
  async ensureDirectory() {
38937
39833
  try {
38938
- await fs17__namespace.mkdir(this.directory, { recursive: true });
38939
- await fs17__namespace.chmod(this.directory, 448);
39834
+ await fs18__namespace.mkdir(this.directory, { recursive: true });
39835
+ await fs18__namespace.chmod(this.directory, 448);
38940
39836
  } catch (error) {
38941
39837
  }
38942
39838
  }
@@ -38952,13 +39848,13 @@ var FileStorage = class {
38952
39848
  const filePath = this.getFilePath(key);
38953
39849
  const plaintext = JSON.stringify(token);
38954
39850
  const encrypted = encrypt(plaintext, this.encryptionKey);
38955
- await fs17__namespace.writeFile(filePath, encrypted, "utf8");
38956
- await fs17__namespace.chmod(filePath, 384);
39851
+ await fs18__namespace.writeFile(filePath, encrypted, "utf8");
39852
+ await fs18__namespace.chmod(filePath, 384);
38957
39853
  }
38958
39854
  async getToken(key) {
38959
39855
  const filePath = this.getFilePath(key);
38960
39856
  try {
38961
- const encrypted = await fs17__namespace.readFile(filePath, "utf8");
39857
+ const encrypted = await fs18__namespace.readFile(filePath, "utf8");
38962
39858
  const decrypted = decrypt(encrypted, this.encryptionKey);
38963
39859
  return JSON.parse(decrypted);
38964
39860
  } catch (error) {
@@ -38967,7 +39863,7 @@ var FileStorage = class {
38967
39863
  }
38968
39864
  console.error("Failed to read/decrypt token file:", error);
38969
39865
  try {
38970
- await fs17__namespace.unlink(filePath);
39866
+ await fs18__namespace.unlink(filePath);
38971
39867
  } catch {
38972
39868
  }
38973
39869
  return null;
@@ -38976,7 +39872,7 @@ var FileStorage = class {
38976
39872
  async deleteToken(key) {
38977
39873
  const filePath = this.getFilePath(key);
38978
39874
  try {
38979
- await fs17__namespace.unlink(filePath);
39875
+ await fs18__namespace.unlink(filePath);
38980
39876
  } catch (error) {
38981
39877
  if (error.code !== "ENOENT") {
38982
39878
  throw error;
@@ -38986,7 +39882,7 @@ var FileStorage = class {
38986
39882
  async hasToken(key) {
38987
39883
  const filePath = this.getFilePath(key);
38988
39884
  try {
38989
- await fs17__namespace.access(filePath);
39885
+ await fs18__namespace.access(filePath);
38990
39886
  return true;
38991
39887
  } catch {
38992
39888
  return false;
@@ -38997,7 +39893,7 @@ var FileStorage = class {
38997
39893
  */
38998
39894
  async listTokens() {
38999
39895
  try {
39000
- const files = await fs17__namespace.readdir(this.directory);
39896
+ const files = await fs18__namespace.readdir(this.directory);
39001
39897
  return files.filter((f) => f.endsWith(".token")).map((f) => f.replace(".token", ""));
39002
39898
  } catch {
39003
39899
  return [];
@@ -39008,10 +39904,10 @@ var FileStorage = class {
39008
39904
  */
39009
39905
  async clearAll() {
39010
39906
  try {
39011
- const files = await fs17__namespace.readdir(this.directory);
39907
+ const files = await fs18__namespace.readdir(this.directory);
39012
39908
  const tokenFiles = files.filter((f) => f.endsWith(".token"));
39013
39909
  await Promise.all(
39014
- tokenFiles.map((f) => fs17__namespace.unlink(path2__namespace.join(this.directory, f)).catch(() => {
39910
+ tokenFiles.map((f) => fs18__namespace.unlink(path2__namespace.join(this.directory, f)).catch(() => {
39015
39911
  }))
39016
39912
  );
39017
39913
  } catch {
@@ -39459,14 +40355,14 @@ var FileConnectorStorage = class {
39459
40355
  await this.ensureDirectory();
39460
40356
  const filePath = this.getFilePath(name);
39461
40357
  const json = JSON.stringify(stored, null, 2);
39462
- await fs17__namespace.writeFile(filePath, json, "utf8");
39463
- await fs17__namespace.chmod(filePath, 384);
40358
+ await fs18__namespace.writeFile(filePath, json, "utf8");
40359
+ await fs18__namespace.chmod(filePath, 384);
39464
40360
  await this.updateIndex(name, "add");
39465
40361
  }
39466
40362
  async get(name) {
39467
40363
  const filePath = this.getFilePath(name);
39468
40364
  try {
39469
- const json = await fs17__namespace.readFile(filePath, "utf8");
40365
+ const json = await fs18__namespace.readFile(filePath, "utf8");
39470
40366
  return JSON.parse(json);
39471
40367
  } catch (error) {
39472
40368
  const err = error;
@@ -39479,7 +40375,7 @@ var FileConnectorStorage = class {
39479
40375
  async delete(name) {
39480
40376
  const filePath = this.getFilePath(name);
39481
40377
  try {
39482
- await fs17__namespace.unlink(filePath);
40378
+ await fs18__namespace.unlink(filePath);
39483
40379
  await this.updateIndex(name, "remove");
39484
40380
  return true;
39485
40381
  } catch (error) {
@@ -39493,7 +40389,7 @@ var FileConnectorStorage = class {
39493
40389
  async has(name) {
39494
40390
  const filePath = this.getFilePath(name);
39495
40391
  try {
39496
- await fs17__namespace.access(filePath);
40392
+ await fs18__namespace.access(filePath);
39497
40393
  return true;
39498
40394
  } catch {
39499
40395
  return false;
@@ -39519,13 +40415,13 @@ var FileConnectorStorage = class {
39519
40415
  */
39520
40416
  async clear() {
39521
40417
  try {
39522
- const files = await fs17__namespace.readdir(this.directory);
40418
+ const files = await fs18__namespace.readdir(this.directory);
39523
40419
  const connectorFiles = files.filter(
39524
40420
  (f) => f.endsWith(".connector.json") || f === "_index.json"
39525
40421
  );
39526
40422
  await Promise.all(
39527
40423
  connectorFiles.map(
39528
- (f) => fs17__namespace.unlink(path2__namespace.join(this.directory, f)).catch(() => {
40424
+ (f) => fs18__namespace.unlink(path2__namespace.join(this.directory, f)).catch(() => {
39529
40425
  })
39530
40426
  )
39531
40427
  );
@@ -39552,8 +40448,8 @@ var FileConnectorStorage = class {
39552
40448
  async ensureDirectory() {
39553
40449
  if (this.initialized) return;
39554
40450
  try {
39555
- await fs17__namespace.mkdir(this.directory, { recursive: true });
39556
- await fs17__namespace.chmod(this.directory, 448);
40451
+ await fs18__namespace.mkdir(this.directory, { recursive: true });
40452
+ await fs18__namespace.chmod(this.directory, 448);
39557
40453
  this.initialized = true;
39558
40454
  } catch {
39559
40455
  this.initialized = true;
@@ -39564,7 +40460,7 @@ var FileConnectorStorage = class {
39564
40460
  */
39565
40461
  async loadIndex() {
39566
40462
  try {
39567
- const json = await fs17__namespace.readFile(this.indexPath, "utf8");
40463
+ const json = await fs18__namespace.readFile(this.indexPath, "utf8");
39568
40464
  return JSON.parse(json);
39569
40465
  } catch {
39570
40466
  return { connectors: {} };
@@ -39582,8 +40478,8 @@ var FileConnectorStorage = class {
39582
40478
  delete index.connectors[hash];
39583
40479
  }
39584
40480
  const json = JSON.stringify(index, null, 2);
39585
- await fs17__namespace.writeFile(this.indexPath, json, "utf8");
39586
- await fs17__namespace.chmod(this.indexPath, 384);
40481
+ await fs18__namespace.writeFile(this.indexPath, json, "utf8");
40482
+ await fs18__namespace.chmod(this.indexPath, 384);
39587
40483
  }
39588
40484
  };
39589
40485
 
@@ -42038,8 +42934,8 @@ function createMessageWithImages(text, imageUrls, role = "user" /* USER */) {
42038
42934
  var execAsync = util.promisify(child_process.exec);
42039
42935
  function cleanupTempFile(filePath) {
42040
42936
  try {
42041
- if (fs18__namespace.existsSync(filePath)) {
42042
- fs18__namespace.unlinkSync(filePath);
42937
+ if (fs19__namespace.existsSync(filePath)) {
42938
+ fs19__namespace.unlinkSync(filePath);
42043
42939
  }
42044
42940
  } catch {
42045
42941
  }
@@ -42090,7 +42986,7 @@ async function readClipboardImageMac() {
42090
42986
  end try
42091
42987
  `;
42092
42988
  const { stdout } = await execAsync(`osascript -e '${script}'`);
42093
- if (stdout.includes("success") || fs18__namespace.existsSync(tempFile)) {
42989
+ if (stdout.includes("success") || fs19__namespace.existsSync(tempFile)) {
42094
42990
  return await convertFileToDataUri(tempFile);
42095
42991
  }
42096
42992
  return {
@@ -42107,14 +43003,14 @@ async function readClipboardImageLinux() {
42107
43003
  try {
42108
43004
  try {
42109
43005
  await execAsync(`xclip -selection clipboard -t image/png -o > "${tempFile}"`);
42110
- if (fs18__namespace.existsSync(tempFile) && fs18__namespace.statSync(tempFile).size > 0) {
43006
+ if (fs19__namespace.existsSync(tempFile) && fs19__namespace.statSync(tempFile).size > 0) {
42111
43007
  return await convertFileToDataUri(tempFile);
42112
43008
  }
42113
43009
  } catch {
42114
43010
  }
42115
43011
  try {
42116
43012
  await execAsync(`wl-paste -t image/png > "${tempFile}"`);
42117
- if (fs18__namespace.existsSync(tempFile) && fs18__namespace.statSync(tempFile).size > 0) {
43013
+ if (fs19__namespace.existsSync(tempFile) && fs19__namespace.statSync(tempFile).size > 0) {
42118
43014
  return await convertFileToDataUri(tempFile);
42119
43015
  }
42120
43016
  } catch {
@@ -42141,7 +43037,7 @@ async function readClipboardImageWindows() {
42141
43037
  }
42142
43038
  `;
42143
43039
  await execAsync(`powershell -Command "${psScript}"`);
42144
- if (fs18__namespace.existsSync(tempFile) && fs18__namespace.statSync(tempFile).size > 0) {
43040
+ if (fs19__namespace.existsSync(tempFile) && fs19__namespace.statSync(tempFile).size > 0) {
42145
43041
  return await convertFileToDataUri(tempFile);
42146
43042
  }
42147
43043
  return {
@@ -42154,7 +43050,7 @@ async function readClipboardImageWindows() {
42154
43050
  }
42155
43051
  async function convertFileToDataUri(filePath) {
42156
43052
  try {
42157
- const imageBuffer = fs18__namespace.readFileSync(filePath);
43053
+ const imageBuffer = fs19__namespace.readFileSync(filePath);
42158
43054
  const base64Image = imageBuffer.toString("base64");
42159
43055
  const magic = imageBuffer.slice(0, 4).toString("hex");
42160
43056
  let mimeType = "image/png";
@@ -42213,186 +43109,6 @@ async function hasClipboardImage() {
42213
43109
  }
42214
43110
  }
42215
43111
 
42216
- // src/utils/jsonExtractor.ts
42217
- function extractJSON(text) {
42218
- if (!text || typeof text !== "string") {
42219
- return {
42220
- success: false,
42221
- error: "Input is empty or not a string"
42222
- };
42223
- }
42224
- const trimmedText = text.trim();
42225
- const codeBlockResult = extractFromCodeBlock(trimmedText);
42226
- if (codeBlockResult.success) {
42227
- return codeBlockResult;
42228
- }
42229
- const inlineResult = extractInlineJSON(trimmedText);
42230
- if (inlineResult.success) {
42231
- return inlineResult;
42232
- }
42233
- try {
42234
- const data = JSON.parse(trimmedText);
42235
- return {
42236
- success: true,
42237
- data,
42238
- rawJson: trimmedText,
42239
- method: "raw"
42240
- };
42241
- } catch (e) {
42242
- return {
42243
- success: false,
42244
- error: `Could not extract JSON from text: ${e instanceof Error ? e.message : String(e)}`
42245
- };
42246
- }
42247
- }
42248
- function extractFromCodeBlock(text) {
42249
- const codeBlockRegex = /```(?:json)?\s*([\s\S]*?)```/g;
42250
- let match;
42251
- while ((match = codeBlockRegex.exec(text)) !== null) {
42252
- const content = match[1];
42253
- if (content) {
42254
- const trimmed = content.trim();
42255
- try {
42256
- const data = JSON.parse(trimmed);
42257
- return {
42258
- success: true,
42259
- data,
42260
- rawJson: trimmed,
42261
- method: "code_block"
42262
- };
42263
- } catch {
42264
- continue;
42265
- }
42266
- }
42267
- }
42268
- return { success: false };
42269
- }
42270
- function extractInlineJSON(text) {
42271
- const objectMatch = findJSONObject(text);
42272
- if (objectMatch) {
42273
- try {
42274
- const data = JSON.parse(objectMatch);
42275
- return {
42276
- success: true,
42277
- data,
42278
- rawJson: objectMatch,
42279
- method: "inline"
42280
- };
42281
- } catch {
42282
- }
42283
- }
42284
- const arrayMatch = findJSONArray(text);
42285
- if (arrayMatch) {
42286
- try {
42287
- const data = JSON.parse(arrayMatch);
42288
- return {
42289
- success: true,
42290
- data,
42291
- rawJson: arrayMatch,
42292
- method: "inline"
42293
- };
42294
- } catch {
42295
- }
42296
- }
42297
- return { success: false };
42298
- }
42299
- function findJSONObject(text) {
42300
- const startIndex = text.indexOf("{");
42301
- if (startIndex === -1) return null;
42302
- let depth = 0;
42303
- let inString = false;
42304
- let escaped = false;
42305
- for (let i = startIndex; i < text.length; i++) {
42306
- const char = text[i];
42307
- if (escaped) {
42308
- escaped = false;
42309
- continue;
42310
- }
42311
- if (char === "\\" && inString) {
42312
- escaped = true;
42313
- continue;
42314
- }
42315
- if (char === '"') {
42316
- inString = !inString;
42317
- continue;
42318
- }
42319
- if (inString) continue;
42320
- if (char === "{") {
42321
- depth++;
42322
- } else if (char === "}") {
42323
- depth--;
42324
- if (depth === 0) {
42325
- return text.slice(startIndex, i + 1);
42326
- }
42327
- }
42328
- }
42329
- return null;
42330
- }
42331
- function findJSONArray(text) {
42332
- const startIndex = text.indexOf("[");
42333
- if (startIndex === -1) return null;
42334
- let depth = 0;
42335
- let inString = false;
42336
- let escaped = false;
42337
- for (let i = startIndex; i < text.length; i++) {
42338
- const char = text[i];
42339
- if (escaped) {
42340
- escaped = false;
42341
- continue;
42342
- }
42343
- if (char === "\\" && inString) {
42344
- escaped = true;
42345
- continue;
42346
- }
42347
- if (char === '"') {
42348
- inString = !inString;
42349
- continue;
42350
- }
42351
- if (inString) continue;
42352
- if (char === "[") {
42353
- depth++;
42354
- } else if (char === "]") {
42355
- depth--;
42356
- if (depth === 0) {
42357
- return text.slice(startIndex, i + 1);
42358
- }
42359
- }
42360
- }
42361
- return null;
42362
- }
42363
- function extractJSONField(text, field, defaultValue) {
42364
- const result = extractJSON(text);
42365
- if (result.success && result.data && field in result.data) {
42366
- return result.data[field];
42367
- }
42368
- return defaultValue;
42369
- }
42370
- function extractNumber(text, patterns = [
42371
- /(\d{1,3})%?\s*(?:complete|score|percent)/i,
42372
- /(?:score|completion|rating)[:\s]+(\d{1,3})/i,
42373
- /(\d{1,3})\s*(?:out of|\/)\s*100/i
42374
- ], defaultValue = 0) {
42375
- const jsonResult = extractJSON(text);
42376
- if (jsonResult.success && jsonResult.data) {
42377
- const scoreFields = ["score", "completionScore", "completion_score", "rating", "percent", "value"];
42378
- for (const field of scoreFields) {
42379
- if (field in jsonResult.data && typeof jsonResult.data[field] === "number") {
42380
- return jsonResult.data[field];
42381
- }
42382
- }
42383
- }
42384
- for (const pattern of patterns) {
42385
- const match = text.match(pattern);
42386
- if (match && match[1]) {
42387
- const num = parseInt(match[1], 10);
42388
- if (!isNaN(num)) {
42389
- return num;
42390
- }
42391
- }
42392
- }
42393
- return defaultValue;
42394
- }
42395
-
42396
43112
  // src/tools/index.ts
42397
43113
  var tools_exports = {};
42398
43114
  __export(tools_exports, {
@@ -42428,19 +43144,25 @@ __export(tools_exports, {
42428
43144
  createDesktopScreenshotTool: () => createDesktopScreenshotTool,
42429
43145
  createDesktopWindowFocusTool: () => createDesktopWindowFocusTool,
42430
43146
  createDesktopWindowListTool: () => createDesktopWindowListTool,
43147
+ createDraftEmailTool: () => createDraftEmailTool,
42431
43148
  createEditFileTool: () => createEditFileTool,
43149
+ createEditMeetingTool: () => createEditMeetingTool,
42432
43150
  createExecuteJavaScriptTool: () => createExecuteJavaScriptTool,
43151
+ createFindMeetingSlotsTool: () => createFindMeetingSlotsTool,
43152
+ createGetMeetingTranscriptTool: () => createGetMeetingTranscriptTool,
42433
43153
  createGetPRTool: () => createGetPRTool,
42434
43154
  createGitHubReadFileTool: () => createGitHubReadFileTool,
42435
43155
  createGlobTool: () => createGlobTool,
42436
43156
  createGrepTool: () => createGrepTool,
42437
43157
  createImageGenerationTool: () => createImageGenerationTool,
42438
43158
  createListDirectoryTool: () => createListDirectoryTool,
43159
+ createMeetingTool: () => createMeetingTool,
42439
43160
  createPRCommentsTool: () => createPRCommentsTool,
42440
43161
  createPRFilesTool: () => createPRFilesTool,
42441
43162
  createReadFileTool: () => createReadFileTool,
42442
43163
  createSearchCodeTool: () => createSearchCodeTool,
42443
43164
  createSearchFilesTool: () => createSearchFilesTool,
43165
+ createSendEmailTool: () => createSendEmailTool,
42444
43166
  createSpeechToTextTool: () => createSpeechToTextTool,
42445
43167
  createTextToSpeechTool: () => createTextToSpeechTool,
42446
43168
  createVideoTools: () => createVideoTools,
@@ -42470,6 +43192,8 @@ __export(tools_exports, {
42470
43192
  executeInVM: () => executeInVM,
42471
43193
  executeJavaScript: () => executeJavaScript,
42472
43194
  expandTilde: () => expandTilde,
43195
+ formatAttendees: () => formatAttendees,
43196
+ formatRecipients: () => formatRecipients,
42473
43197
  getAllBuiltInTools: () => getAllBuiltInTools,
42474
43198
  getBackgroundOutput: () => getBackgroundOutput,
42475
43199
  getDesktopDriver: () => getDesktopDriver,
@@ -42480,19 +43204,24 @@ __export(tools_exports, {
42480
43204
  getToolRegistry: () => getToolRegistry,
42481
43205
  getToolsByCategory: () => getToolsByCategory,
42482
43206
  getToolsRequiringConnector: () => getToolsRequiringConnector,
43207
+ getUserPathPrefix: () => getUserPathPrefix,
42483
43208
  glob: () => glob,
42484
43209
  grep: () => grep,
42485
43210
  hydrateCustomTool: () => hydrateCustomTool,
42486
43211
  isBlockedCommand: () => isBlockedCommand,
42487
43212
  isExcludedExtension: () => isExcludedExtension,
43213
+ isTeamsMeetingUrl: () => isTeamsMeetingUrl,
42488
43214
  jsonManipulator: () => jsonManipulator,
42489
43215
  killBackgroundProcess: () => killBackgroundProcess,
42490
43216
  listDirectory: () => listDirectory,
42491
43217
  mergeTextPieces: () => mergeTextPieces,
43218
+ microsoftFetch: () => microsoftFetch,
43219
+ normalizeEmails: () => normalizeEmails,
42492
43220
  parseKeyCombo: () => parseKeyCombo,
42493
43221
  parseRepository: () => parseRepository,
42494
43222
  readFile: () => readFile5,
42495
43223
  resetDefaultDriver: () => resetDefaultDriver,
43224
+ resolveMeetingId: () => resolveMeetingId,
42496
43225
  resolveRepository: () => resolveRepository,
42497
43226
  setMediaOutputHandler: () => setMediaOutputHandler,
42498
43227
  setMediaStorage: () => setMediaStorage,
@@ -42703,7 +43432,7 @@ EXAMPLES:
42703
43432
  };
42704
43433
  }
42705
43434
  const resolvedPath = validation.resolvedPath;
42706
- if (!fs18.existsSync(resolvedPath)) {
43435
+ if (!fs19.existsSync(resolvedPath)) {
42707
43436
  return {
42708
43437
  success: false,
42709
43438
  error: `File not found: ${file_path}`,
@@ -42711,7 +43440,7 @@ EXAMPLES:
42711
43440
  };
42712
43441
  }
42713
43442
  try {
42714
- const stats = await fs17.stat(resolvedPath);
43443
+ const stats = await fs18.stat(resolvedPath);
42715
43444
  if (!stats.isFile()) {
42716
43445
  return {
42717
43446
  success: false,
@@ -42753,7 +43482,7 @@ EXAMPLES:
42753
43482
  } catch {
42754
43483
  }
42755
43484
  }
42756
- const content = await fs17.readFile(resolvedPath, "utf-8");
43485
+ const content = await fs18.readFile(resolvedPath, "utf-8");
42757
43486
  const allLines = content.split("\n");
42758
43487
  const totalLines = allLines.length;
42759
43488
  const startIndex = Math.max(0, offset - 1);
@@ -42858,13 +43587,13 @@ EXAMPLES:
42858
43587
  };
42859
43588
  }
42860
43589
  const resolvedPath = validation.resolvedPath;
42861
- const fileExists = fs18.existsSync(resolvedPath);
43590
+ const fileExists = fs19.existsSync(resolvedPath);
42862
43591
  try {
42863
43592
  const parentDir = path2.dirname(resolvedPath);
42864
- if (!fs18.existsSync(parentDir)) {
42865
- await fs17.mkdir(parentDir, { recursive: true });
43593
+ if (!fs19.existsSync(parentDir)) {
43594
+ await fs18.mkdir(parentDir, { recursive: true });
42866
43595
  }
42867
- await fs17.writeFile(resolvedPath, content, "utf-8");
43596
+ await fs18.writeFile(resolvedPath, content, "utf-8");
42868
43597
  return {
42869
43598
  success: true,
42870
43599
  path: file_path,
@@ -42967,7 +43696,7 @@ EXAMPLES:
42967
43696
  };
42968
43697
  }
42969
43698
  const resolvedPath = validation.resolvedPath;
42970
- if (!fs18.existsSync(resolvedPath)) {
43699
+ if (!fs19.existsSync(resolvedPath)) {
42971
43700
  return {
42972
43701
  success: false,
42973
43702
  error: `File not found: ${file_path}`,
@@ -42975,7 +43704,7 @@ EXAMPLES:
42975
43704
  };
42976
43705
  }
42977
43706
  try {
42978
- const content = await fs17.readFile(resolvedPath, "utf-8");
43707
+ const content = await fs18.readFile(resolvedPath, "utf-8");
42979
43708
  let occurrences = 0;
42980
43709
  let searchIndex = 0;
42981
43710
  while (true) {
@@ -43014,7 +43743,7 @@ EXAMPLES:
43014
43743
  } else {
43015
43744
  newContent = content.replace(old_string, new_string);
43016
43745
  }
43017
- await fs17.writeFile(resolvedPath, newContent, "utf-8");
43746
+ await fs18.writeFile(resolvedPath, newContent, "utf-8");
43018
43747
  const diffPreview = generateDiffPreview(old_string, new_string);
43019
43748
  return {
43020
43749
  success: true,
@@ -43070,7 +43799,7 @@ async function findFiles(dir, pattern, baseDir, config, results = [], depth = 0)
43070
43799
  return results;
43071
43800
  }
43072
43801
  try {
43073
- const entries = await fs17.readdir(dir, { withFileTypes: true });
43802
+ const entries = await fs18.readdir(dir, { withFileTypes: true });
43074
43803
  for (const entry of entries) {
43075
43804
  if (results.length >= config.maxResults) break;
43076
43805
  const fullPath = path2.join(dir, entry.name);
@@ -43084,7 +43813,7 @@ async function findFiles(dir, pattern, baseDir, config, results = [], depth = 0)
43084
43813
  } else if (entry.isFile()) {
43085
43814
  if (matchGlobPattern(pattern, relativePath)) {
43086
43815
  try {
43087
- const stats = await fs17.stat(fullPath);
43816
+ const stats = await fs18.stat(fullPath);
43088
43817
  results.push({
43089
43818
  path: relativePath,
43090
43819
  mtime: stats.mtimeMs
@@ -43166,7 +43895,7 @@ WHEN TO USE:
43166
43895
  };
43167
43896
  }
43168
43897
  const resolvedDir = validation.resolvedPath;
43169
- if (!fs18.existsSync(resolvedDir)) {
43898
+ if (!fs19.existsSync(resolvedDir)) {
43170
43899
  return {
43171
43900
  success: false,
43172
43901
  error: `Directory not found: ${searchDir}`
@@ -43221,7 +43950,7 @@ async function findFilesToSearch(dir, baseDir, config, globPattern, fileType, fi
43221
43950
  return files;
43222
43951
  }
43223
43952
  try {
43224
- const entries = await fs17.readdir(dir, { withFileTypes: true });
43953
+ const entries = await fs18.readdir(dir, { withFileTypes: true });
43225
43954
  for (const entry of entries) {
43226
43955
  const fullPath = path2.join(dir, entry.name);
43227
43956
  if (entry.isDirectory()) {
@@ -43254,7 +43983,7 @@ async function findFilesToSearch(dir, baseDir, config, globPattern, fileType, fi
43254
43983
  async function searchFile(filePath, regex, contextBefore, contextAfter) {
43255
43984
  const matches = [];
43256
43985
  try {
43257
- const content = await fs17.readFile(filePath, "utf-8");
43986
+ const content = await fs18.readFile(filePath, "utf-8");
43258
43987
  const lines = content.split("\n");
43259
43988
  for (let i = 0; i < lines.length; i++) {
43260
43989
  const line = lines[i] ?? "";
@@ -43395,7 +44124,7 @@ WHEN TO USE:
43395
44124
  };
43396
44125
  }
43397
44126
  const resolvedPath = validation.resolvedPath;
43398
- if (!fs18.existsSync(resolvedPath)) {
44127
+ if (!fs19.existsSync(resolvedPath)) {
43399
44128
  return {
43400
44129
  success: false,
43401
44130
  error: `Path not found: ${searchPath}`
@@ -43411,7 +44140,7 @@ WHEN TO USE:
43411
44140
  };
43412
44141
  }
43413
44142
  try {
43414
- const stats = await fs17.stat(resolvedPath);
44143
+ const stats = await fs18.stat(resolvedPath);
43415
44144
  let filesToSearch;
43416
44145
  if (stats.isFile()) {
43417
44146
  filesToSearch = [resolvedPath];
@@ -43499,7 +44228,7 @@ async function listDir(dir, baseDir, config, recursive, filter, maxDepth = 3, cu
43499
44228
  return entries;
43500
44229
  }
43501
44230
  try {
43502
- const dirEntries = await fs17.readdir(dir, { withFileTypes: true });
44231
+ const dirEntries = await fs18.readdir(dir, { withFileTypes: true });
43503
44232
  for (const entry of dirEntries) {
43504
44233
  if (entries.length >= config.maxResults) break;
43505
44234
  const fullPath = path2.join(dir, entry.name);
@@ -43517,7 +44246,7 @@ async function listDir(dir, baseDir, config, recursive, filter, maxDepth = 3, cu
43517
44246
  }
43518
44247
  if (filter === "directories" && !isDir) continue;
43519
44248
  try {
43520
- const stats = await fs17.stat(fullPath);
44249
+ const stats = await fs18.stat(fullPath);
43521
44250
  const dirEntry = {
43522
44251
  name: entry.name,
43523
44252
  path: relativePath,
@@ -43613,14 +44342,14 @@ EXAMPLES:
43613
44342
  };
43614
44343
  }
43615
44344
  const resolvedPath = validation.resolvedPath;
43616
- if (!fs18.existsSync(resolvedPath)) {
44345
+ if (!fs19.existsSync(resolvedPath)) {
43617
44346
  return {
43618
44347
  success: false,
43619
44348
  error: `Directory not found: ${path6}`
43620
44349
  };
43621
44350
  }
43622
44351
  try {
43623
- const stats = await fs17.stat(resolvedPath);
44352
+ const stats = await fs18.stat(resolvedPath);
43624
44353
  if (!stats.isDirectory()) {
43625
44354
  return {
43626
44355
  success: false,
@@ -46574,6 +47303,840 @@ function registerGitHubTools() {
46574
47303
  // src/tools/github/index.ts
46575
47304
  registerGitHubTools();
46576
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
+
46577
48140
  // src/tools/desktop/types.ts
46578
48141
  var DEFAULT_DESKTOP_CONFIG = {
46579
48142
  driver: null,
@@ -48605,6 +50168,7 @@ exports.FileCustomToolStorage = FileCustomToolStorage;
48605
50168
  exports.FileMediaOutputHandler = FileMediaStorage;
48606
50169
  exports.FileMediaStorage = FileMediaStorage;
48607
50170
  exports.FilePersistentInstructionsStorage = FilePersistentInstructionsStorage;
50171
+ exports.FileRoutineDefinitionStorage = FileRoutineDefinitionStorage;
48608
50172
  exports.FileStorage = FileStorage;
48609
50173
  exports.FileUserInfoStorage = FileUserInfoStorage;
48610
50174
  exports.FormatDetector = FormatDetector;
@@ -48732,13 +50296,18 @@ exports.createDesktopMouseScrollTool = createDesktopMouseScrollTool;
48732
50296
  exports.createDesktopScreenshotTool = createDesktopScreenshotTool;
48733
50297
  exports.createDesktopWindowFocusTool = createDesktopWindowFocusTool;
48734
50298
  exports.createDesktopWindowListTool = createDesktopWindowListTool;
50299
+ exports.createDraftEmailTool = createDraftEmailTool;
48735
50300
  exports.createEditFileTool = createEditFileTool;
50301
+ exports.createEditMeetingTool = createEditMeetingTool;
48736
50302
  exports.createEstimator = createEstimator;
48737
50303
  exports.createExecuteJavaScriptTool = createExecuteJavaScriptTool;
48738
50304
  exports.createFileAgentDefinitionStorage = createFileAgentDefinitionStorage;
48739
50305
  exports.createFileContextStorage = createFileContextStorage;
48740
50306
  exports.createFileCustomToolStorage = createFileCustomToolStorage;
48741
50307
  exports.createFileMediaStorage = createFileMediaStorage;
50308
+ exports.createFileRoutineDefinitionStorage = createFileRoutineDefinitionStorage;
50309
+ exports.createFindMeetingSlotsTool = createFindMeetingSlotsTool;
50310
+ exports.createGetMeetingTranscriptTool = createGetMeetingTranscriptTool;
48742
50311
  exports.createGetPRTool = createGetPRTool;
48743
50312
  exports.createGitHubReadFileTool = createGitHubReadFileTool;
48744
50313
  exports.createGlobTool = createGlobTool;
@@ -48746,6 +50315,7 @@ exports.createGrepTool = createGrepTool;
48746
50315
  exports.createImageGenerationTool = createImageGenerationTool;
48747
50316
  exports.createImageProvider = createImageProvider;
48748
50317
  exports.createListDirectoryTool = createListDirectoryTool;
50318
+ exports.createMeetingTool = createMeetingTool;
48749
50319
  exports.createMessageWithImages = createMessageWithImages;
48750
50320
  exports.createMetricsCollector = createMetricsCollector;
48751
50321
  exports.createPRCommentsTool = createPRCommentsTool;
@@ -48753,8 +50323,11 @@ exports.createPRFilesTool = createPRFilesTool;
48753
50323
  exports.createPlan = createPlan;
48754
50324
  exports.createProvider = createProvider;
48755
50325
  exports.createReadFileTool = createReadFileTool;
50326
+ exports.createRoutineDefinition = createRoutineDefinition;
50327
+ exports.createRoutineExecution = createRoutineExecution;
48756
50328
  exports.createSearchCodeTool = createSearchCodeTool;
48757
50329
  exports.createSearchFilesTool = createSearchFilesTool;
50330
+ exports.createSendEmailTool = createSendEmailTool;
48758
50331
  exports.createSpeechToTextTool = createSpeechToTextTool;
48759
50332
  exports.createTask = createTask;
48760
50333
  exports.createTextMessage = createTextMessage;
@@ -48787,12 +50360,15 @@ exports.developerTools = developerTools;
48787
50360
  exports.documentToContent = documentToContent;
48788
50361
  exports.editFile = editFile;
48789
50362
  exports.evaluateCondition = evaluateCondition;
50363
+ exports.executeRoutine = executeRoutine;
48790
50364
  exports.extractJSON = extractJSON;
48791
50365
  exports.extractJSONField = extractJSONField;
48792
50366
  exports.extractNumber = extractNumber;
48793
50367
  exports.findConnectorByServiceTypes = findConnectorByServiceTypes;
48794
50368
  exports.forPlan = forPlan;
48795
50369
  exports.forTasks = forTasks;
50370
+ exports.formatAttendees = formatAttendees;
50371
+ exports.formatRecipients = formatRecipients;
48796
50372
  exports.generateEncryptionKey = generateEncryptionKey;
48797
50373
  exports.generateSimplePlan = generateSimplePlan;
48798
50374
  exports.generateWebAPITool = generateWebAPITool;
@@ -48819,6 +50395,7 @@ exports.getModelInfo = getModelInfo;
48819
50395
  exports.getModelsByVendor = getModelsByVendor;
48820
50396
  exports.getNextExecutableTasks = getNextExecutableTasks;
48821
50397
  exports.getRegisteredScrapeProviders = getRegisteredScrapeProviders;
50398
+ exports.getRoutineProgress = getRoutineProgress;
48822
50399
  exports.getSTTModelInfo = getSTTModelInfo;
48823
50400
  exports.getSTTModelsByVendor = getSTTModelsByVendor;
48824
50401
  exports.getSTTModelsWithFeature = getSTTModelsWithFeature;
@@ -48835,6 +50412,7 @@ exports.getToolCategories = getToolCategories;
48835
50412
  exports.getToolRegistry = getToolRegistry;
48836
50413
  exports.getToolsByCategory = getToolsByCategory;
48837
50414
  exports.getToolsRequiringConnector = getToolsRequiringConnector;
50415
+ exports.getUserPathPrefix = getUserPathPrefix;
48838
50416
  exports.getVendorAuthTemplate = getVendorAuthTemplate;
48839
50417
  exports.getVendorColor = getVendorColor;
48840
50418
  exports.getVendorDefaultBaseURL = getVendorDefaultBaseURL;
@@ -48863,6 +50441,7 @@ exports.isSimpleScope = isSimpleScope;
48863
50441
  exports.isStreamEvent = isStreamEvent;
48864
50442
  exports.isTaskAwareScope = isTaskAwareScope;
48865
50443
  exports.isTaskBlocked = isTaskBlocked;
50444
+ exports.isTeamsMeetingUrl = isTeamsMeetingUrl;
48866
50445
  exports.isTerminalMemoryStatus = isTerminalMemoryStatus;
48867
50446
  exports.isTerminalStatus = isTerminalStatus;
48868
50447
  exports.isToolCallArgumentsDelta = isToolCallArgumentsDelta;
@@ -48878,6 +50457,8 @@ exports.listVendorsByAuthType = listVendorsByAuthType;
48878
50457
  exports.listVendorsByCategory = listVendorsByCategory;
48879
50458
  exports.listVendorsWithLogos = listVendorsWithLogos;
48880
50459
  exports.mergeTextPieces = mergeTextPieces;
50460
+ exports.microsoftFetch = microsoftFetch;
50461
+ exports.normalizeEmails = normalizeEmails;
48881
50462
  exports.parseKeyCombo = parseKeyCombo;
48882
50463
  exports.parseRepository = parseRepository;
48883
50464
  exports.readClipboardImage = readClipboardImage;
@@ -48888,6 +50469,7 @@ exports.resetDefaultDriver = resetDefaultDriver;
48888
50469
  exports.resolveConnector = resolveConnector;
48889
50470
  exports.resolveDependencies = resolveDependencies;
48890
50471
  exports.resolveMaxContextTokens = resolveMaxContextTokens;
50472
+ exports.resolveMeetingId = resolveMeetingId;
48891
50473
  exports.resolveModelCapabilities = resolveModelCapabilities;
48892
50474
  exports.resolveRepository = resolveRepository;
48893
50475
  exports.retryWithBackoff = retryWithBackoff;