@everworker/oneringai 0.1.4 → 0.2.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 +16 -4
- package/dist/{ImageModel-qNJHPh4q.d.ts → IProvider-DcYJ3YE-.d.ts} +15 -328
- package/dist/{ImageModel-DSY7SNsq.d.cts → IProvider-c4QCbPjn.d.cts} +15 -328
- package/dist/ImageModel-BJ2mVPGV.d.ts +337 -0
- package/dist/ImageModel-BWN6VVS6.d.cts +337 -0
- package/dist/capabilities/agents/index.d.cts +3 -2
- package/dist/capabilities/agents/index.d.ts +3 -2
- package/dist/capabilities/images/index.cjs +8 -1
- package/dist/capabilities/images/index.cjs.map +1 -1
- package/dist/capabilities/images/index.d.cts +2 -2
- package/dist/capabilities/images/index.d.ts +2 -2
- package/dist/capabilities/images/index.js +8 -1
- package/dist/capabilities/images/index.js.map +1 -1
- package/dist/{index-CEp1H4fV.d.ts → index-B5UaeEvK.d.ts} +9 -6
- package/dist/{index-NOV01LWF.d.cts → index-MJ14lkui.d.cts} +9 -6
- package/dist/index.cjs +610 -768
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +156 -85
- package/dist/index.d.ts +156 -85
- package/dist/index.js +610 -768
- package/dist/index.js.map +1 -1
- package/package.json +1 -4
- package/dist/IProvider-BP49c93d.d.cts +0 -22
- package/dist/IProvider-BP49c93d.d.ts +0 -22
package/dist/index.js
CHANGED
|
@@ -2003,7 +2003,14 @@ var init_Connector = __esm({
|
|
|
2003
2003
|
}
|
|
2004
2004
|
const startTime = Date.now();
|
|
2005
2005
|
this.requestCount++;
|
|
2006
|
-
|
|
2006
|
+
let url2;
|
|
2007
|
+
if (endpoint.startsWith("http")) {
|
|
2008
|
+
url2 = endpoint;
|
|
2009
|
+
} else {
|
|
2010
|
+
const base = (this.baseURL ?? "").replace(/\/+$/, "");
|
|
2011
|
+
const path6 = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
2012
|
+
url2 = `${base}${path6}`;
|
|
2013
|
+
}
|
|
2007
2014
|
const timeout = options?.timeout ?? this.config.timeout ?? DEFAULT_CONNECTOR_TIMEOUT;
|
|
2008
2015
|
if (this.config.logging?.enabled) {
|
|
2009
2016
|
this.logRequest(url2, options);
|
|
@@ -18406,6 +18413,9 @@ var BasePluginNextGen = class {
|
|
|
18406
18413
|
}
|
|
18407
18414
|
};
|
|
18408
18415
|
|
|
18416
|
+
// src/core/context-nextgen/AgentContextNextGen.ts
|
|
18417
|
+
init_Connector();
|
|
18418
|
+
|
|
18409
18419
|
// src/domain/entities/Memory.ts
|
|
18410
18420
|
function isTaskAwareScope(scope) {
|
|
18411
18421
|
return typeof scope === "object" && scope !== null && "type" in scope;
|
|
@@ -19344,13 +19354,18 @@ Values are immediately visible - no retrieval needed.
|
|
|
19344
19354
|
- \`high\`: Keep longer. Important state.
|
|
19345
19355
|
- \`critical\`: Never auto-evicted.
|
|
19346
19356
|
|
|
19357
|
+
**UI Display:** Set \`showInUI: true\` in context_set to display the entry in the user's side panel.
|
|
19358
|
+
Values shown in the UI support the same rich markdown formatting as the chat window
|
|
19359
|
+
(see formatting instructions above). Use this for dashboards, progress displays, and results the user should see.
|
|
19360
|
+
|
|
19347
19361
|
**Tools:** context_set, context_delete, context_list`;
|
|
19348
19362
|
var contextSetDefinition = {
|
|
19349
19363
|
type: "function",
|
|
19350
19364
|
function: {
|
|
19351
19365
|
name: "context_set",
|
|
19352
19366
|
description: `Store or update a key-value pair in live context.
|
|
19353
|
-
Value appears directly in context - no retrieval needed
|
|
19367
|
+
Value appears directly in context - no retrieval needed.
|
|
19368
|
+
Set showInUI to true to also display the entry in the user's side panel.`,
|
|
19354
19369
|
parameters: {
|
|
19355
19370
|
type: "object",
|
|
19356
19371
|
properties: {
|
|
@@ -19361,6 +19376,10 @@ Value appears directly in context - no retrieval needed.`,
|
|
|
19361
19376
|
type: "string",
|
|
19362
19377
|
enum: ["low", "normal", "high", "critical"],
|
|
19363
19378
|
description: 'Eviction priority. Default: "normal"'
|
|
19379
|
+
},
|
|
19380
|
+
showInUI: {
|
|
19381
|
+
type: "boolean",
|
|
19382
|
+
description: "If true, display this entry in the user's side panel with full rich markdown rendering \u2014 same capabilities as the chat window (code blocks, tables, LaTeX, Mermaid diagrams, Vega-Lite charts, mindmaps, etc. \u2014 see formatting instructions in system prompt). Use this for dashboards, status displays, and structured results the user should see. Default: false"
|
|
19364
19383
|
}
|
|
19365
19384
|
},
|
|
19366
19385
|
required: ["key", "description", "value"]
|
|
@@ -19401,6 +19420,7 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19401
19420
|
_destroyed = false;
|
|
19402
19421
|
_tokenCache = null;
|
|
19403
19422
|
_instructionsTokenCache = null;
|
|
19423
|
+
_notifyTimer = null;
|
|
19404
19424
|
constructor(config = {}) {
|
|
19405
19425
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
19406
19426
|
}
|
|
@@ -19441,15 +19461,18 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19441
19461
|
return a.updatedAt - b.updatedAt;
|
|
19442
19462
|
});
|
|
19443
19463
|
let freed = 0;
|
|
19464
|
+
let evicted = false;
|
|
19444
19465
|
for (const entry of evictable) {
|
|
19445
19466
|
if (freed >= targetTokensToFree) break;
|
|
19446
19467
|
const entryTokens = this.estimator.estimateTokens(this.formatEntry(entry));
|
|
19447
19468
|
this.entries.delete(entry.key);
|
|
19448
19469
|
freed += entryTokens;
|
|
19470
|
+
evicted = true;
|
|
19449
19471
|
}
|
|
19450
19472
|
this._tokenCache = null;
|
|
19451
19473
|
const content = await this.getContent();
|
|
19452
19474
|
const after = content ? this.estimator.estimateTokens(content) : 0;
|
|
19475
|
+
if (evicted) this.notifyEntriesChanged();
|
|
19453
19476
|
return Math.max(0, before - after);
|
|
19454
19477
|
}
|
|
19455
19478
|
getTools() {
|
|
@@ -19461,6 +19484,7 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19461
19484
|
}
|
|
19462
19485
|
destroy() {
|
|
19463
19486
|
if (this._destroyed) return;
|
|
19487
|
+
if (this._notifyTimer) clearTimeout(this._notifyTimer);
|
|
19464
19488
|
this.entries.clear();
|
|
19465
19489
|
this._destroyed = true;
|
|
19466
19490
|
this._tokenCache = null;
|
|
@@ -19478,6 +19502,7 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19478
19502
|
this.entries.set(entry.key, entry);
|
|
19479
19503
|
}
|
|
19480
19504
|
this._tokenCache = null;
|
|
19505
|
+
this.notifyEntriesChanged();
|
|
19481
19506
|
}
|
|
19482
19507
|
// ============================================================================
|
|
19483
19508
|
// Entry Management
|
|
@@ -19485,19 +19510,21 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19485
19510
|
/**
|
|
19486
19511
|
* Store or update a key-value pair
|
|
19487
19512
|
*/
|
|
19488
|
-
set(key, description, value, priority) {
|
|
19513
|
+
set(key, description, value, priority, showInUI) {
|
|
19489
19514
|
this.assertNotDestroyed();
|
|
19490
19515
|
const entry = {
|
|
19491
19516
|
key,
|
|
19492
19517
|
description,
|
|
19493
19518
|
value,
|
|
19494
19519
|
updatedAt: Date.now(),
|
|
19495
|
-
priority: priority ?? this.config.defaultPriority
|
|
19520
|
+
priority: priority ?? this.config.defaultPriority,
|
|
19521
|
+
showInUI: showInUI ?? false
|
|
19496
19522
|
};
|
|
19497
19523
|
this.entries.set(key, entry);
|
|
19498
19524
|
this.enforceMaxEntries();
|
|
19499
19525
|
this.enforceTokenLimit();
|
|
19500
19526
|
this._tokenCache = null;
|
|
19527
|
+
this.notifyEntriesChanged();
|
|
19501
19528
|
}
|
|
19502
19529
|
/**
|
|
19503
19530
|
* Get a value by key
|
|
@@ -19519,7 +19546,10 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19519
19546
|
delete(key) {
|
|
19520
19547
|
this.assertNotDestroyed();
|
|
19521
19548
|
const deleted = this.entries.delete(key);
|
|
19522
|
-
if (deleted)
|
|
19549
|
+
if (deleted) {
|
|
19550
|
+
this._tokenCache = null;
|
|
19551
|
+
this.notifyEntriesChanged();
|
|
19552
|
+
}
|
|
19523
19553
|
return deleted;
|
|
19524
19554
|
}
|
|
19525
19555
|
/**
|
|
@@ -19531,7 +19561,8 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19531
19561
|
key: e.key,
|
|
19532
19562
|
description: e.description,
|
|
19533
19563
|
priority: e.priority,
|
|
19534
|
-
updatedAt: e.updatedAt
|
|
19564
|
+
updatedAt: e.updatedAt,
|
|
19565
|
+
showInUI: e.showInUI ?? false
|
|
19535
19566
|
}));
|
|
19536
19567
|
}
|
|
19537
19568
|
/**
|
|
@@ -19541,6 +19572,7 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19541
19572
|
this.assertNotDestroyed();
|
|
19542
19573
|
this.entries.clear();
|
|
19543
19574
|
this._tokenCache = null;
|
|
19575
|
+
this.notifyEntriesChanged();
|
|
19544
19576
|
}
|
|
19545
19577
|
// ============================================================================
|
|
19546
19578
|
// Private Helpers
|
|
@@ -19604,6 +19636,20 @@ ${valueStr}
|
|
|
19604
19636
|
return a.updatedAt - b.updatedAt;
|
|
19605
19637
|
});
|
|
19606
19638
|
}
|
|
19639
|
+
/**
|
|
19640
|
+
* Debounced notification when entries change.
|
|
19641
|
+
* Calls config.onEntriesChanged with all current entries.
|
|
19642
|
+
*/
|
|
19643
|
+
notifyEntriesChanged() {
|
|
19644
|
+
if (!this.config.onEntriesChanged) return;
|
|
19645
|
+
if (this._notifyTimer) clearTimeout(this._notifyTimer);
|
|
19646
|
+
this._notifyTimer = setTimeout(() => {
|
|
19647
|
+
this._notifyTimer = null;
|
|
19648
|
+
if (!this._destroyed && this.config.onEntriesChanged) {
|
|
19649
|
+
this.config.onEntriesChanged(Array.from(this.entries.values()));
|
|
19650
|
+
}
|
|
19651
|
+
}, 100);
|
|
19652
|
+
}
|
|
19607
19653
|
assertNotDestroyed() {
|
|
19608
19654
|
if (this._destroyed) {
|
|
19609
19655
|
throw new Error("InContextMemoryPluginNextGen is destroyed");
|
|
@@ -19620,16 +19666,18 @@ ${valueStr}
|
|
|
19620
19666
|
args.key,
|
|
19621
19667
|
args.description,
|
|
19622
19668
|
args.value,
|
|
19623
|
-
args.priority
|
|
19669
|
+
args.priority,
|
|
19670
|
+
args.showInUI
|
|
19624
19671
|
);
|
|
19625
19672
|
return {
|
|
19626
19673
|
success: true,
|
|
19627
19674
|
key: args.key,
|
|
19628
|
-
|
|
19675
|
+
showInUI: args.showInUI ?? false,
|
|
19676
|
+
message: `Stored "${args.key}" in live context${args.showInUI ? " (visible in UI)" : ""}`
|
|
19629
19677
|
};
|
|
19630
19678
|
},
|
|
19631
19679
|
permission: { scope: "always", riskLevel: "low" },
|
|
19632
|
-
describeCall: (args) => `set ${args.key}`
|
|
19680
|
+
describeCall: (args) => `set ${args.key}${args.showInUI ? " [UI]" : ""}`
|
|
19633
19681
|
};
|
|
19634
19682
|
}
|
|
19635
19683
|
createContextDeleteTool() {
|
|
@@ -20941,6 +20989,10 @@ var AgentContextNextGen = class _AgentContextNextGen extends EventEmitter {
|
|
|
20941
20989
|
_sessionId = null;
|
|
20942
20990
|
/** Agent ID */
|
|
20943
20991
|
_agentId;
|
|
20992
|
+
/** User ID for multi-user scenarios */
|
|
20993
|
+
_userId;
|
|
20994
|
+
/** Allowed connector names (when agent is restricted to a subset) */
|
|
20995
|
+
_allowedConnectors;
|
|
20944
20996
|
/** Storage backend */
|
|
20945
20997
|
_storage;
|
|
20946
20998
|
/** Destroyed flag */
|
|
@@ -20977,6 +21029,8 @@ var AgentContextNextGen = class _AgentContextNextGen extends EventEmitter {
|
|
|
20977
21029
|
};
|
|
20978
21030
|
this._systemPrompt = config.systemPrompt;
|
|
20979
21031
|
this._agentId = this._config.agentId;
|
|
21032
|
+
this._userId = config.userId;
|
|
21033
|
+
this._allowedConnectors = config.connectors;
|
|
20980
21034
|
this._storage = config.storage;
|
|
20981
21035
|
this._compactionStrategy = config.compactionStrategy ?? StrategyRegistry.create(this._config.strategy);
|
|
20982
21036
|
this._tools = new ToolManager(
|
|
@@ -20988,6 +21042,7 @@ var AgentContextNextGen = class _AgentContextNextGen extends EventEmitter {
|
|
|
20988
21042
|
}
|
|
20989
21043
|
}
|
|
20990
21044
|
this.initializePlugins(config.plugins);
|
|
21045
|
+
this.syncToolContext();
|
|
20991
21046
|
}
|
|
20992
21047
|
/**
|
|
20993
21048
|
* Initialize plugins based on feature flags.
|
|
@@ -21032,6 +21087,62 @@ var AgentContextNextGen = class _AgentContextNextGen extends EventEmitter {
|
|
|
21032
21087
|
);
|
|
21033
21088
|
}
|
|
21034
21089
|
}
|
|
21090
|
+
/**
|
|
21091
|
+
* Sync identity fields and connector registry to ToolContext.
|
|
21092
|
+
* Merges with existing ToolContext to preserve other fields (memory, signal, taskId).
|
|
21093
|
+
*
|
|
21094
|
+
* Connector registry resolution order:
|
|
21095
|
+
* 1. If `connectors` (allowed names) is set → filtered view of global registry
|
|
21096
|
+
* 2. If access policy + userId → scoped view via Connector.scoped()
|
|
21097
|
+
* 3. Otherwise → full global registry
|
|
21098
|
+
*/
|
|
21099
|
+
syncToolContext() {
|
|
21100
|
+
const existing = this._tools.getToolContext();
|
|
21101
|
+
this._tools.setToolContext({
|
|
21102
|
+
...existing,
|
|
21103
|
+
agentId: this._agentId,
|
|
21104
|
+
userId: this._userId,
|
|
21105
|
+
connectorRegistry: this.buildConnectorRegistry()
|
|
21106
|
+
});
|
|
21107
|
+
}
|
|
21108
|
+
/**
|
|
21109
|
+
* Build the connector registry appropriate for this agent's config.
|
|
21110
|
+
*/
|
|
21111
|
+
buildConnectorRegistry() {
|
|
21112
|
+
if (this._allowedConnectors?.length) {
|
|
21113
|
+
const allowedSet = new Set(this._allowedConnectors);
|
|
21114
|
+
const base = this._userId && Connector.getAccessPolicy() ? Connector.scoped({ userId: this._userId }) : Connector.asRegistry();
|
|
21115
|
+
return {
|
|
21116
|
+
get: (name) => {
|
|
21117
|
+
if (!allowedSet.has(name)) {
|
|
21118
|
+
const available = this._allowedConnectors.filter((n) => base.has(n)).join(", ") || "none";
|
|
21119
|
+
throw new Error(`Connector '${name}' not found. Available: ${available}`);
|
|
21120
|
+
}
|
|
21121
|
+
return base.get(name);
|
|
21122
|
+
},
|
|
21123
|
+
has: (name) => allowedSet.has(name) && base.has(name),
|
|
21124
|
+
list: () => base.list().filter((n) => allowedSet.has(n)),
|
|
21125
|
+
listAll: () => base.listAll().filter((c) => allowedSet.has(c.name)),
|
|
21126
|
+
size: () => base.listAll().filter((c) => allowedSet.has(c.name)).length,
|
|
21127
|
+
getDescriptionsForTools: () => {
|
|
21128
|
+
const connectors = base.listAll().filter((c) => allowedSet.has(c.name));
|
|
21129
|
+
if (connectors.length === 0) return "No connectors registered yet.";
|
|
21130
|
+
return connectors.map((c) => ` - "${c.name}": ${c.displayName} - ${c.config.description || "No description"}`).join("\n");
|
|
21131
|
+
},
|
|
21132
|
+
getInfo: () => {
|
|
21133
|
+
const info = {};
|
|
21134
|
+
for (const c of base.listAll().filter((c2) => allowedSet.has(c2.name))) {
|
|
21135
|
+
info[c.name] = { displayName: c.displayName, description: c.config.description || "", baseURL: c.baseURL };
|
|
21136
|
+
}
|
|
21137
|
+
return info;
|
|
21138
|
+
}
|
|
21139
|
+
};
|
|
21140
|
+
}
|
|
21141
|
+
if (this._userId && Connector.getAccessPolicy()) {
|
|
21142
|
+
return Connector.scoped({ userId: this._userId });
|
|
21143
|
+
}
|
|
21144
|
+
return Connector.asRegistry();
|
|
21145
|
+
}
|
|
21035
21146
|
// ============================================================================
|
|
21036
21147
|
// Public Properties
|
|
21037
21148
|
// ============================================================================
|
|
@@ -21047,6 +21158,24 @@ var AgentContextNextGen = class _AgentContextNextGen extends EventEmitter {
|
|
|
21047
21158
|
get agentId() {
|
|
21048
21159
|
return this._agentId;
|
|
21049
21160
|
}
|
|
21161
|
+
/** Get the current user ID */
|
|
21162
|
+
get userId() {
|
|
21163
|
+
return this._userId;
|
|
21164
|
+
}
|
|
21165
|
+
/** Set user ID. Automatically updates ToolContext for all tool executions. */
|
|
21166
|
+
set userId(value) {
|
|
21167
|
+
this._userId = value;
|
|
21168
|
+
this.syncToolContext();
|
|
21169
|
+
}
|
|
21170
|
+
/** Get the allowed connector names (undefined = all visible connectors) */
|
|
21171
|
+
get connectors() {
|
|
21172
|
+
return this._allowedConnectors;
|
|
21173
|
+
}
|
|
21174
|
+
/** Set allowed connector names. Updates ToolContext.connectorRegistry. */
|
|
21175
|
+
set connectors(value) {
|
|
21176
|
+
this._allowedConnectors = value;
|
|
21177
|
+
this.syncToolContext();
|
|
21178
|
+
}
|
|
21050
21179
|
/** Get/set system prompt */
|
|
21051
21180
|
get systemPrompt() {
|
|
21052
21181
|
return this._systemPrompt;
|
|
@@ -21960,6 +22089,7 @@ ${content}`);
|
|
|
21960
22089
|
metadata: {
|
|
21961
22090
|
savedAt: Date.now(),
|
|
21962
22091
|
agentId: this._agentId,
|
|
22092
|
+
userId: this._userId,
|
|
21963
22093
|
model: this._config.model
|
|
21964
22094
|
}
|
|
21965
22095
|
};
|
|
@@ -24922,6 +25052,8 @@ var BaseAgent = class extends EventEmitter {
|
|
|
24922
25052
|
const contextConfig = {
|
|
24923
25053
|
model: config.model,
|
|
24924
25054
|
agentId: config.name,
|
|
25055
|
+
userId: config.userId,
|
|
25056
|
+
connectors: config.connectors,
|
|
24925
25057
|
// Include storage and sessionId if session config is provided
|
|
24926
25058
|
storage: config.session?.storage,
|
|
24927
25059
|
// Thread tool execution timeout to ToolManager
|
|
@@ -25078,6 +25210,30 @@ var BaseAgent = class extends EventEmitter {
|
|
|
25078
25210
|
get context() {
|
|
25079
25211
|
return this._agentContext;
|
|
25080
25212
|
}
|
|
25213
|
+
/**
|
|
25214
|
+
* Get the current user ID. Delegates to AgentContextNextGen.
|
|
25215
|
+
*/
|
|
25216
|
+
get userId() {
|
|
25217
|
+
return this._agentContext.userId;
|
|
25218
|
+
}
|
|
25219
|
+
/**
|
|
25220
|
+
* Set user ID at runtime. Automatically updates ToolContext for all tool executions.
|
|
25221
|
+
*/
|
|
25222
|
+
set userId(value) {
|
|
25223
|
+
this._agentContext.userId = value;
|
|
25224
|
+
}
|
|
25225
|
+
/**
|
|
25226
|
+
* Get the allowed connector names (undefined = all visible connectors).
|
|
25227
|
+
*/
|
|
25228
|
+
get connectors() {
|
|
25229
|
+
return this._agentContext.connectors;
|
|
25230
|
+
}
|
|
25231
|
+
/**
|
|
25232
|
+
* Restrict this agent to a subset of connectors. Updates ToolContext.connectorRegistry.
|
|
25233
|
+
*/
|
|
25234
|
+
set connectors(value) {
|
|
25235
|
+
this._agentContext.connectors = value;
|
|
25236
|
+
}
|
|
25081
25237
|
/**
|
|
25082
25238
|
* Permission management. Returns ToolPermissionManager for approval control.
|
|
25083
25239
|
*/
|
|
@@ -25129,9 +25285,10 @@ var BaseAgent = class extends EventEmitter {
|
|
|
25129
25285
|
* always sees up-to-date tool descriptions.
|
|
25130
25286
|
*/
|
|
25131
25287
|
getEnabledToolDefinitions() {
|
|
25288
|
+
const toolContext = this._agentContext.tools.getToolContext();
|
|
25132
25289
|
return this._agentContext.tools.getEnabled().map((tool) => {
|
|
25133
25290
|
if (tool.descriptionFactory) {
|
|
25134
|
-
const dynamicDescription = tool.descriptionFactory();
|
|
25291
|
+
const dynamicDescription = tool.descriptionFactory(toolContext);
|
|
25135
25292
|
return {
|
|
25136
25293
|
...tool.definition,
|
|
25137
25294
|
function: {
|
|
@@ -26177,6 +26334,7 @@ var Agent = class _Agent extends BaseAgent {
|
|
|
26177
26334
|
* const agent = Agent.create({
|
|
26178
26335
|
* connector: 'openai', // or Connector instance
|
|
26179
26336
|
* model: 'gpt-4',
|
|
26337
|
+
* userId: 'user-123', // flows to all tool executions automatically
|
|
26180
26338
|
* instructions: 'You are a helpful assistant',
|
|
26181
26339
|
* tools: [myTool]
|
|
26182
26340
|
* });
|
|
@@ -41754,6 +41912,32 @@ function filterProtectedHeaders(headers) {
|
|
|
41754
41912
|
}
|
|
41755
41913
|
return filtered;
|
|
41756
41914
|
}
|
|
41915
|
+
function normalizeBody(body) {
|
|
41916
|
+
if (typeof body === "string") {
|
|
41917
|
+
try {
|
|
41918
|
+
return JSON.parse(body);
|
|
41919
|
+
} catch {
|
|
41920
|
+
return body;
|
|
41921
|
+
}
|
|
41922
|
+
}
|
|
41923
|
+
return body;
|
|
41924
|
+
}
|
|
41925
|
+
function detectAPIError(data) {
|
|
41926
|
+
if (!data || typeof data !== "object") return null;
|
|
41927
|
+
const obj = data;
|
|
41928
|
+
if (obj.ok === false && typeof obj.error === "string") {
|
|
41929
|
+
return obj.error;
|
|
41930
|
+
}
|
|
41931
|
+
if (obj.success === false) {
|
|
41932
|
+
if (typeof obj.error === "string") return obj.error;
|
|
41933
|
+
if (typeof obj.message === "string") return obj.message;
|
|
41934
|
+
}
|
|
41935
|
+
if (obj.error && typeof obj.error === "object") {
|
|
41936
|
+
const err = obj.error;
|
|
41937
|
+
if (typeof err.message === "string") return err.message;
|
|
41938
|
+
}
|
|
41939
|
+
return null;
|
|
41940
|
+
}
|
|
41757
41941
|
var ConnectorTools = class {
|
|
41758
41942
|
/** Registry of service-specific tool factories */
|
|
41759
41943
|
static factories = /* @__PURE__ */ new Map();
|
|
@@ -42007,7 +42191,7 @@ var ConnectorTools = class {
|
|
|
42007
42191
|
},
|
|
42008
42192
|
body: {
|
|
42009
42193
|
type: "object",
|
|
42010
|
-
description: 'JSON request body for POST/PUT/PATCH requests.
|
|
42194
|
+
description: 'JSON request body for POST/PUT/PATCH requests. MUST be a JSON object (NOT a string). Example: {"channel": "C123", "text": "hello"}. Do NOT stringify this \u2014 pass it as a raw JSON object. Do NOT use query string parameters for POST data.'
|
|
42011
42195
|
},
|
|
42012
42196
|
queryParams: {
|
|
42013
42197
|
type: "object",
|
|
@@ -42022,7 +42206,8 @@ var ConnectorTools = class {
|
|
|
42022
42206
|
}
|
|
42023
42207
|
}
|
|
42024
42208
|
},
|
|
42025
|
-
execute: async (args) => {
|
|
42209
|
+
execute: async (args, context) => {
|
|
42210
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
42026
42211
|
let url2 = args.endpoint;
|
|
42027
42212
|
if (args.queryParams && Object.keys(args.queryParams).length > 0) {
|
|
42028
42213
|
const params = new URLSearchParams();
|
|
@@ -42035,7 +42220,8 @@ var ConnectorTools = class {
|
|
|
42035
42220
|
let bodyStr;
|
|
42036
42221
|
if (args.body) {
|
|
42037
42222
|
try {
|
|
42038
|
-
|
|
42223
|
+
const normalized = normalizeBody(args.body);
|
|
42224
|
+
bodyStr = safeStringify2(normalized);
|
|
42039
42225
|
} catch (e) {
|
|
42040
42226
|
return {
|
|
42041
42227
|
success: false,
|
|
@@ -42054,7 +42240,7 @@ var ConnectorTools = class {
|
|
|
42054
42240
|
},
|
|
42055
42241
|
body: bodyStr
|
|
42056
42242
|
},
|
|
42057
|
-
|
|
42243
|
+
effectiveUserId
|
|
42058
42244
|
);
|
|
42059
42245
|
const text = await response.text();
|
|
42060
42246
|
let data;
|
|
@@ -42063,11 +42249,12 @@ var ConnectorTools = class {
|
|
|
42063
42249
|
} catch {
|
|
42064
42250
|
data = text;
|
|
42065
42251
|
}
|
|
42252
|
+
const apiError = detectAPIError(data);
|
|
42066
42253
|
return {
|
|
42067
|
-
success: response.ok,
|
|
42254
|
+
success: response.ok && !apiError,
|
|
42068
42255
|
status: response.status,
|
|
42069
|
-
data: response.ok ? data : void 0,
|
|
42070
|
-
error: response.ok ? void 0 : typeof data === "string" ? data : safeStringify2(data)
|
|
42256
|
+
data: response.ok && !apiError ? data : void 0,
|
|
42257
|
+
error: apiError ? apiError : response.ok ? void 0 : typeof data === "string" ? data : safeStringify2(data)
|
|
42071
42258
|
};
|
|
42072
42259
|
} catch (error) {
|
|
42073
42260
|
return {
|
|
@@ -45290,6 +45477,8 @@ __export(tools_exports, {
|
|
|
45290
45477
|
createSpeechToTextTool: () => createSpeechToTextTool,
|
|
45291
45478
|
createTextToSpeechTool: () => createTextToSpeechTool,
|
|
45292
45479
|
createVideoTools: () => createVideoTools,
|
|
45480
|
+
createWebScrapeTool: () => createWebScrapeTool,
|
|
45481
|
+
createWebSearchTool: () => createWebSearchTool,
|
|
45293
45482
|
createWriteFileTool: () => createWriteFileTool,
|
|
45294
45483
|
developerTools: () => developerTools,
|
|
45295
45484
|
editFile: () => editFile,
|
|
@@ -45319,9 +45508,6 @@ __export(tools_exports, {
|
|
|
45319
45508
|
toolRegistry: () => toolRegistry,
|
|
45320
45509
|
validatePath: () => validatePath,
|
|
45321
45510
|
webFetch: () => webFetch,
|
|
45322
|
-
webFetchJS: () => webFetchJS,
|
|
45323
|
-
webScrape: () => webScrape,
|
|
45324
|
-
webSearch: () => webSearch,
|
|
45325
45511
|
writeFile: () => writeFile5
|
|
45326
45512
|
});
|
|
45327
45513
|
var DEFAULT_FILESYSTEM_CONFIG = {
|
|
@@ -47044,6 +47230,111 @@ The tool returns a result object with:
|
|
|
47044
47230
|
}
|
|
47045
47231
|
};
|
|
47046
47232
|
|
|
47233
|
+
// src/tools/web/createWebSearchTool.ts
|
|
47234
|
+
init_Logger();
|
|
47235
|
+
var searchLogger = logger.child({ component: "webSearch" });
|
|
47236
|
+
function createWebSearchTool(connector) {
|
|
47237
|
+
return {
|
|
47238
|
+
definition: {
|
|
47239
|
+
type: "function",
|
|
47240
|
+
function: {
|
|
47241
|
+
name: "web_search",
|
|
47242
|
+
description: `Search the web and get relevant results with snippets.
|
|
47243
|
+
|
|
47244
|
+
RETURNS:
|
|
47245
|
+
An array of search results, each containing:
|
|
47246
|
+
- title: Page title
|
|
47247
|
+
- url: Direct URL to the page
|
|
47248
|
+
- snippet: Short description/excerpt from the page
|
|
47249
|
+
- position: Search ranking position (1, 2, 3...)
|
|
47250
|
+
|
|
47251
|
+
USE CASES:
|
|
47252
|
+
- Find current information on any topic
|
|
47253
|
+
- Research multiple sources
|
|
47254
|
+
- Discover relevant websites
|
|
47255
|
+
- Find URLs to fetch with web_fetch tool
|
|
47256
|
+
|
|
47257
|
+
WORKFLOW PATTERN:
|
|
47258
|
+
1. Use web_search to find relevant URLs
|
|
47259
|
+
2. Use web_fetch to get full content from top results
|
|
47260
|
+
3. Process and summarize the information
|
|
47261
|
+
|
|
47262
|
+
EXAMPLE:
|
|
47263
|
+
{
|
|
47264
|
+
"query": "latest AI developments 2026",
|
|
47265
|
+
"numResults": 5
|
|
47266
|
+
}`,
|
|
47267
|
+
parameters: {
|
|
47268
|
+
type: "object",
|
|
47269
|
+
properties: {
|
|
47270
|
+
query: {
|
|
47271
|
+
type: "string",
|
|
47272
|
+
description: "The search query string. Be specific for better results."
|
|
47273
|
+
},
|
|
47274
|
+
numResults: {
|
|
47275
|
+
type: "number",
|
|
47276
|
+
description: "Number of results to return (default: 10, max: 100)."
|
|
47277
|
+
},
|
|
47278
|
+
country: {
|
|
47279
|
+
type: "string",
|
|
47280
|
+
description: 'Country/region code for localized results (e.g., "us", "gb", "de")'
|
|
47281
|
+
},
|
|
47282
|
+
language: {
|
|
47283
|
+
type: "string",
|
|
47284
|
+
description: 'Language code for results (e.g., "en", "fr", "de")'
|
|
47285
|
+
}
|
|
47286
|
+
},
|
|
47287
|
+
required: ["query"]
|
|
47288
|
+
}
|
|
47289
|
+
},
|
|
47290
|
+
blocking: true,
|
|
47291
|
+
timeout: 15e3
|
|
47292
|
+
},
|
|
47293
|
+
execute: async (args) => {
|
|
47294
|
+
const numResults = args.numResults || 10;
|
|
47295
|
+
searchLogger.debug({ connectorName: connector.name }, "Executing search with connector");
|
|
47296
|
+
try {
|
|
47297
|
+
const searchProvider = SearchProvider.create({ connector: connector.name });
|
|
47298
|
+
const response = await searchProvider.search(args.query, {
|
|
47299
|
+
numResults,
|
|
47300
|
+
country: args.country,
|
|
47301
|
+
language: args.language
|
|
47302
|
+
});
|
|
47303
|
+
if (response.success) {
|
|
47304
|
+
searchLogger.debug({
|
|
47305
|
+
provider: response.provider,
|
|
47306
|
+
count: response.count
|
|
47307
|
+
}, "Search completed successfully");
|
|
47308
|
+
} else {
|
|
47309
|
+
searchLogger.warn({
|
|
47310
|
+
provider: response.provider,
|
|
47311
|
+
error: response.error
|
|
47312
|
+
}, "Search failed");
|
|
47313
|
+
}
|
|
47314
|
+
return {
|
|
47315
|
+
success: response.success,
|
|
47316
|
+
query: response.query,
|
|
47317
|
+
provider: response.provider,
|
|
47318
|
+
results: response.results,
|
|
47319
|
+
count: response.count,
|
|
47320
|
+
error: response.error
|
|
47321
|
+
};
|
|
47322
|
+
} catch (error) {
|
|
47323
|
+
searchLogger.error({ error: error.message, connectorName: connector.name }, "Search threw exception");
|
|
47324
|
+
return {
|
|
47325
|
+
success: false,
|
|
47326
|
+
query: args.query,
|
|
47327
|
+
provider: connector.name,
|
|
47328
|
+
results: [],
|
|
47329
|
+
count: 0,
|
|
47330
|
+
error: error.message || "Unknown error"
|
|
47331
|
+
};
|
|
47332
|
+
}
|
|
47333
|
+
},
|
|
47334
|
+
describeCall: (args) => `"${args.query}"${args.numResults ? ` (${args.numResults} results)` : ""}`
|
|
47335
|
+
};
|
|
47336
|
+
}
|
|
47337
|
+
|
|
47047
47338
|
// src/tools/web/contentDetector.ts
|
|
47048
47339
|
function detectContentQuality(html, text, $) {
|
|
47049
47340
|
const issues = [];
|
|
@@ -47118,7 +47409,7 @@ function detectContentQuality(html, text, $) {
|
|
|
47118
47409
|
}
|
|
47119
47410
|
let suggestion;
|
|
47120
47411
|
if (requiresJS && score < 50) {
|
|
47121
|
-
suggestion = "Content quality is low. This appears to be a JavaScript-rendered site. Use
|
|
47412
|
+
suggestion = "Content quality is low. This appears to be a JavaScript-rendered site. Use a scraping service connector for better results.";
|
|
47122
47413
|
} else if (score < 30) {
|
|
47123
47414
|
suggestion = "Content extraction failed or page has errors. Check the URL and try again.";
|
|
47124
47415
|
}
|
|
@@ -47227,7 +47518,7 @@ The tool analyzes the fetched content and returns a quality score (0-100):
|
|
|
47227
47518
|
- 50-79: Moderate quality, some content extracted
|
|
47228
47519
|
- 0-49: Low quality, likely needs JavaScript or has errors
|
|
47229
47520
|
|
|
47230
|
-
If the quality score is low or requiresJS is true,
|
|
47521
|
+
If the quality score is low or requiresJS is true, consider using a scraping service connector for better results.
|
|
47231
47522
|
|
|
47232
47523
|
RETURNS:
|
|
47233
47524
|
{
|
|
@@ -47391,460 +47682,109 @@ With custom user agent:
|
|
|
47391
47682
|
}
|
|
47392
47683
|
};
|
|
47393
47684
|
|
|
47394
|
-
// src/tools/web/
|
|
47395
|
-
|
|
47396
|
-
var
|
|
47397
|
-
|
|
47398
|
-
|
|
47399
|
-
|
|
47400
|
-
|
|
47401
|
-
|
|
47402
|
-
|
|
47403
|
-
|
|
47404
|
-
}
|
|
47405
|
-
return puppeteerModule;
|
|
47406
|
-
}
|
|
47407
|
-
async function getBrowser() {
|
|
47408
|
-
if (!browserInstance) {
|
|
47409
|
-
const puppeteer = await loadPuppeteer();
|
|
47410
|
-
browserInstance = await puppeteer.launch({
|
|
47411
|
-
headless: true,
|
|
47412
|
-
args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"]
|
|
47413
|
-
});
|
|
47414
|
-
process.on("exit", async () => {
|
|
47415
|
-
if (browserInstance) {
|
|
47416
|
-
await browserInstance.close();
|
|
47417
|
-
}
|
|
47418
|
-
});
|
|
47419
|
-
}
|
|
47420
|
-
return browserInstance;
|
|
47421
|
-
}
|
|
47422
|
-
var webFetchJS = {
|
|
47423
|
-
definition: {
|
|
47424
|
-
type: "function",
|
|
47425
|
-
function: {
|
|
47426
|
-
name: "web_fetch_js",
|
|
47427
|
-
description: `Fetch and extract content from JavaScript-rendered websites using a headless browser (Puppeteer).
|
|
47428
|
-
|
|
47429
|
-
USE THIS TOOL WHEN:
|
|
47430
|
-
- The web_fetch tool returned a low quality score (<50)
|
|
47431
|
-
- The web_fetch tool suggested using JavaScript rendering
|
|
47432
|
-
- You know the website is built with React/Vue/Angular/Next.js
|
|
47433
|
-
- Content loads dynamically via JavaScript
|
|
47434
|
-
- The page requires interaction (though this tool doesn't support interaction yet)
|
|
47435
|
-
|
|
47436
|
-
HOW IT WORKS:
|
|
47437
|
-
- Launches a headless Chrome browser
|
|
47438
|
-
- Navigates to the URL
|
|
47439
|
-
- Waits for JavaScript to execute and content to load
|
|
47440
|
-
- Extracts the rendered HTML and text content
|
|
47441
|
-
- Optionally captures a screenshot
|
|
47442
|
-
|
|
47443
|
-
CAPABILITIES:
|
|
47444
|
-
- Executes all JavaScript on the page
|
|
47445
|
-
- Waits for network to be idle (all resources loaded)
|
|
47446
|
-
- Can wait for specific CSS selectors to appear
|
|
47447
|
-
- Handles React, Vue, Angular, Next.js, and other SPAs
|
|
47448
|
-
- Returns content after full JavaScript execution
|
|
47449
|
-
|
|
47450
|
-
LIMITATIONS:
|
|
47451
|
-
- Slower than web_fetch (typically 3-10 seconds vs <1 second)
|
|
47452
|
-
- Uses more system resources (runs a full browser)
|
|
47453
|
-
- May still fail on sites with aggressive bot detection
|
|
47454
|
-
- Requires puppeteer to be installed (npm install puppeteer)
|
|
47455
|
-
|
|
47456
|
-
PERFORMANCE:
|
|
47457
|
-
- First call: Slower (launches browser ~1-2s)
|
|
47458
|
-
- Subsequent calls: Faster (reuses browser instance)
|
|
47459
|
-
|
|
47460
|
-
RETURNS:
|
|
47461
|
-
{
|
|
47462
|
-
success: boolean,
|
|
47463
|
-
url: string,
|
|
47464
|
-
title: string,
|
|
47465
|
-
content: string, // Clean markdown (converted via Readability + Turndown)
|
|
47466
|
-
screenshot: string, // Base64 PNG screenshot (if requested)
|
|
47467
|
-
loadTime: number, // Time taken in milliseconds
|
|
47468
|
-
excerpt: string, // Short summary excerpt (if extracted)
|
|
47469
|
-
byline: string, // Author info (if extracted)
|
|
47470
|
-
wasTruncated: boolean, // True if content was truncated
|
|
47471
|
-
error: string // Error message if failed
|
|
47472
|
-
}
|
|
47473
|
-
|
|
47474
|
-
EXAMPLES:
|
|
47475
|
-
Basic usage:
|
|
47476
|
-
{
|
|
47477
|
-
url: "https://react-app.com/page"
|
|
47478
|
-
}
|
|
47479
|
-
|
|
47480
|
-
Wait for specific content:
|
|
47481
|
-
{
|
|
47482
|
-
url: "https://app.com/dashboard",
|
|
47483
|
-
waitForSelector: "#main-content", // Wait for this element
|
|
47484
|
-
timeout: 20000
|
|
47685
|
+
// src/tools/web/createWebScrapeTool.ts
|
|
47686
|
+
init_Logger();
|
|
47687
|
+
var scrapeLogger = logger.child({ component: "webScrape" });
|
|
47688
|
+
var DEFAULT_MIN_QUALITY = 50;
|
|
47689
|
+
function stripBase64DataUris(content) {
|
|
47690
|
+
if (!content) return content;
|
|
47691
|
+
let cleaned = content.replace(/!\[[^\]]*\]\(data:[^)]+\)/g, "[image removed]");
|
|
47692
|
+
cleaned = cleaned.replace(/url\(['"]?data:[^)]+['"]?\)/gi, "url([data-uri-removed])");
|
|
47693
|
+
cleaned = cleaned.replace(/data:(?:image|font|application)\/[^;]+;base64,[A-Za-z0-9+/=]{100,}/g, "[base64-data-removed]");
|
|
47694
|
+
return cleaned;
|
|
47485
47695
|
}
|
|
47486
|
-
|
|
47487
|
-
|
|
47488
|
-
|
|
47489
|
-
|
|
47490
|
-
takeScreenshot: true
|
|
47491
|
-
}`,
|
|
47492
|
-
parameters: {
|
|
47493
|
-
type: "object",
|
|
47494
|
-
properties: {
|
|
47495
|
-
url: {
|
|
47496
|
-
type: "string",
|
|
47497
|
-
description: "The URL to fetch. Must start with http:// or https://"
|
|
47498
|
-
},
|
|
47499
|
-
waitForSelector: {
|
|
47500
|
-
type: "string",
|
|
47501
|
-
description: 'Optional CSS selector to wait for before extracting content. Example: "#main-content" or ".article-body"'
|
|
47502
|
-
},
|
|
47503
|
-
timeout: {
|
|
47504
|
-
type: "number",
|
|
47505
|
-
description: "Max wait time in milliseconds (default: 15000)"
|
|
47506
|
-
},
|
|
47507
|
-
takeScreenshot: {
|
|
47508
|
-
type: "boolean",
|
|
47509
|
-
description: "Whether to capture a screenshot of the page (default: false). Screenshot returned as base64 PNG."
|
|
47510
|
-
}
|
|
47511
|
-
},
|
|
47512
|
-
required: ["url"]
|
|
47513
|
-
}
|
|
47514
|
-
},
|
|
47515
|
-
blocking: true,
|
|
47516
|
-
timeout: 3e4
|
|
47517
|
-
// Allow extra time for browser operations
|
|
47518
|
-
},
|
|
47519
|
-
execute: async (args) => {
|
|
47520
|
-
let page = null;
|
|
47696
|
+
function createWebScrapeTool(connector) {
|
|
47697
|
+
async function tryNative(args, startTime, attemptedMethods) {
|
|
47698
|
+
attemptedMethods.push("native");
|
|
47699
|
+
scrapeLogger.debug({ url: args.url }, "Trying native fetch");
|
|
47521
47700
|
try {
|
|
47522
|
-
const
|
|
47523
|
-
|
|
47524
|
-
|
|
47525
|
-
await page.setUserAgent(
|
|
47526
|
-
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
|
47527
|
-
);
|
|
47528
|
-
const startTime = Date.now();
|
|
47529
|
-
await page.goto(args.url, {
|
|
47530
|
-
waitUntil: "networkidle2",
|
|
47531
|
-
// Wait until network is mostly idle
|
|
47532
|
-
timeout: args.timeout || 15e3
|
|
47701
|
+
const result = await webFetch.execute({
|
|
47702
|
+
url: args.url,
|
|
47703
|
+
timeout: args.timeout || 1e4
|
|
47533
47704
|
});
|
|
47534
|
-
|
|
47535
|
-
await page.waitForSelector(args.waitForSelector, {
|
|
47536
|
-
timeout: args.timeout || 15e3
|
|
47537
|
-
});
|
|
47538
|
-
}
|
|
47539
|
-
const html = await page.content();
|
|
47540
|
-
const browserTitle = await page.title();
|
|
47541
|
-
const loadTime = Date.now() - startTime;
|
|
47542
|
-
let screenshot;
|
|
47543
|
-
if (args.takeScreenshot) {
|
|
47544
|
-
const buffer = await page.screenshot({
|
|
47545
|
-
type: "png",
|
|
47546
|
-
fullPage: false
|
|
47547
|
-
// Just viewport
|
|
47548
|
-
});
|
|
47549
|
-
screenshot = buffer.toString("base64");
|
|
47550
|
-
}
|
|
47551
|
-
await page.close();
|
|
47552
|
-
const mdResult = await htmlToMarkdown(html, args.url);
|
|
47553
|
-
const title = browserTitle || mdResult.title || "Untitled";
|
|
47705
|
+
const cleanContent = stripBase64DataUris(result.content);
|
|
47554
47706
|
return {
|
|
47555
|
-
success:
|
|
47707
|
+
success: result.success,
|
|
47556
47708
|
url: args.url,
|
|
47557
|
-
|
|
47558
|
-
|
|
47559
|
-
|
|
47560
|
-
|
|
47561
|
-
|
|
47562
|
-
|
|
47563
|
-
|
|
47564
|
-
|
|
47709
|
+
finalUrl: args.url,
|
|
47710
|
+
method: "native",
|
|
47711
|
+
title: result.title,
|
|
47712
|
+
content: cleanContent,
|
|
47713
|
+
qualityScore: result.qualityScore,
|
|
47714
|
+
durationMs: Date.now() - startTime,
|
|
47715
|
+
attemptedMethods,
|
|
47716
|
+
error: result.error
|
|
47565
47717
|
};
|
|
47566
47718
|
} catch (error) {
|
|
47567
|
-
if (page) {
|
|
47568
|
-
try {
|
|
47569
|
-
await page.close();
|
|
47570
|
-
} catch {
|
|
47571
|
-
}
|
|
47572
|
-
}
|
|
47573
|
-
if (error.message === "Puppeteer not installed") {
|
|
47574
|
-
return {
|
|
47575
|
-
success: false,
|
|
47576
|
-
url: args.url,
|
|
47577
|
-
title: "",
|
|
47578
|
-
content: "",
|
|
47579
|
-
loadTime: 0,
|
|
47580
|
-
error: "Puppeteer is not installed",
|
|
47581
|
-
suggestion: "Install Puppeteer with: npm install puppeteer (note: downloads ~50MB Chrome binary)"
|
|
47582
|
-
};
|
|
47583
|
-
}
|
|
47584
47719
|
return {
|
|
47585
47720
|
success: false,
|
|
47586
47721
|
url: args.url,
|
|
47722
|
+
method: "native",
|
|
47587
47723
|
title: "",
|
|
47588
47724
|
content: "",
|
|
47589
|
-
|
|
47725
|
+
durationMs: Date.now() - startTime,
|
|
47726
|
+
attemptedMethods,
|
|
47590
47727
|
error: error.message
|
|
47591
47728
|
};
|
|
47592
47729
|
}
|
|
47593
47730
|
}
|
|
47594
|
-
|
|
47595
|
-
|
|
47596
|
-
|
|
47597
|
-
|
|
47598
|
-
|
|
47599
|
-
|
|
47600
|
-
|
|
47601
|
-
|
|
47602
|
-
|
|
47603
|
-
|
|
47604
|
-
|
|
47605
|
-
|
|
47606
|
-
|
|
47607
|
-
|
|
47608
|
-
|
|
47609
|
-
|
|
47610
|
-
|
|
47611
|
-
|
|
47612
|
-
|
|
47613
|
-
|
|
47614
|
-
|
|
47615
|
-
|
|
47616
|
-
|
|
47617
|
-
|
|
47618
|
-
|
|
47619
|
-
|
|
47620
|
-
|
|
47621
|
-
|
|
47622
|
-
|
|
47623
|
-
|
|
47624
|
-
|
|
47625
|
-
|
|
47626
|
-
|
|
47627
|
-
|
|
47628
|
-
|
|
47629
|
-
|
|
47630
|
-
|
|
47631
|
-
|
|
47632
|
-
|
|
47633
|
-
|
|
47634
|
-
|
|
47635
|
-
|
|
47636
|
-
|
|
47637
|
-
|
|
47638
|
-
|
|
47639
|
-
const data = await response.json();
|
|
47640
|
-
if (!data.web?.results || !Array.isArray(data.web.results)) {
|
|
47641
|
-
throw new Error("Invalid response from Brave API");
|
|
47642
|
-
}
|
|
47643
|
-
return data.web.results.slice(0, numResults).map((result, index) => ({
|
|
47644
|
-
title: result.title || "Untitled",
|
|
47645
|
-
url: result.url || "",
|
|
47646
|
-
snippet: result.description || "",
|
|
47647
|
-
position: index + 1
|
|
47648
|
-
}));
|
|
47649
|
-
}
|
|
47650
|
-
|
|
47651
|
-
// src/tools/web/searchProviders/tavily.ts
|
|
47652
|
-
async function searchWithTavily(query, numResults, apiKey) {
|
|
47653
|
-
const response = await fetch("https://api.tavily.com/search", {
|
|
47654
|
-
method: "POST",
|
|
47655
|
-
headers: {
|
|
47656
|
-
"Content-Type": "application/json"
|
|
47657
|
-
},
|
|
47658
|
-
body: JSON.stringify({
|
|
47659
|
-
api_key: apiKey,
|
|
47660
|
-
query,
|
|
47661
|
-
max_results: numResults,
|
|
47662
|
-
search_depth: "basic",
|
|
47663
|
-
// 'basic' or 'advanced'
|
|
47664
|
-
include_answer: false,
|
|
47665
|
-
include_raw_content: false
|
|
47666
|
-
})
|
|
47667
|
-
});
|
|
47668
|
-
if (!response.ok) {
|
|
47669
|
-
throw new Error(`Tavily API error: ${response.status} ${response.statusText}`);
|
|
47670
|
-
}
|
|
47671
|
-
const data = await response.json();
|
|
47672
|
-
if (!data.results || !Array.isArray(data.results)) {
|
|
47673
|
-
throw new Error("Invalid response from Tavily API");
|
|
47674
|
-
}
|
|
47675
|
-
return data.results.slice(0, numResults).map((result, index) => ({
|
|
47676
|
-
title: result.title || "Untitled",
|
|
47677
|
-
url: result.url || "",
|
|
47678
|
-
snippet: result.content || "",
|
|
47679
|
-
position: index + 1
|
|
47680
|
-
}));
|
|
47681
|
-
}
|
|
47682
|
-
|
|
47683
|
-
// src/tools/web/webSearch.ts
|
|
47684
|
-
var searchLogger = logger.child({ component: "webSearch" });
|
|
47685
|
-
var SEARCH_SERVICE_TYPES = ["serper", "brave-search", "tavily", "rapidapi-search"];
|
|
47686
|
-
var webSearch = {
|
|
47687
|
-
definition: {
|
|
47688
|
-
type: "function",
|
|
47689
|
-
function: {
|
|
47690
|
-
name: "web_search",
|
|
47691
|
-
description: `Search the web and get relevant results with snippets.
|
|
47692
|
-
|
|
47693
|
-
RETURNS:
|
|
47694
|
-
An array of search results, each containing:
|
|
47695
|
-
- title: Page title
|
|
47696
|
-
- url: Direct URL to the page
|
|
47697
|
-
- snippet: Short description/excerpt from the page
|
|
47698
|
-
- position: Search ranking position (1, 2, 3...)
|
|
47699
|
-
|
|
47700
|
-
USE CASES:
|
|
47701
|
-
- Find current information on any topic
|
|
47702
|
-
- Research multiple sources
|
|
47703
|
-
- Discover relevant websites
|
|
47704
|
-
- Find URLs to fetch with web_fetch tool
|
|
47705
|
-
|
|
47706
|
-
WORKFLOW PATTERN:
|
|
47707
|
-
1. Use web_search to find relevant URLs
|
|
47708
|
-
2. Use web_fetch to get full content from top results
|
|
47709
|
-
3. Process and summarize the information
|
|
47710
|
-
|
|
47711
|
-
EXAMPLE:
|
|
47712
|
-
{
|
|
47713
|
-
"query": "latest AI developments 2026",
|
|
47714
|
-
"numResults": 5
|
|
47715
|
-
}`,
|
|
47716
|
-
parameters: {
|
|
47717
|
-
type: "object",
|
|
47718
|
-
properties: {
|
|
47719
|
-
query: {
|
|
47720
|
-
type: "string",
|
|
47721
|
-
description: "The search query string. Be specific for better results."
|
|
47722
|
-
},
|
|
47723
|
-
numResults: {
|
|
47724
|
-
type: "number",
|
|
47725
|
-
description: "Number of results to return (default: 10, max: 100)."
|
|
47726
|
-
},
|
|
47727
|
-
country: {
|
|
47728
|
-
type: "string",
|
|
47729
|
-
description: 'Country/region code for localized results (e.g., "us", "gb", "de")'
|
|
47730
|
-
},
|
|
47731
|
-
language: {
|
|
47732
|
-
type: "string",
|
|
47733
|
-
description: 'Language code for results (e.g., "en", "fr", "de")'
|
|
47734
|
-
}
|
|
47735
|
-
},
|
|
47736
|
-
required: ["query"]
|
|
47737
|
-
}
|
|
47738
|
-
},
|
|
47739
|
-
blocking: true,
|
|
47740
|
-
timeout: 15e3
|
|
47741
|
-
},
|
|
47742
|
-
execute: async (args) => {
|
|
47743
|
-
const numResults = args.numResults || 10;
|
|
47744
|
-
const connector = findConnectorByServiceTypes(SEARCH_SERVICE_TYPES);
|
|
47745
|
-
if (connector) {
|
|
47746
|
-
return await executeWithConnector(connector.name, args, numResults);
|
|
47747
|
-
}
|
|
47748
|
-
return await executeWithEnvVar(args, numResults);
|
|
47749
|
-
},
|
|
47750
|
-
describeCall: (args) => `"${args.query}"${args.numResults ? ` (${args.numResults} results)` : ""}`
|
|
47751
|
-
};
|
|
47752
|
-
async function executeWithConnector(connectorName, args, numResults) {
|
|
47753
|
-
searchLogger.debug({ connectorName }, "Executing search with connector");
|
|
47754
|
-
try {
|
|
47755
|
-
const searchProvider = SearchProvider.create({ connector: connectorName });
|
|
47756
|
-
const response = await searchProvider.search(args.query, {
|
|
47757
|
-
numResults,
|
|
47758
|
-
country: args.country,
|
|
47759
|
-
language: args.language
|
|
47760
|
-
});
|
|
47761
|
-
if (response.success) {
|
|
47762
|
-
searchLogger.debug({
|
|
47763
|
-
provider: response.provider,
|
|
47764
|
-
count: response.count
|
|
47765
|
-
}, "Search completed successfully");
|
|
47766
|
-
} else {
|
|
47767
|
-
searchLogger.warn({
|
|
47768
|
-
provider: response.provider,
|
|
47769
|
-
error: response.error
|
|
47770
|
-
}, "Search failed");
|
|
47771
|
-
}
|
|
47772
|
-
return {
|
|
47773
|
-
success: response.success,
|
|
47774
|
-
query: response.query,
|
|
47775
|
-
provider: response.provider,
|
|
47776
|
-
results: response.results,
|
|
47777
|
-
count: response.count,
|
|
47778
|
-
error: response.error
|
|
47779
|
-
};
|
|
47780
|
-
} catch (error) {
|
|
47781
|
-
searchLogger.error({ error: error.message, connectorName }, "Search threw exception");
|
|
47782
|
-
return {
|
|
47783
|
-
success: false,
|
|
47784
|
-
query: args.query,
|
|
47785
|
-
provider: connectorName,
|
|
47786
|
-
results: [],
|
|
47787
|
-
count: 0,
|
|
47788
|
-
error: error.message || "Unknown error"
|
|
47789
|
-
};
|
|
47790
|
-
}
|
|
47791
|
-
}
|
|
47792
|
-
async function executeWithEnvVar(args, numResults) {
|
|
47793
|
-
const providers = [
|
|
47794
|
-
{ name: "serper", key: process.env.SERPER_API_KEY, fn: searchWithSerper },
|
|
47795
|
-
{ name: "brave", key: process.env.BRAVE_API_KEY, fn: searchWithBrave },
|
|
47796
|
-
{ name: "tavily", key: process.env.TAVILY_API_KEY, fn: searchWithTavily }
|
|
47797
|
-
];
|
|
47798
|
-
for (const provider of providers) {
|
|
47799
|
-
if (provider.key) {
|
|
47800
|
-
searchLogger.debug({ provider: provider.name }, "Using environment variable fallback");
|
|
47801
|
-
try {
|
|
47802
|
-
const results = await provider.fn(args.query, numResults, provider.key);
|
|
47803
|
-
return {
|
|
47804
|
-
success: true,
|
|
47805
|
-
query: args.query,
|
|
47806
|
-
provider: provider.name,
|
|
47807
|
-
results,
|
|
47808
|
-
count: results.length
|
|
47809
|
-
};
|
|
47810
|
-
} catch (error) {
|
|
47811
|
-
searchLogger.warn({ provider: provider.name, error: error.message }, "Provider failed, trying next");
|
|
47812
|
-
}
|
|
47731
|
+
async function tryAPI(args, startTime, attemptedMethods) {
|
|
47732
|
+
attemptedMethods.push(`api:${connector.name}`);
|
|
47733
|
+
scrapeLogger.debug({ url: args.url, connectorName: connector.name }, "Trying external API");
|
|
47734
|
+
try {
|
|
47735
|
+
const provider = ScrapeProvider.create({ connector: connector.name });
|
|
47736
|
+
const options = {
|
|
47737
|
+
timeout: args.timeout,
|
|
47738
|
+
waitForSelector: args.waitForSelector,
|
|
47739
|
+
includeHtml: args.includeHtml,
|
|
47740
|
+
includeMarkdown: args.includeMarkdown,
|
|
47741
|
+
includeLinks: args.includeLinks
|
|
47742
|
+
};
|
|
47743
|
+
const result = await provider.scrape(args.url, options);
|
|
47744
|
+
const rawContent = result.result?.content || "";
|
|
47745
|
+
const rawMarkdown = result.result?.markdown;
|
|
47746
|
+
const cleanContent = stripBase64DataUris(rawContent);
|
|
47747
|
+
const cleanMarkdown = rawMarkdown ? stripBase64DataUris(rawMarkdown) : void 0;
|
|
47748
|
+
const isDuplicate = !!cleanMarkdown && cleanContent === cleanMarkdown;
|
|
47749
|
+
return {
|
|
47750
|
+
success: result.success,
|
|
47751
|
+
url: args.url,
|
|
47752
|
+
finalUrl: result.finalUrl,
|
|
47753
|
+
method: result.provider,
|
|
47754
|
+
title: result.result?.title || "",
|
|
47755
|
+
content: cleanContent,
|
|
47756
|
+
html: result.result?.html,
|
|
47757
|
+
markdown: isDuplicate ? void 0 : cleanMarkdown,
|
|
47758
|
+
metadata: result.result?.metadata,
|
|
47759
|
+
links: result.result?.links,
|
|
47760
|
+
qualityScore: result.success ? 90 : 0,
|
|
47761
|
+
durationMs: Date.now() - startTime,
|
|
47762
|
+
attemptedMethods,
|
|
47763
|
+
error: result.error
|
|
47764
|
+
};
|
|
47765
|
+
} catch (error) {
|
|
47766
|
+
return {
|
|
47767
|
+
success: false,
|
|
47768
|
+
url: args.url,
|
|
47769
|
+
method: "api",
|
|
47770
|
+
title: "",
|
|
47771
|
+
content: "",
|
|
47772
|
+
durationMs: Date.now() - startTime,
|
|
47773
|
+
attemptedMethods,
|
|
47774
|
+
error: error.message
|
|
47775
|
+
};
|
|
47813
47776
|
}
|
|
47814
47777
|
}
|
|
47815
47778
|
return {
|
|
47816
|
-
|
|
47817
|
-
|
|
47818
|
-
|
|
47819
|
-
|
|
47820
|
-
|
|
47821
|
-
error: "No search provider configured. Set up a search connector (serper, brave-search, tavily) or set SERPER_API_KEY, BRAVE_API_KEY, or TAVILY_API_KEY environment variable."
|
|
47822
|
-
};
|
|
47823
|
-
}
|
|
47824
|
-
|
|
47825
|
-
// src/tools/web/webScrape.ts
|
|
47826
|
-
init_Logger();
|
|
47827
|
-
var scrapeLogger = logger.child({ component: "webScrape" });
|
|
47828
|
-
function stripBase64DataUris(content) {
|
|
47829
|
-
if (!content) return content;
|
|
47830
|
-
let cleaned = content.replace(/!\[[^\]]*\]\(data:[^)]+\)/g, "[image removed]");
|
|
47831
|
-
cleaned = cleaned.replace(/url\(['"]?data:[^)]+['"]?\)/gi, "url([data-uri-removed])");
|
|
47832
|
-
cleaned = cleaned.replace(/data:(?:image|font|application)\/[^;]+;base64,[A-Za-z0-9+/=]{100,}/g, "[base64-data-removed]");
|
|
47833
|
-
return cleaned;
|
|
47834
|
-
}
|
|
47835
|
-
var SCRAPE_SERVICE_TYPES = ["zenrows", "jina-reader", "firecrawl", "scrapingbee"];
|
|
47836
|
-
var DEFAULT_MIN_QUALITY = 50;
|
|
47837
|
-
var webScrape = {
|
|
47838
|
-
definition: {
|
|
47839
|
-
type: "function",
|
|
47840
|
-
function: {
|
|
47841
|
-
name: "web_scrape",
|
|
47842
|
-
description: `Scrape any URL with automatic fallback - guaranteed to work on most sites.
|
|
47779
|
+
definition: {
|
|
47780
|
+
type: "function",
|
|
47781
|
+
function: {
|
|
47782
|
+
name: "web_scrape",
|
|
47783
|
+
description: `Scrape any URL with automatic fallback - guaranteed to work on most sites.
|
|
47843
47784
|
|
|
47844
47785
|
Automatically tries multiple methods in sequence:
|
|
47845
47786
|
1. Native fetch - Fast (~1s), works for blogs/docs/articles
|
|
47846
|
-
2.
|
|
47847
|
-
3. External API - Handles bot protection, CAPTCHAs (if configured)
|
|
47787
|
+
2. External API - Handles bot protection, CAPTCHAs, SPAs (if configured)
|
|
47848
47788
|
|
|
47849
47789
|
RETURNS:
|
|
47850
47790
|
{
|
|
@@ -47879,46 +47819,64 @@ For JS-heavy sites:
|
|
|
47879
47819
|
"url": "https://spa-app.com",
|
|
47880
47820
|
"waitForSelector": ".main-content"
|
|
47881
47821
|
}`,
|
|
47882
|
-
|
|
47883
|
-
|
|
47884
|
-
|
|
47885
|
-
|
|
47886
|
-
|
|
47887
|
-
|
|
47888
|
-
|
|
47889
|
-
|
|
47890
|
-
|
|
47891
|
-
|
|
47892
|
-
|
|
47893
|
-
|
|
47894
|
-
|
|
47895
|
-
|
|
47896
|
-
|
|
47897
|
-
|
|
47898
|
-
|
|
47899
|
-
|
|
47900
|
-
|
|
47901
|
-
|
|
47902
|
-
|
|
47903
|
-
|
|
47822
|
+
parameters: {
|
|
47823
|
+
type: "object",
|
|
47824
|
+
properties: {
|
|
47825
|
+
url: {
|
|
47826
|
+
type: "string",
|
|
47827
|
+
description: "URL to scrape. Must start with http:// or https://"
|
|
47828
|
+
},
|
|
47829
|
+
timeout: {
|
|
47830
|
+
type: "number",
|
|
47831
|
+
description: "Timeout in milliseconds (default: 30000)"
|
|
47832
|
+
},
|
|
47833
|
+
includeHtml: {
|
|
47834
|
+
type: "boolean",
|
|
47835
|
+
description: "Include raw HTML in response (default: false)"
|
|
47836
|
+
},
|
|
47837
|
+
includeMarkdown: {
|
|
47838
|
+
type: "boolean",
|
|
47839
|
+
description: "Include markdown conversion (default: false)"
|
|
47840
|
+
},
|
|
47841
|
+
includeLinks: {
|
|
47842
|
+
type: "boolean",
|
|
47843
|
+
description: "Extract and include links (default: false)"
|
|
47844
|
+
},
|
|
47845
|
+
waitForSelector: {
|
|
47846
|
+
type: "string",
|
|
47847
|
+
description: "CSS selector to wait for before scraping (for JS-heavy sites)"
|
|
47848
|
+
}
|
|
47904
47849
|
},
|
|
47905
|
-
|
|
47906
|
-
|
|
47907
|
-
|
|
47908
|
-
|
|
47909
|
-
|
|
47910
|
-
required: ["url"]
|
|
47911
|
-
}
|
|
47850
|
+
required: ["url"]
|
|
47851
|
+
}
|
|
47852
|
+
},
|
|
47853
|
+
blocking: true,
|
|
47854
|
+
timeout: 6e4
|
|
47912
47855
|
},
|
|
47913
|
-
|
|
47914
|
-
|
|
47915
|
-
|
|
47916
|
-
|
|
47917
|
-
|
|
47918
|
-
|
|
47919
|
-
|
|
47920
|
-
|
|
47921
|
-
|
|
47856
|
+
execute: async (args) => {
|
|
47857
|
+
const startTime = Date.now();
|
|
47858
|
+
const attemptedMethods = [];
|
|
47859
|
+
try {
|
|
47860
|
+
new URL(args.url);
|
|
47861
|
+
} catch {
|
|
47862
|
+
return {
|
|
47863
|
+
success: false,
|
|
47864
|
+
url: args.url,
|
|
47865
|
+
method: "none",
|
|
47866
|
+
title: "",
|
|
47867
|
+
content: "",
|
|
47868
|
+
durationMs: Date.now() - startTime,
|
|
47869
|
+
attemptedMethods: [],
|
|
47870
|
+
error: "Invalid URL format"
|
|
47871
|
+
};
|
|
47872
|
+
}
|
|
47873
|
+
const native = await tryNative(args, startTime, attemptedMethods);
|
|
47874
|
+
if (native.success && (native.qualityScore ?? 0) >= DEFAULT_MIN_QUALITY) {
|
|
47875
|
+
return native;
|
|
47876
|
+
}
|
|
47877
|
+
const api = await tryAPI(args, startTime, attemptedMethods);
|
|
47878
|
+
if (api.success) return api;
|
|
47879
|
+
if (native.success) return native;
|
|
47922
47880
|
return {
|
|
47923
47881
|
success: false,
|
|
47924
47882
|
url: args.url,
|
|
@@ -47926,232 +47884,119 @@ For JS-heavy sites:
|
|
|
47926
47884
|
title: "",
|
|
47927
47885
|
content: "",
|
|
47928
47886
|
durationMs: Date.now() - startTime,
|
|
47929
|
-
attemptedMethods
|
|
47930
|
-
error: "
|
|
47887
|
+
attemptedMethods,
|
|
47888
|
+
error: "All scraping methods failed. Site may have bot protection."
|
|
47931
47889
|
};
|
|
47932
|
-
}
|
|
47933
|
-
|
|
47934
|
-
|
|
47935
|
-
return native;
|
|
47936
|
-
}
|
|
47937
|
-
const js = await tryJS(args, startTime, attemptedMethods);
|
|
47938
|
-
if (js.success && (js.qualityScore ?? 0) >= DEFAULT_MIN_QUALITY) {
|
|
47939
|
-
return js;
|
|
47940
|
-
}
|
|
47941
|
-
const connector = findConnectorByServiceTypes(SCRAPE_SERVICE_TYPES);
|
|
47942
|
-
if (connector) {
|
|
47943
|
-
const api = await tryAPI(connector.name, args, startTime, attemptedMethods);
|
|
47944
|
-
if (api.success) return api;
|
|
47945
|
-
}
|
|
47946
|
-
if (js.success) return js;
|
|
47947
|
-
if (native.success) return native;
|
|
47948
|
-
return {
|
|
47949
|
-
success: false,
|
|
47950
|
-
url: args.url,
|
|
47951
|
-
method: "none",
|
|
47952
|
-
title: "",
|
|
47953
|
-
content: "",
|
|
47954
|
-
durationMs: Date.now() - startTime,
|
|
47955
|
-
attemptedMethods,
|
|
47956
|
-
error: "All scraping methods failed. Site may have bot protection."
|
|
47957
|
-
};
|
|
47958
|
-
},
|
|
47959
|
-
describeCall: (args) => args.url
|
|
47960
|
-
};
|
|
47961
|
-
async function tryNative(args, startTime, attemptedMethods) {
|
|
47962
|
-
attemptedMethods.push("native");
|
|
47963
|
-
scrapeLogger.debug({ url: args.url }, "Trying native fetch");
|
|
47964
|
-
try {
|
|
47965
|
-
const result = await webFetch.execute({
|
|
47966
|
-
url: args.url,
|
|
47967
|
-
timeout: args.timeout || 1e4
|
|
47968
|
-
});
|
|
47969
|
-
const cleanContent = stripBase64DataUris(result.content);
|
|
47970
|
-
return {
|
|
47971
|
-
success: result.success,
|
|
47972
|
-
url: args.url,
|
|
47973
|
-
finalUrl: args.url,
|
|
47974
|
-
method: "native",
|
|
47975
|
-
title: result.title,
|
|
47976
|
-
content: cleanContent,
|
|
47977
|
-
// Native method already returns markdown-like content — no separate markdown field needed
|
|
47978
|
-
// (would just duplicate content and waste tokens)
|
|
47979
|
-
qualityScore: result.qualityScore,
|
|
47980
|
-
durationMs: Date.now() - startTime,
|
|
47981
|
-
attemptedMethods,
|
|
47982
|
-
error: result.error
|
|
47983
|
-
};
|
|
47984
|
-
} catch (error) {
|
|
47985
|
-
return {
|
|
47986
|
-
success: false,
|
|
47987
|
-
url: args.url,
|
|
47988
|
-
method: "native",
|
|
47989
|
-
title: "",
|
|
47990
|
-
content: "",
|
|
47991
|
-
durationMs: Date.now() - startTime,
|
|
47992
|
-
attemptedMethods,
|
|
47993
|
-
error: error.message
|
|
47994
|
-
};
|
|
47995
|
-
}
|
|
47890
|
+
},
|
|
47891
|
+
describeCall: (args) => args.url
|
|
47892
|
+
};
|
|
47996
47893
|
}
|
|
47997
|
-
|
|
47998
|
-
|
|
47999
|
-
|
|
48000
|
-
|
|
48001
|
-
|
|
48002
|
-
|
|
48003
|
-
|
|
48004
|
-
|
|
48005
|
-
|
|
48006
|
-
const cleanContent = stripBase64DataUris(result.content);
|
|
48007
|
-
return {
|
|
48008
|
-
success: result.success,
|
|
48009
|
-
url: args.url,
|
|
48010
|
-
finalUrl: args.url,
|
|
48011
|
-
method: "js",
|
|
48012
|
-
title: result.title,
|
|
48013
|
-
content: cleanContent,
|
|
48014
|
-
// JS method already returns markdown-like content — no separate markdown field needed
|
|
48015
|
-
qualityScore: result.success ? 80 : 0,
|
|
48016
|
-
durationMs: Date.now() - startTime,
|
|
48017
|
-
attemptedMethods,
|
|
48018
|
-
error: result.error
|
|
48019
|
-
};
|
|
48020
|
-
} catch (error) {
|
|
48021
|
-
return {
|
|
48022
|
-
success: false,
|
|
48023
|
-
url: args.url,
|
|
48024
|
-
method: "js",
|
|
48025
|
-
title: "",
|
|
48026
|
-
content: "",
|
|
48027
|
-
durationMs: Date.now() - startTime,
|
|
48028
|
-
attemptedMethods,
|
|
48029
|
-
error: error.message
|
|
48030
|
-
};
|
|
47894
|
+
|
|
47895
|
+
// src/tools/web/register.ts
|
|
47896
|
+
var SEARCH_SERVICE_TYPES = ["serper", "brave-search", "tavily", "rapidapi-search"];
|
|
47897
|
+
var SCRAPE_SERVICE_TYPES = ["zenrows", "jina-reader", "firecrawl", "scrapingbee"];
|
|
47898
|
+
function registerWebTools() {
|
|
47899
|
+
for (const st of SEARCH_SERVICE_TYPES) {
|
|
47900
|
+
ConnectorTools.registerService(st, (connector) => [
|
|
47901
|
+
createWebSearchTool(connector)
|
|
47902
|
+
]);
|
|
48031
47903
|
}
|
|
48032
|
-
|
|
48033
|
-
|
|
48034
|
-
|
|
48035
|
-
|
|
48036
|
-
try {
|
|
48037
|
-
const provider = ScrapeProvider.create({ connector: connectorName });
|
|
48038
|
-
const options = {
|
|
48039
|
-
timeout: args.timeout,
|
|
48040
|
-
waitForSelector: args.waitForSelector,
|
|
48041
|
-
includeHtml: args.includeHtml,
|
|
48042
|
-
includeMarkdown: args.includeMarkdown,
|
|
48043
|
-
includeLinks: args.includeLinks
|
|
48044
|
-
};
|
|
48045
|
-
const result = await provider.scrape(args.url, options);
|
|
48046
|
-
const rawContent = result.result?.content || "";
|
|
48047
|
-
const rawMarkdown = result.result?.markdown;
|
|
48048
|
-
const cleanContent = stripBase64DataUris(rawContent);
|
|
48049
|
-
const cleanMarkdown = rawMarkdown ? stripBase64DataUris(rawMarkdown) : void 0;
|
|
48050
|
-
const isDuplicate = !!cleanMarkdown && cleanContent === cleanMarkdown;
|
|
48051
|
-
return {
|
|
48052
|
-
success: result.success,
|
|
48053
|
-
url: args.url,
|
|
48054
|
-
finalUrl: result.finalUrl,
|
|
48055
|
-
method: result.provider,
|
|
48056
|
-
title: result.result?.title || "",
|
|
48057
|
-
content: cleanContent,
|
|
48058
|
-
html: result.result?.html,
|
|
48059
|
-
// Keep raw HTML as-is (only used if explicitly requested)
|
|
48060
|
-
markdown: isDuplicate ? void 0 : cleanMarkdown,
|
|
48061
|
-
metadata: result.result?.metadata,
|
|
48062
|
-
links: result.result?.links,
|
|
48063
|
-
qualityScore: result.success ? 90 : 0,
|
|
48064
|
-
durationMs: Date.now() - startTime,
|
|
48065
|
-
attemptedMethods,
|
|
48066
|
-
error: result.error
|
|
48067
|
-
};
|
|
48068
|
-
} catch (error) {
|
|
48069
|
-
return {
|
|
48070
|
-
success: false,
|
|
48071
|
-
url: args.url,
|
|
48072
|
-
method: "api",
|
|
48073
|
-
title: "",
|
|
48074
|
-
content: "",
|
|
48075
|
-
durationMs: Date.now() - startTime,
|
|
48076
|
-
attemptedMethods,
|
|
48077
|
-
error: error.message
|
|
48078
|
-
};
|
|
47904
|
+
for (const st of SCRAPE_SERVICE_TYPES) {
|
|
47905
|
+
ConnectorTools.registerService(st, (connector) => [
|
|
47906
|
+
createWebScrapeTool(connector)
|
|
47907
|
+
]);
|
|
48079
47908
|
}
|
|
48080
47909
|
}
|
|
48081
47910
|
|
|
47911
|
+
// src/tools/web/index.ts
|
|
47912
|
+
registerWebTools();
|
|
47913
|
+
|
|
48082
47914
|
// src/tools/code/executeJavaScript.ts
|
|
48083
47915
|
init_Connector();
|
|
48084
|
-
|
|
48085
|
-
|
|
48086
|
-
|
|
48087
|
-
|
|
48088
|
-
|
|
48089
|
-
|
|
48090
|
-
|
|
48091
|
-
|
|
48092
|
-
}).join("\n
|
|
48093
|
-
return `
|
|
48094
|
-
|
|
48095
|
-
|
|
48096
|
-
|
|
48097
|
-
|
|
48098
|
-
|
|
48099
|
-
|
|
47916
|
+
var DEFAULT_TIMEOUT = 1e4;
|
|
47917
|
+
var DEFAULT_MAX_TIMEOUT = 3e4;
|
|
47918
|
+
function formatConnectorEntry(c) {
|
|
47919
|
+
const parts = [];
|
|
47920
|
+
const serviceOrVendor = c.serviceType ?? c.vendor ?? void 0;
|
|
47921
|
+
if (serviceOrVendor) parts.push(`Service: ${serviceOrVendor}`);
|
|
47922
|
+
if (c.config.description) parts.push(c.config.description);
|
|
47923
|
+
if (c.baseURL) parts.push(`URL: ${c.baseURL}`);
|
|
47924
|
+
const details = parts.map((p) => ` ${p}`).join("\n");
|
|
47925
|
+
return ` \u2022 "${c.name}" (${c.displayName})
|
|
47926
|
+
${details}`;
|
|
47927
|
+
}
|
|
47928
|
+
function generateDescription(context, maxTimeout) {
|
|
47929
|
+
const registry = context?.connectorRegistry ?? Connector.asRegistry();
|
|
47930
|
+
const connectors = registry.listAll();
|
|
47931
|
+
const connectorList = connectors.length > 0 ? connectors.map(formatConnectorEntry).join("\n\n") : " No connectors registered.";
|
|
47932
|
+
const timeoutSec = Math.round(maxTimeout / 1e3);
|
|
47933
|
+
return `Execute JavaScript code in a secure sandbox with authenticated API access to external services.
|
|
47934
|
+
|
|
47935
|
+
Use this tool when you need to:
|
|
47936
|
+
- Call external APIs (GitHub, Slack, Stripe, etc.) using registered connectors
|
|
47937
|
+
- Process, transform, or compute data that requires programmatic logic
|
|
47938
|
+
- Chain multiple API calls or perform complex data manipulation
|
|
47939
|
+
- Do anything that plain text generation cannot accomplish
|
|
47940
|
+
|
|
47941
|
+
SANDBOX API:
|
|
47942
|
+
|
|
47943
|
+
1. authenticatedFetch(url, options, connectorName)
|
|
47944
|
+
Makes authenticated HTTP requests using the connector's credentials.
|
|
47945
|
+
The current user's identity (userId) is automatically included \u2014 no need to pass it.
|
|
47946
|
+
Auth headers are added automatically \u2014 DO NOT set Authorization header manually.
|
|
48100
47947
|
|
|
48101
47948
|
Parameters:
|
|
48102
|
-
\u2022 url: Full URL or relative
|
|
47949
|
+
\u2022 url: Full URL or path relative to the connector's base URL
|
|
48103
47950
|
- Full: "https://api.github.com/user/repos"
|
|
48104
|
-
- Relative: "/user/repos" (
|
|
48105
|
-
\u2022 options: Standard fetch options { method,
|
|
48106
|
-
|
|
48107
|
-
\u2022
|
|
47951
|
+
- Relative: "/user/repos" (resolved against connector's base URL)
|
|
47952
|
+
\u2022 options: Standard fetch options { method, headers, body }
|
|
47953
|
+
- For POST/PUT: set body to JSON.stringify(data) and headers to { 'Content-Type': 'application/json' }
|
|
47954
|
+
\u2022 connectorName: Name of a registered connector (see list below)
|
|
48108
47955
|
|
|
48109
47956
|
Returns: Promise<Response>
|
|
48110
|
-
\u2022 response.ok
|
|
48111
|
-
\u2022 response.status
|
|
48112
|
-
\u2022 response.json()
|
|
48113
|
-
\u2022 response.text()
|
|
47957
|
+
\u2022 response.ok \u2014 true if status 200-299
|
|
47958
|
+
\u2022 response.status \u2014 HTTP status code
|
|
47959
|
+
\u2022 await response.json() \u2014 parse JSON body
|
|
47960
|
+
\u2022 await response.text() \u2014 get text body
|
|
48114
47961
|
|
|
48115
|
-
|
|
48116
|
-
\u2022 Bearer tokens (GitHub, Slack, Stripe)
|
|
48117
|
-
\u2022 Bot tokens (Discord)
|
|
48118
|
-
\u2022 Basic auth (Twilio, Zendesk)
|
|
48119
|
-
\u2022 Custom headers (Shopify uses X-Shopify-Access-Token)
|
|
47962
|
+
2. fetch(url, options) \u2014 Standard fetch without authentication
|
|
48120
47963
|
|
|
48121
|
-
|
|
48122
|
-
|
|
48123
|
-
4. fetch(url, options) - Standard fetch (no auth)
|
|
47964
|
+
3. connectors.list() \u2014 Array of available connector names
|
|
47965
|
+
4. connectors.get(name) \u2014 Connector info: { displayName, description, baseURL, serviceType }
|
|
48124
47966
|
|
|
48125
|
-
|
|
48126
|
-
\u2022 input
|
|
48127
|
-
\u2022 output
|
|
47967
|
+
VARIABLES:
|
|
47968
|
+
\u2022 input \u2014 data passed via the "input" parameter (default: {})
|
|
47969
|
+
\u2022 output \u2014 SET THIS to return your result to the caller
|
|
48128
47970
|
|
|
48129
|
-
|
|
47971
|
+
GLOBALS: console.log/error/warn, JSON, Math, Date, Buffer, Promise, Array, Object, String, Number, Boolean, setTimeout, setInterval, URL, URLSearchParams, RegExp, Map, Set, Error, TextEncoder, TextDecoder
|
|
48130
47972
|
|
|
48131
47973
|
REGISTERED CONNECTORS:
|
|
48132
47974
|
${connectorList}
|
|
48133
47975
|
|
|
48134
|
-
|
|
48135
|
-
(async () => {
|
|
48136
|
-
const response = await authenticatedFetch(
|
|
48137
|
-
'/user/repos',
|
|
48138
|
-
{ method: 'GET' },
|
|
48139
|
-
'github'
|
|
48140
|
-
);
|
|
47976
|
+
EXAMPLES:
|
|
48141
47977
|
|
|
48142
|
-
|
|
48143
|
-
|
|
48144
|
-
|
|
47978
|
+
// GET request
|
|
47979
|
+
const resp = await authenticatedFetch('/user/repos', { method: 'GET' }, 'github');
|
|
47980
|
+
const repos = await resp.json();
|
|
47981
|
+
output = repos.map(r => r.full_name);
|
|
48145
47982
|
|
|
48146
|
-
|
|
48147
|
-
|
|
47983
|
+
// POST request with JSON body
|
|
47984
|
+
const resp = await authenticatedFetch('/chat.postMessage', {
|
|
47985
|
+
method: 'POST',
|
|
47986
|
+
headers: { 'Content-Type': 'application/json' },
|
|
47987
|
+
body: JSON.stringify({ channel: '#general', text: 'Hello!' })
|
|
47988
|
+
}, 'slack');
|
|
47989
|
+
output = await resp.json();
|
|
48148
47990
|
|
|
48149
|
-
|
|
48150
|
-
|
|
47991
|
+
// Data processing (no API needed)
|
|
47992
|
+
const items = input.data;
|
|
47993
|
+
output = items.filter(i => i.score > 0.8).sort((a, b) => b.score - a.score);
|
|
48151
47994
|
|
|
48152
|
-
|
|
47995
|
+
LIMITS: ${timeoutSec}s max timeout, no file system access, no require/import.`;
|
|
48153
47996
|
}
|
|
48154
|
-
function createExecuteJavaScriptTool() {
|
|
47997
|
+
function createExecuteJavaScriptTool(options) {
|
|
47998
|
+
const maxTimeout = options?.maxTimeout ?? DEFAULT_MAX_TIMEOUT;
|
|
47999
|
+
const defaultTimeout = options?.defaultTimeout ?? DEFAULT_TIMEOUT;
|
|
48155
48000
|
return {
|
|
48156
48001
|
definition: {
|
|
48157
48002
|
type: "function",
|
|
@@ -48164,32 +48009,40 @@ function createExecuteJavaScriptTool() {
|
|
|
48164
48009
|
properties: {
|
|
48165
48010
|
code: {
|
|
48166
48011
|
type: "string",
|
|
48167
|
-
description: 'JavaScript code to execute.
|
|
48012
|
+
description: 'JavaScript code to execute. Set the "output" variable with your result. Code is auto-wrapped in async IIFE \u2014 you can use await directly. For explicit async control, wrap in (async () => { ... })().'
|
|
48168
48013
|
},
|
|
48169
48014
|
input: {
|
|
48170
|
-
description: 'Optional
|
|
48015
|
+
description: 'Optional data available as the "input" variable in your code. Can be any JSON value.'
|
|
48171
48016
|
},
|
|
48172
48017
|
timeout: {
|
|
48173
48018
|
type: "number",
|
|
48174
|
-
description:
|
|
48019
|
+
description: `Execution timeout in milliseconds. Default: ${defaultTimeout}ms, max: ${maxTimeout}ms. Increase for slow API calls or multiple sequential requests.`
|
|
48175
48020
|
}
|
|
48176
48021
|
},
|
|
48177
48022
|
required: ["code"]
|
|
48178
48023
|
}
|
|
48179
48024
|
},
|
|
48180
48025
|
blocking: true,
|
|
48181
|
-
timeout:
|
|
48182
|
-
// Tool timeout
|
|
48026
|
+
timeout: maxTimeout + 5e3
|
|
48027
|
+
// Tool-level timeout slightly above max code timeout
|
|
48183
48028
|
},
|
|
48184
|
-
// Dynamic description
|
|
48185
|
-
//
|
|
48186
|
-
descriptionFactory: generateDescription,
|
|
48187
|
-
execute: async (args) => {
|
|
48029
|
+
// Dynamic description — regenerated each time tool definitions are sent to LLM.
|
|
48030
|
+
// Receives ToolContext so connector list is scoped to current userId.
|
|
48031
|
+
descriptionFactory: (context) => generateDescription(context, maxTimeout),
|
|
48032
|
+
execute: async (args, context) => {
|
|
48188
48033
|
const logs = [];
|
|
48189
48034
|
const startTime = Date.now();
|
|
48190
48035
|
try {
|
|
48191
|
-
const timeout = Math.min(args.timeout ||
|
|
48192
|
-
const
|
|
48036
|
+
const timeout = Math.min(Math.max(args.timeout || defaultTimeout, 0), maxTimeout);
|
|
48037
|
+
const registry = context?.connectorRegistry ?? Connector.asRegistry();
|
|
48038
|
+
const result = await executeInVM(
|
|
48039
|
+
args.code,
|
|
48040
|
+
args.input,
|
|
48041
|
+
timeout,
|
|
48042
|
+
logs,
|
|
48043
|
+
context?.userId,
|
|
48044
|
+
registry
|
|
48045
|
+
);
|
|
48193
48046
|
return {
|
|
48194
48047
|
success: true,
|
|
48195
48048
|
result,
|
|
@@ -48209,31 +48062,36 @@ function createExecuteJavaScriptTool() {
|
|
|
48209
48062
|
};
|
|
48210
48063
|
}
|
|
48211
48064
|
var executeJavaScript = createExecuteJavaScriptTool();
|
|
48212
|
-
async function executeInVM(code, input, timeout, logs) {
|
|
48065
|
+
async function executeInVM(code, input, timeout, logs, userId, registry) {
|
|
48213
48066
|
const sandbox = {
|
|
48214
48067
|
// Input/output
|
|
48215
|
-
input: input
|
|
48068
|
+
input: input ?? {},
|
|
48216
48069
|
output: null,
|
|
48217
|
-
// Console (captured)
|
|
48070
|
+
// Console (captured) — stringify objects for readable logs
|
|
48218
48071
|
console: {
|
|
48219
|
-
log: (...args) => logs.push(args.map((a) => String(a)).join(" ")),
|
|
48220
|
-
error: (...args) => logs.push("ERROR: " + args.map((a) => String(a)).join(" ")),
|
|
48221
|
-
warn: (...args) => logs.push("WARN: " + args.map((a) => String(a)).join(" "))
|
|
48072
|
+
log: (...args) => logs.push(args.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")),
|
|
48073
|
+
error: (...args) => logs.push("ERROR: " + args.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")),
|
|
48074
|
+
warn: (...args) => logs.push("WARN: " + args.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" "))
|
|
48222
48075
|
},
|
|
48223
|
-
// Authenticated fetch
|
|
48224
|
-
|
|
48225
|
-
|
|
48076
|
+
// Authenticated fetch — userId auto-injected from ToolContext.
|
|
48077
|
+
// Only connectors visible in the scoped registry are accessible.
|
|
48078
|
+
authenticatedFetch: (url2, options, connectorName) => {
|
|
48079
|
+
registry.get(connectorName);
|
|
48080
|
+
return authenticatedFetch(url2, options, connectorName, userId);
|
|
48081
|
+
},
|
|
48082
|
+
// Standard fetch (no auth)
|
|
48226
48083
|
fetch: globalThis.fetch,
|
|
48227
|
-
// Connector info
|
|
48084
|
+
// Connector info (userId-scoped)
|
|
48228
48085
|
connectors: {
|
|
48229
|
-
list: () =>
|
|
48086
|
+
list: () => registry.list(),
|
|
48230
48087
|
get: (name) => {
|
|
48231
48088
|
try {
|
|
48232
|
-
const connector =
|
|
48089
|
+
const connector = registry.get(name);
|
|
48233
48090
|
return {
|
|
48234
48091
|
displayName: connector.displayName,
|
|
48235
48092
|
description: connector.config.description || "",
|
|
48236
|
-
baseURL: connector.baseURL
|
|
48093
|
+
baseURL: connector.baseURL,
|
|
48094
|
+
serviceType: connector.serviceType
|
|
48237
48095
|
};
|
|
48238
48096
|
} catch {
|
|
48239
48097
|
return null;
|
|
@@ -48250,14 +48108,22 @@ async function executeInVM(code, input, timeout, logs) {
|
|
|
48250
48108
|
clearTimeout,
|
|
48251
48109
|
clearInterval,
|
|
48252
48110
|
Promise,
|
|
48253
|
-
//
|
|
48111
|
+
// Built-in types
|
|
48254
48112
|
Array,
|
|
48255
48113
|
Object,
|
|
48256
48114
|
String,
|
|
48257
48115
|
Number,
|
|
48258
|
-
Boolean
|
|
48116
|
+
Boolean,
|
|
48117
|
+
RegExp,
|
|
48118
|
+
Map,
|
|
48119
|
+
Set,
|
|
48120
|
+
Error,
|
|
48121
|
+
URL,
|
|
48122
|
+
URLSearchParams,
|
|
48123
|
+
TextEncoder,
|
|
48124
|
+
TextDecoder
|
|
48259
48125
|
};
|
|
48260
|
-
const
|
|
48126
|
+
const vmContext = vm.createContext(sandbox);
|
|
48261
48127
|
const wrappedCode = code.trim().startsWith("(async") ? code : `
|
|
48262
48128
|
(async () => {
|
|
48263
48129
|
${code}
|
|
@@ -48265,7 +48131,7 @@ async function executeInVM(code, input, timeout, logs) {
|
|
|
48265
48131
|
})()
|
|
48266
48132
|
`;
|
|
48267
48133
|
const script = new vm.Script(wrappedCode);
|
|
48268
|
-
const resultPromise = script.runInContext(
|
|
48134
|
+
const resultPromise = script.runInContext(vmContext, {
|
|
48269
48135
|
timeout,
|
|
48270
48136
|
displayErrors: true
|
|
48271
48137
|
});
|
|
@@ -48721,7 +48587,7 @@ function createTextToSpeechTool(connector, storage, userId) {
|
|
|
48721
48587
|
}
|
|
48722
48588
|
|
|
48723
48589
|
// src/tools/multimedia/speechToText.ts
|
|
48724
|
-
function createSpeechToTextTool(connector, storage
|
|
48590
|
+
function createSpeechToTextTool(connector, storage) {
|
|
48725
48591
|
const vendor = connector.vendor;
|
|
48726
48592
|
const handler = storage ?? getMediaStorage();
|
|
48727
48593
|
const vendorModels = vendor ? getSTTModelsByVendor(vendor) : [];
|
|
@@ -48982,7 +48848,8 @@ EXAMPLES:
|
|
|
48982
48848
|
riskLevel: "low",
|
|
48983
48849
|
approvalMessage: `Search files in a GitHub repository via ${connector.displayName}`
|
|
48984
48850
|
},
|
|
48985
|
-
execute: async (args) => {
|
|
48851
|
+
execute: async (args, context) => {
|
|
48852
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
48986
48853
|
const resolved = resolveRepository(args.repository, connector);
|
|
48987
48854
|
if (!resolved.success) {
|
|
48988
48855
|
return { success: false, error: resolved.error };
|
|
@@ -48994,14 +48861,14 @@ EXAMPLES:
|
|
|
48994
48861
|
const repoInfo = await githubFetch(
|
|
48995
48862
|
connector,
|
|
48996
48863
|
`/repos/${owner}/${repo}`,
|
|
48997
|
-
{ userId }
|
|
48864
|
+
{ userId: effectiveUserId }
|
|
48998
48865
|
);
|
|
48999
48866
|
ref = repoInfo.default_branch;
|
|
49000
48867
|
}
|
|
49001
48868
|
const tree = await githubFetch(
|
|
49002
48869
|
connector,
|
|
49003
48870
|
`/repos/${owner}/${repo}/git/trees/${ref}?recursive=1`,
|
|
49004
|
-
{ userId }
|
|
48871
|
+
{ userId: effectiveUserId }
|
|
49005
48872
|
);
|
|
49006
48873
|
const matching = tree.tree.filter(
|
|
49007
48874
|
(entry) => entry.type === "blob" && matchGlobPattern2(args.pattern, entry.path)
|
|
@@ -49092,7 +48959,8 @@ EXAMPLES:
|
|
|
49092
48959
|
riskLevel: "low",
|
|
49093
48960
|
approvalMessage: `Search code in a GitHub repository via ${connector.displayName}`
|
|
49094
48961
|
},
|
|
49095
|
-
execute: async (args) => {
|
|
48962
|
+
execute: async (args, context) => {
|
|
48963
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
49096
48964
|
const resolved = resolveRepository(args.repository, connector);
|
|
49097
48965
|
if (!resolved.success) {
|
|
49098
48966
|
return { success: false, error: resolved.error };
|
|
@@ -49109,7 +48977,7 @@ EXAMPLES:
|
|
|
49109
48977
|
connector,
|
|
49110
48978
|
`/search/code`,
|
|
49111
48979
|
{
|
|
49112
|
-
userId,
|
|
48980
|
+
userId: effectiveUserId,
|
|
49113
48981
|
// Request text-match fragments
|
|
49114
48982
|
accept: "application/vnd.github.text-match+json",
|
|
49115
48983
|
queryParams: { q, per_page: perPage }
|
|
@@ -49196,7 +49064,8 @@ NOTE: Files larger than 1MB are fetched via the Git Blob API. Very large files (
|
|
|
49196
49064
|
riskLevel: "low",
|
|
49197
49065
|
approvalMessage: `Read a file from a GitHub repository via ${connector.displayName}`
|
|
49198
49066
|
},
|
|
49199
|
-
execute: async (args) => {
|
|
49067
|
+
execute: async (args, context) => {
|
|
49068
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
49200
49069
|
const resolved = resolveRepository(args.repository, connector);
|
|
49201
49070
|
if (!resolved.success) {
|
|
49202
49071
|
return { success: false, error: resolved.error };
|
|
@@ -49210,7 +49079,7 @@ NOTE: Files larger than 1MB are fetched via the Git Blob API. Very large files (
|
|
|
49210
49079
|
const contentResp = await githubFetch(
|
|
49211
49080
|
connector,
|
|
49212
49081
|
`/repos/${owner}/${repo}/contents/${args.path}${refParam}`,
|
|
49213
|
-
{ userId }
|
|
49082
|
+
{ userId: effectiveUserId }
|
|
49214
49083
|
);
|
|
49215
49084
|
if (contentResp.type !== "file") {
|
|
49216
49085
|
return {
|
|
@@ -49227,7 +49096,7 @@ NOTE: Files larger than 1MB are fetched via the Git Blob API. Very large files (
|
|
|
49227
49096
|
const blob = await githubFetch(
|
|
49228
49097
|
connector,
|
|
49229
49098
|
contentResp.git_url,
|
|
49230
|
-
{ userId }
|
|
49099
|
+
{ userId: effectiveUserId }
|
|
49231
49100
|
);
|
|
49232
49101
|
fileContent = Buffer.from(blob.content, "base64").toString("utf-8");
|
|
49233
49102
|
fileSize = blob.size;
|
|
@@ -49314,7 +49183,8 @@ EXAMPLES:
|
|
|
49314
49183
|
riskLevel: "low",
|
|
49315
49184
|
approvalMessage: `Get pull request details from GitHub via ${connector.displayName}`
|
|
49316
49185
|
},
|
|
49317
|
-
execute: async (args) => {
|
|
49186
|
+
execute: async (args, context) => {
|
|
49187
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
49318
49188
|
const resolved = resolveRepository(args.repository, connector);
|
|
49319
49189
|
if (!resolved.success) {
|
|
49320
49190
|
return { success: false, error: resolved.error };
|
|
@@ -49324,7 +49194,7 @@ EXAMPLES:
|
|
|
49324
49194
|
const pr = await githubFetch(
|
|
49325
49195
|
connector,
|
|
49326
49196
|
`/repos/${owner}/${repo}/pulls/${args.pull_number}`,
|
|
49327
|
-
{ userId }
|
|
49197
|
+
{ userId: effectiveUserId }
|
|
49328
49198
|
);
|
|
49329
49199
|
return {
|
|
49330
49200
|
success: true,
|
|
@@ -49400,7 +49270,8 @@ NOTE: Very large diffs may be truncated by GitHub. Patch content may be absent f
|
|
|
49400
49270
|
riskLevel: "low",
|
|
49401
49271
|
approvalMessage: `Get PR changed files from GitHub via ${connector.displayName}`
|
|
49402
49272
|
},
|
|
49403
|
-
execute: async (args) => {
|
|
49273
|
+
execute: async (args, context) => {
|
|
49274
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
49404
49275
|
const resolved = resolveRepository(args.repository, connector);
|
|
49405
49276
|
if (!resolved.success) {
|
|
49406
49277
|
return { success: false, error: resolved.error };
|
|
@@ -49411,7 +49282,7 @@ NOTE: Very large diffs may be truncated by GitHub. Patch content may be absent f
|
|
|
49411
49282
|
connector,
|
|
49412
49283
|
`/repos/${owner}/${repo}/pulls/${args.pull_number}/files`,
|
|
49413
49284
|
{
|
|
49414
|
-
userId,
|
|
49285
|
+
userId: effectiveUserId,
|
|
49415
49286
|
queryParams: { per_page: 100 }
|
|
49416
49287
|
}
|
|
49417
49288
|
);
|
|
@@ -49482,7 +49353,8 @@ EXAMPLES:
|
|
|
49482
49353
|
riskLevel: "low",
|
|
49483
49354
|
approvalMessage: `Get PR comments and reviews from GitHub via ${connector.displayName}`
|
|
49484
49355
|
},
|
|
49485
|
-
execute: async (args) => {
|
|
49356
|
+
execute: async (args, context) => {
|
|
49357
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
49486
49358
|
const resolved = resolveRepository(args.repository, connector);
|
|
49487
49359
|
if (!resolved.success) {
|
|
49488
49360
|
return { success: false, error: resolved.error };
|
|
@@ -49490,7 +49362,7 @@ EXAMPLES:
|
|
|
49490
49362
|
const { owner, repo } = resolved.repo;
|
|
49491
49363
|
try {
|
|
49492
49364
|
const basePath = `/repos/${owner}/${repo}`;
|
|
49493
|
-
const queryOpts = { userId, queryParams: { per_page: 100 } };
|
|
49365
|
+
const queryOpts = { userId: effectiveUserId, queryParams: { per_page: 100 } };
|
|
49494
49366
|
const [reviewComments, reviews, issueComments] = await Promise.all([
|
|
49495
49367
|
githubFetch(
|
|
49496
49368
|
connector,
|
|
@@ -49617,7 +49489,8 @@ EXAMPLES:
|
|
|
49617
49489
|
riskLevel: "medium",
|
|
49618
49490
|
approvalMessage: `Create a pull request on GitHub via ${connector.displayName}`
|
|
49619
49491
|
},
|
|
49620
|
-
execute: async (args) => {
|
|
49492
|
+
execute: async (args, context) => {
|
|
49493
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
49621
49494
|
const resolved = resolveRepository(args.repository, connector);
|
|
49622
49495
|
if (!resolved.success) {
|
|
49623
49496
|
return { success: false, error: resolved.error };
|
|
@@ -49629,7 +49502,7 @@ EXAMPLES:
|
|
|
49629
49502
|
`/repos/${owner}/${repo}/pulls`,
|
|
49630
49503
|
{
|
|
49631
49504
|
method: "POST",
|
|
49632
|
-
userId,
|
|
49505
|
+
userId: effectiveUserId,
|
|
49633
49506
|
body: {
|
|
49634
49507
|
title: args.title,
|
|
49635
49508
|
body: args.body,
|
|
@@ -49767,37 +49640,6 @@ var toolRegistry = [
|
|
|
49767
49640
|
description: "Fetch and extract text content from a web page URL.",
|
|
49768
49641
|
tool: webFetch,
|
|
49769
49642
|
safeByDefault: true
|
|
49770
|
-
},
|
|
49771
|
-
{
|
|
49772
|
-
name: "web_fetch_js",
|
|
49773
|
-
exportName: "webFetchJS",
|
|
49774
|
-
displayName: "Web Fetch Js",
|
|
49775
|
-
category: "web",
|
|
49776
|
-
description: "Fetch and extract content from JavaScript-rendered websites using a headless browser (Puppeteer).",
|
|
49777
|
-
tool: webFetchJS,
|
|
49778
|
-
safeByDefault: true
|
|
49779
|
-
},
|
|
49780
|
-
{
|
|
49781
|
-
name: "web_scrape",
|
|
49782
|
-
exportName: "webScrape",
|
|
49783
|
-
displayName: "Web Scrape",
|
|
49784
|
-
category: "web",
|
|
49785
|
-
description: "Scrape any URL with automatic fallback - guaranteed to work on most sites.",
|
|
49786
|
-
tool: webScrape,
|
|
49787
|
-
safeByDefault: true,
|
|
49788
|
-
requiresConnector: true,
|
|
49789
|
-
connectorServiceTypes: ["zenrows"]
|
|
49790
|
-
},
|
|
49791
|
-
{
|
|
49792
|
-
name: "web_search",
|
|
49793
|
-
exportName: "webSearch",
|
|
49794
|
-
displayName: "Web Search",
|
|
49795
|
-
category: "web",
|
|
49796
|
-
description: "Search the web and get relevant results with snippets.",
|
|
49797
|
-
tool: webSearch,
|
|
49798
|
-
safeByDefault: true,
|
|
49799
|
-
requiresConnector: true,
|
|
49800
|
-
connectorServiceTypes: ["serper", "brave-search", "tavily", "rapidapi-websearch"]
|
|
49801
49643
|
}
|
|
49802
49644
|
];
|
|
49803
49645
|
function getAllBuiltInTools() {
|