@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/README.md +115 -3
- package/dist/capabilities/agents/index.cjs +14 -2
- package/dist/capabilities/agents/index.cjs.map +1 -1
- package/dist/capabilities/agents/index.d.cts +1 -1
- package/dist/capabilities/agents/index.d.ts +1 -1
- package/dist/capabilities/agents/index.js +14 -2
- package/dist/capabilities/agents/index.js.map +1 -1
- package/dist/capabilities/images/index.cjs.map +1 -1
- package/dist/capabilities/images/index.js.map +1 -1
- package/dist/{index-WlQwiNF8.d.cts → index-CR5PHkck.d.cts} +6 -1
- package/dist/{index-BeZcWDiq.d.ts → index-Cb7N9QIj.d.ts} +6 -1
- package/dist/index.cjs +2148 -566
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +919 -340
- package/dist/index.d.ts +919 -340
- package/dist/index.js +2054 -491
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var crypto2 = require('crypto');
|
|
4
4
|
var jose = require('jose');
|
|
5
|
-
var
|
|
5
|
+
var fs19 = require('fs');
|
|
6
6
|
var eventemitter3 = require('eventemitter3');
|
|
7
7
|
var path2 = require('path');
|
|
8
8
|
var TurndownService = require('turndown');
|
|
@@ -17,7 +17,7 @@ var z = require('zod/v4');
|
|
|
17
17
|
var spawn = require('cross-spawn');
|
|
18
18
|
var process2 = require('process');
|
|
19
19
|
var stream = require('stream');
|
|
20
|
-
var
|
|
20
|
+
var fs18 = require('fs/promises');
|
|
21
21
|
var simpleIcons = require('simple-icons');
|
|
22
22
|
var child_process = require('child_process');
|
|
23
23
|
var util = require('util');
|
|
@@ -45,7 +45,7 @@ function _interopNamespace(e) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
var crypto2__namespace = /*#__PURE__*/_interopNamespace(crypto2);
|
|
48
|
-
var
|
|
48
|
+
var fs19__namespace = /*#__PURE__*/_interopNamespace(fs19);
|
|
49
49
|
var path2__namespace = /*#__PURE__*/_interopNamespace(path2);
|
|
50
50
|
var TurndownService__default = /*#__PURE__*/_interopDefault(TurndownService);
|
|
51
51
|
var os2__namespace = /*#__PURE__*/_interopNamespace(os2);
|
|
@@ -55,7 +55,7 @@ var z4mini__namespace = /*#__PURE__*/_interopNamespace(z4mini);
|
|
|
55
55
|
var z__namespace = /*#__PURE__*/_interopNamespace(z);
|
|
56
56
|
var spawn__default = /*#__PURE__*/_interopDefault(spawn);
|
|
57
57
|
var process2__default = /*#__PURE__*/_interopDefault(process2);
|
|
58
|
-
var
|
|
58
|
+
var fs18__namespace = /*#__PURE__*/_interopNamespace(fs18);
|
|
59
59
|
var simpleIcons__namespace = /*#__PURE__*/_interopNamespace(simpleIcons);
|
|
60
60
|
var vm__namespace = /*#__PURE__*/_interopNamespace(vm);
|
|
61
61
|
|
|
@@ -673,7 +673,7 @@ var init_JWTBearer = __esm({
|
|
|
673
673
|
this.privateKey = config.privateKey;
|
|
674
674
|
} else if (config.privateKeyPath) {
|
|
675
675
|
try {
|
|
676
|
-
this.privateKey =
|
|
676
|
+
this.privateKey = fs19__namespace.readFileSync(config.privateKeyPath, "utf8");
|
|
677
677
|
} catch (error) {
|
|
678
678
|
throw new Error(`Failed to read private key from ${config.privateKeyPath}: ${error.message}`);
|
|
679
679
|
}
|
|
@@ -1432,10 +1432,10 @@ var init_Logger = __esm({
|
|
|
1432
1432
|
initFileStream(filePath) {
|
|
1433
1433
|
try {
|
|
1434
1434
|
const dir = path2__namespace.dirname(filePath);
|
|
1435
|
-
if (!
|
|
1436
|
-
|
|
1435
|
+
if (!fs19__namespace.existsSync(dir)) {
|
|
1436
|
+
fs19__namespace.mkdirSync(dir, { recursive: true });
|
|
1437
1437
|
}
|
|
1438
|
-
this.fileStream =
|
|
1438
|
+
this.fileStream = fs19__namespace.createWriteStream(filePath, {
|
|
1439
1439
|
flags: "a",
|
|
1440
1440
|
// append mode
|
|
1441
1441
|
encoding: "utf8"
|
|
@@ -9165,12 +9165,12 @@ var require_dist = __commonJS({
|
|
|
9165
9165
|
throw new Error(`Unknown format "${name}"`);
|
|
9166
9166
|
return f;
|
|
9167
9167
|
};
|
|
9168
|
-
function addFormats(ajv, list,
|
|
9168
|
+
function addFormats(ajv, list, fs20, exportName) {
|
|
9169
9169
|
var _a;
|
|
9170
9170
|
var _b;
|
|
9171
9171
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
9172
9172
|
for (const f of list)
|
|
9173
|
-
ajv.addFormat(f,
|
|
9173
|
+
ajv.addFormat(f, fs20[f]);
|
|
9174
9174
|
}
|
|
9175
9175
|
module.exports = exports$1 = formatsPlugin;
|
|
9176
9176
|
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
@@ -14902,7 +14902,7 @@ var FilePersistentInstructionsStorage = class {
|
|
|
14902
14902
|
*/
|
|
14903
14903
|
async load() {
|
|
14904
14904
|
try {
|
|
14905
|
-
const raw = await
|
|
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
|
|
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
|
|
14948
|
-
await
|
|
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
|
|
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
|
|
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
|
|
14976
|
+
await fs19.promises.access(this.filePath);
|
|
14977
14977
|
return true;
|
|
14978
14978
|
} catch {
|
|
14979
14979
|
try {
|
|
14980
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
15550
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
23407
|
-
const content =
|
|
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
|
|
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
|
|
24343
|
+
const fs20 = __require("fs");
|
|
23437
24344
|
for (const path6 of this.DEFAULT_PATHS) {
|
|
23438
24345
|
try {
|
|
23439
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
30993
|
-
const buffer =
|
|
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
|
|
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
|
|
32605
|
-
const data =
|
|
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
|
|
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
|
|
32795
|
-
await
|
|
33701
|
+
buffer = await fs20.readFile(tempFile);
|
|
33702
|
+
await fs20.unlink(tempFile).catch(() => {
|
|
32796
33703
|
});
|
|
32797
33704
|
} catch (downloadError) {
|
|
32798
|
-
await
|
|
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
|
|
32921
|
-
const data = await
|
|
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
|
|
33229
|
-
const data =
|
|
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
|
|
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
|
|
36985
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
37269
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
37654
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
38939
|
-
await
|
|
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
|
|
38956
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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) =>
|
|
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
|
|
39463
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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) =>
|
|
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
|
|
39556
|
-
await
|
|
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
|
|
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
|
|
39586
|
-
await
|
|
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 (
|
|
42042
|
-
|
|
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") ||
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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 (!
|
|
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
|
|
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
|
|
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 =
|
|
43590
|
+
const fileExists = fs19.existsSync(resolvedPath);
|
|
42862
43591
|
try {
|
|
42863
43592
|
const parentDir = path2.dirname(resolvedPath);
|
|
42864
|
-
if (!
|
|
42865
|
-
await
|
|
43593
|
+
if (!fs19.existsSync(parentDir)) {
|
|
43594
|
+
await fs18.mkdir(parentDir, { recursive: true });
|
|
42866
43595
|
}
|
|
42867
|
-
await
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
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
|
|
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;
|