@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.cjs
CHANGED
|
@@ -2034,7 +2034,14 @@ var init_Connector = __esm({
|
|
|
2034
2034
|
}
|
|
2035
2035
|
const startTime = Date.now();
|
|
2036
2036
|
this.requestCount++;
|
|
2037
|
-
|
|
2037
|
+
let url2;
|
|
2038
|
+
if (endpoint.startsWith("http")) {
|
|
2039
|
+
url2 = endpoint;
|
|
2040
|
+
} else {
|
|
2041
|
+
const base = (this.baseURL ?? "").replace(/\/+$/, "");
|
|
2042
|
+
const path6 = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
2043
|
+
url2 = `${base}${path6}`;
|
|
2044
|
+
}
|
|
2038
2045
|
const timeout = options?.timeout ?? this.config.timeout ?? exports.DEFAULT_CONNECTOR_TIMEOUT;
|
|
2039
2046
|
if (this.config.logging?.enabled) {
|
|
2040
2047
|
this.logRequest(url2, options);
|
|
@@ -18437,6 +18444,9 @@ var BasePluginNextGen = class {
|
|
|
18437
18444
|
}
|
|
18438
18445
|
};
|
|
18439
18446
|
|
|
18447
|
+
// src/core/context-nextgen/AgentContextNextGen.ts
|
|
18448
|
+
init_Connector();
|
|
18449
|
+
|
|
18440
18450
|
// src/domain/entities/Memory.ts
|
|
18441
18451
|
function isTaskAwareScope(scope) {
|
|
18442
18452
|
return typeof scope === "object" && scope !== null && "type" in scope;
|
|
@@ -19375,13 +19385,18 @@ Values are immediately visible - no retrieval needed.
|
|
|
19375
19385
|
- \`high\`: Keep longer. Important state.
|
|
19376
19386
|
- \`critical\`: Never auto-evicted.
|
|
19377
19387
|
|
|
19388
|
+
**UI Display:** Set \`showInUI: true\` in context_set to display the entry in the user's side panel.
|
|
19389
|
+
Values shown in the UI support the same rich markdown formatting as the chat window
|
|
19390
|
+
(see formatting instructions above). Use this for dashboards, progress displays, and results the user should see.
|
|
19391
|
+
|
|
19378
19392
|
**Tools:** context_set, context_delete, context_list`;
|
|
19379
19393
|
var contextSetDefinition = {
|
|
19380
19394
|
type: "function",
|
|
19381
19395
|
function: {
|
|
19382
19396
|
name: "context_set",
|
|
19383
19397
|
description: `Store or update a key-value pair in live context.
|
|
19384
|
-
Value appears directly in context - no retrieval needed
|
|
19398
|
+
Value appears directly in context - no retrieval needed.
|
|
19399
|
+
Set showInUI to true to also display the entry in the user's side panel.`,
|
|
19385
19400
|
parameters: {
|
|
19386
19401
|
type: "object",
|
|
19387
19402
|
properties: {
|
|
@@ -19392,6 +19407,10 @@ Value appears directly in context - no retrieval needed.`,
|
|
|
19392
19407
|
type: "string",
|
|
19393
19408
|
enum: ["low", "normal", "high", "critical"],
|
|
19394
19409
|
description: 'Eviction priority. Default: "normal"'
|
|
19410
|
+
},
|
|
19411
|
+
showInUI: {
|
|
19412
|
+
type: "boolean",
|
|
19413
|
+
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"
|
|
19395
19414
|
}
|
|
19396
19415
|
},
|
|
19397
19416
|
required: ["key", "description", "value"]
|
|
@@ -19432,6 +19451,7 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19432
19451
|
_destroyed = false;
|
|
19433
19452
|
_tokenCache = null;
|
|
19434
19453
|
_instructionsTokenCache = null;
|
|
19454
|
+
_notifyTimer = null;
|
|
19435
19455
|
constructor(config = {}) {
|
|
19436
19456
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
19437
19457
|
}
|
|
@@ -19472,15 +19492,18 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19472
19492
|
return a.updatedAt - b.updatedAt;
|
|
19473
19493
|
});
|
|
19474
19494
|
let freed = 0;
|
|
19495
|
+
let evicted = false;
|
|
19475
19496
|
for (const entry of evictable) {
|
|
19476
19497
|
if (freed >= targetTokensToFree) break;
|
|
19477
19498
|
const entryTokens = this.estimator.estimateTokens(this.formatEntry(entry));
|
|
19478
19499
|
this.entries.delete(entry.key);
|
|
19479
19500
|
freed += entryTokens;
|
|
19501
|
+
evicted = true;
|
|
19480
19502
|
}
|
|
19481
19503
|
this._tokenCache = null;
|
|
19482
19504
|
const content = await this.getContent();
|
|
19483
19505
|
const after = content ? this.estimator.estimateTokens(content) : 0;
|
|
19506
|
+
if (evicted) this.notifyEntriesChanged();
|
|
19484
19507
|
return Math.max(0, before - after);
|
|
19485
19508
|
}
|
|
19486
19509
|
getTools() {
|
|
@@ -19492,6 +19515,7 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19492
19515
|
}
|
|
19493
19516
|
destroy() {
|
|
19494
19517
|
if (this._destroyed) return;
|
|
19518
|
+
if (this._notifyTimer) clearTimeout(this._notifyTimer);
|
|
19495
19519
|
this.entries.clear();
|
|
19496
19520
|
this._destroyed = true;
|
|
19497
19521
|
this._tokenCache = null;
|
|
@@ -19509,6 +19533,7 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19509
19533
|
this.entries.set(entry.key, entry);
|
|
19510
19534
|
}
|
|
19511
19535
|
this._tokenCache = null;
|
|
19536
|
+
this.notifyEntriesChanged();
|
|
19512
19537
|
}
|
|
19513
19538
|
// ============================================================================
|
|
19514
19539
|
// Entry Management
|
|
@@ -19516,19 +19541,21 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19516
19541
|
/**
|
|
19517
19542
|
* Store or update a key-value pair
|
|
19518
19543
|
*/
|
|
19519
|
-
set(key, description, value, priority) {
|
|
19544
|
+
set(key, description, value, priority, showInUI) {
|
|
19520
19545
|
this.assertNotDestroyed();
|
|
19521
19546
|
const entry = {
|
|
19522
19547
|
key,
|
|
19523
19548
|
description,
|
|
19524
19549
|
value,
|
|
19525
19550
|
updatedAt: Date.now(),
|
|
19526
|
-
priority: priority ?? this.config.defaultPriority
|
|
19551
|
+
priority: priority ?? this.config.defaultPriority,
|
|
19552
|
+
showInUI: showInUI ?? false
|
|
19527
19553
|
};
|
|
19528
19554
|
this.entries.set(key, entry);
|
|
19529
19555
|
this.enforceMaxEntries();
|
|
19530
19556
|
this.enforceTokenLimit();
|
|
19531
19557
|
this._tokenCache = null;
|
|
19558
|
+
this.notifyEntriesChanged();
|
|
19532
19559
|
}
|
|
19533
19560
|
/**
|
|
19534
19561
|
* Get a value by key
|
|
@@ -19550,7 +19577,10 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19550
19577
|
delete(key) {
|
|
19551
19578
|
this.assertNotDestroyed();
|
|
19552
19579
|
const deleted = this.entries.delete(key);
|
|
19553
|
-
if (deleted)
|
|
19580
|
+
if (deleted) {
|
|
19581
|
+
this._tokenCache = null;
|
|
19582
|
+
this.notifyEntriesChanged();
|
|
19583
|
+
}
|
|
19554
19584
|
return deleted;
|
|
19555
19585
|
}
|
|
19556
19586
|
/**
|
|
@@ -19562,7 +19592,8 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19562
19592
|
key: e.key,
|
|
19563
19593
|
description: e.description,
|
|
19564
19594
|
priority: e.priority,
|
|
19565
|
-
updatedAt: e.updatedAt
|
|
19595
|
+
updatedAt: e.updatedAt,
|
|
19596
|
+
showInUI: e.showInUI ?? false
|
|
19566
19597
|
}));
|
|
19567
19598
|
}
|
|
19568
19599
|
/**
|
|
@@ -19572,6 +19603,7 @@ var InContextMemoryPluginNextGen = class {
|
|
|
19572
19603
|
this.assertNotDestroyed();
|
|
19573
19604
|
this.entries.clear();
|
|
19574
19605
|
this._tokenCache = null;
|
|
19606
|
+
this.notifyEntriesChanged();
|
|
19575
19607
|
}
|
|
19576
19608
|
// ============================================================================
|
|
19577
19609
|
// Private Helpers
|
|
@@ -19635,6 +19667,20 @@ ${valueStr}
|
|
|
19635
19667
|
return a.updatedAt - b.updatedAt;
|
|
19636
19668
|
});
|
|
19637
19669
|
}
|
|
19670
|
+
/**
|
|
19671
|
+
* Debounced notification when entries change.
|
|
19672
|
+
* Calls config.onEntriesChanged with all current entries.
|
|
19673
|
+
*/
|
|
19674
|
+
notifyEntriesChanged() {
|
|
19675
|
+
if (!this.config.onEntriesChanged) return;
|
|
19676
|
+
if (this._notifyTimer) clearTimeout(this._notifyTimer);
|
|
19677
|
+
this._notifyTimer = setTimeout(() => {
|
|
19678
|
+
this._notifyTimer = null;
|
|
19679
|
+
if (!this._destroyed && this.config.onEntriesChanged) {
|
|
19680
|
+
this.config.onEntriesChanged(Array.from(this.entries.values()));
|
|
19681
|
+
}
|
|
19682
|
+
}, 100);
|
|
19683
|
+
}
|
|
19638
19684
|
assertNotDestroyed() {
|
|
19639
19685
|
if (this._destroyed) {
|
|
19640
19686
|
throw new Error("InContextMemoryPluginNextGen is destroyed");
|
|
@@ -19651,16 +19697,18 @@ ${valueStr}
|
|
|
19651
19697
|
args.key,
|
|
19652
19698
|
args.description,
|
|
19653
19699
|
args.value,
|
|
19654
|
-
args.priority
|
|
19700
|
+
args.priority,
|
|
19701
|
+
args.showInUI
|
|
19655
19702
|
);
|
|
19656
19703
|
return {
|
|
19657
19704
|
success: true,
|
|
19658
19705
|
key: args.key,
|
|
19659
|
-
|
|
19706
|
+
showInUI: args.showInUI ?? false,
|
|
19707
|
+
message: `Stored "${args.key}" in live context${args.showInUI ? " (visible in UI)" : ""}`
|
|
19660
19708
|
};
|
|
19661
19709
|
},
|
|
19662
19710
|
permission: { scope: "always", riskLevel: "low" },
|
|
19663
|
-
describeCall: (args) => `set ${args.key}`
|
|
19711
|
+
describeCall: (args) => `set ${args.key}${args.showInUI ? " [UI]" : ""}`
|
|
19664
19712
|
};
|
|
19665
19713
|
}
|
|
19666
19714
|
createContextDeleteTool() {
|
|
@@ -20972,6 +21020,10 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
|
|
|
20972
21020
|
_sessionId = null;
|
|
20973
21021
|
/** Agent ID */
|
|
20974
21022
|
_agentId;
|
|
21023
|
+
/** User ID for multi-user scenarios */
|
|
21024
|
+
_userId;
|
|
21025
|
+
/** Allowed connector names (when agent is restricted to a subset) */
|
|
21026
|
+
_allowedConnectors;
|
|
20975
21027
|
/** Storage backend */
|
|
20976
21028
|
_storage;
|
|
20977
21029
|
/** Destroyed flag */
|
|
@@ -21008,6 +21060,8 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
|
|
|
21008
21060
|
};
|
|
21009
21061
|
this._systemPrompt = config.systemPrompt;
|
|
21010
21062
|
this._agentId = this._config.agentId;
|
|
21063
|
+
this._userId = config.userId;
|
|
21064
|
+
this._allowedConnectors = config.connectors;
|
|
21011
21065
|
this._storage = config.storage;
|
|
21012
21066
|
this._compactionStrategy = config.compactionStrategy ?? StrategyRegistry.create(this._config.strategy);
|
|
21013
21067
|
this._tools = new ToolManager(
|
|
@@ -21019,6 +21073,7 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
|
|
|
21019
21073
|
}
|
|
21020
21074
|
}
|
|
21021
21075
|
this.initializePlugins(config.plugins);
|
|
21076
|
+
this.syncToolContext();
|
|
21022
21077
|
}
|
|
21023
21078
|
/**
|
|
21024
21079
|
* Initialize plugins based on feature flags.
|
|
@@ -21063,6 +21118,62 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
|
|
|
21063
21118
|
);
|
|
21064
21119
|
}
|
|
21065
21120
|
}
|
|
21121
|
+
/**
|
|
21122
|
+
* Sync identity fields and connector registry to ToolContext.
|
|
21123
|
+
* Merges with existing ToolContext to preserve other fields (memory, signal, taskId).
|
|
21124
|
+
*
|
|
21125
|
+
* Connector registry resolution order:
|
|
21126
|
+
* 1. If `connectors` (allowed names) is set → filtered view of global registry
|
|
21127
|
+
* 2. If access policy + userId → scoped view via Connector.scoped()
|
|
21128
|
+
* 3. Otherwise → full global registry
|
|
21129
|
+
*/
|
|
21130
|
+
syncToolContext() {
|
|
21131
|
+
const existing = this._tools.getToolContext();
|
|
21132
|
+
this._tools.setToolContext({
|
|
21133
|
+
...existing,
|
|
21134
|
+
agentId: this._agentId,
|
|
21135
|
+
userId: this._userId,
|
|
21136
|
+
connectorRegistry: this.buildConnectorRegistry()
|
|
21137
|
+
});
|
|
21138
|
+
}
|
|
21139
|
+
/**
|
|
21140
|
+
* Build the connector registry appropriate for this agent's config.
|
|
21141
|
+
*/
|
|
21142
|
+
buildConnectorRegistry() {
|
|
21143
|
+
if (this._allowedConnectors?.length) {
|
|
21144
|
+
const allowedSet = new Set(this._allowedConnectors);
|
|
21145
|
+
const base = this._userId && exports.Connector.getAccessPolicy() ? exports.Connector.scoped({ userId: this._userId }) : exports.Connector.asRegistry();
|
|
21146
|
+
return {
|
|
21147
|
+
get: (name) => {
|
|
21148
|
+
if (!allowedSet.has(name)) {
|
|
21149
|
+
const available = this._allowedConnectors.filter((n) => base.has(n)).join(", ") || "none";
|
|
21150
|
+
throw new Error(`Connector '${name}' not found. Available: ${available}`);
|
|
21151
|
+
}
|
|
21152
|
+
return base.get(name);
|
|
21153
|
+
},
|
|
21154
|
+
has: (name) => allowedSet.has(name) && base.has(name),
|
|
21155
|
+
list: () => base.list().filter((n) => allowedSet.has(n)),
|
|
21156
|
+
listAll: () => base.listAll().filter((c) => allowedSet.has(c.name)),
|
|
21157
|
+
size: () => base.listAll().filter((c) => allowedSet.has(c.name)).length,
|
|
21158
|
+
getDescriptionsForTools: () => {
|
|
21159
|
+
const connectors = base.listAll().filter((c) => allowedSet.has(c.name));
|
|
21160
|
+
if (connectors.length === 0) return "No connectors registered yet.";
|
|
21161
|
+
return connectors.map((c) => ` - "${c.name}": ${c.displayName} - ${c.config.description || "No description"}`).join("\n");
|
|
21162
|
+
},
|
|
21163
|
+
getInfo: () => {
|
|
21164
|
+
const info = {};
|
|
21165
|
+
for (const c of base.listAll().filter((c2) => allowedSet.has(c2.name))) {
|
|
21166
|
+
info[c.name] = { displayName: c.displayName, description: c.config.description || "", baseURL: c.baseURL };
|
|
21167
|
+
}
|
|
21168
|
+
return info;
|
|
21169
|
+
}
|
|
21170
|
+
};
|
|
21171
|
+
}
|
|
21172
|
+
if (this._userId && exports.Connector.getAccessPolicy()) {
|
|
21173
|
+
return exports.Connector.scoped({ userId: this._userId });
|
|
21174
|
+
}
|
|
21175
|
+
return exports.Connector.asRegistry();
|
|
21176
|
+
}
|
|
21066
21177
|
// ============================================================================
|
|
21067
21178
|
// Public Properties
|
|
21068
21179
|
// ============================================================================
|
|
@@ -21078,6 +21189,24 @@ var AgentContextNextGen = class _AgentContextNextGen extends eventemitter3.Event
|
|
|
21078
21189
|
get agentId() {
|
|
21079
21190
|
return this._agentId;
|
|
21080
21191
|
}
|
|
21192
|
+
/** Get the current user ID */
|
|
21193
|
+
get userId() {
|
|
21194
|
+
return this._userId;
|
|
21195
|
+
}
|
|
21196
|
+
/** Set user ID. Automatically updates ToolContext for all tool executions. */
|
|
21197
|
+
set userId(value) {
|
|
21198
|
+
this._userId = value;
|
|
21199
|
+
this.syncToolContext();
|
|
21200
|
+
}
|
|
21201
|
+
/** Get the allowed connector names (undefined = all visible connectors) */
|
|
21202
|
+
get connectors() {
|
|
21203
|
+
return this._allowedConnectors;
|
|
21204
|
+
}
|
|
21205
|
+
/** Set allowed connector names. Updates ToolContext.connectorRegistry. */
|
|
21206
|
+
set connectors(value) {
|
|
21207
|
+
this._allowedConnectors = value;
|
|
21208
|
+
this.syncToolContext();
|
|
21209
|
+
}
|
|
21081
21210
|
/** Get/set system prompt */
|
|
21082
21211
|
get systemPrompt() {
|
|
21083
21212
|
return this._systemPrompt;
|
|
@@ -21991,6 +22120,7 @@ ${content}`);
|
|
|
21991
22120
|
metadata: {
|
|
21992
22121
|
savedAt: Date.now(),
|
|
21993
22122
|
agentId: this._agentId,
|
|
22123
|
+
userId: this._userId,
|
|
21994
22124
|
model: this._config.model
|
|
21995
22125
|
}
|
|
21996
22126
|
};
|
|
@@ -24953,6 +25083,8 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
|
|
|
24953
25083
|
const contextConfig = {
|
|
24954
25084
|
model: config.model,
|
|
24955
25085
|
agentId: config.name,
|
|
25086
|
+
userId: config.userId,
|
|
25087
|
+
connectors: config.connectors,
|
|
24956
25088
|
// Include storage and sessionId if session config is provided
|
|
24957
25089
|
storage: config.session?.storage,
|
|
24958
25090
|
// Thread tool execution timeout to ToolManager
|
|
@@ -25109,6 +25241,30 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
|
|
|
25109
25241
|
get context() {
|
|
25110
25242
|
return this._agentContext;
|
|
25111
25243
|
}
|
|
25244
|
+
/**
|
|
25245
|
+
* Get the current user ID. Delegates to AgentContextNextGen.
|
|
25246
|
+
*/
|
|
25247
|
+
get userId() {
|
|
25248
|
+
return this._agentContext.userId;
|
|
25249
|
+
}
|
|
25250
|
+
/**
|
|
25251
|
+
* Set user ID at runtime. Automatically updates ToolContext for all tool executions.
|
|
25252
|
+
*/
|
|
25253
|
+
set userId(value) {
|
|
25254
|
+
this._agentContext.userId = value;
|
|
25255
|
+
}
|
|
25256
|
+
/**
|
|
25257
|
+
* Get the allowed connector names (undefined = all visible connectors).
|
|
25258
|
+
*/
|
|
25259
|
+
get connectors() {
|
|
25260
|
+
return this._agentContext.connectors;
|
|
25261
|
+
}
|
|
25262
|
+
/**
|
|
25263
|
+
* Restrict this agent to a subset of connectors. Updates ToolContext.connectorRegistry.
|
|
25264
|
+
*/
|
|
25265
|
+
set connectors(value) {
|
|
25266
|
+
this._agentContext.connectors = value;
|
|
25267
|
+
}
|
|
25112
25268
|
/**
|
|
25113
25269
|
* Permission management. Returns ToolPermissionManager for approval control.
|
|
25114
25270
|
*/
|
|
@@ -25160,9 +25316,10 @@ var BaseAgent = class extends eventemitter3.EventEmitter {
|
|
|
25160
25316
|
* always sees up-to-date tool descriptions.
|
|
25161
25317
|
*/
|
|
25162
25318
|
getEnabledToolDefinitions() {
|
|
25319
|
+
const toolContext = this._agentContext.tools.getToolContext();
|
|
25163
25320
|
return this._agentContext.tools.getEnabled().map((tool) => {
|
|
25164
25321
|
if (tool.descriptionFactory) {
|
|
25165
|
-
const dynamicDescription = tool.descriptionFactory();
|
|
25322
|
+
const dynamicDescription = tool.descriptionFactory(toolContext);
|
|
25166
25323
|
return {
|
|
25167
25324
|
...tool.definition,
|
|
25168
25325
|
function: {
|
|
@@ -26208,6 +26365,7 @@ var Agent = class _Agent extends BaseAgent {
|
|
|
26208
26365
|
* const agent = Agent.create({
|
|
26209
26366
|
* connector: 'openai', // or Connector instance
|
|
26210
26367
|
* model: 'gpt-4',
|
|
26368
|
+
* userId: 'user-123', // flows to all tool executions automatically
|
|
26211
26369
|
* instructions: 'You are a helpful assistant',
|
|
26212
26370
|
* tools: [myTool]
|
|
26213
26371
|
* });
|
|
@@ -41785,6 +41943,32 @@ function filterProtectedHeaders(headers) {
|
|
|
41785
41943
|
}
|
|
41786
41944
|
return filtered;
|
|
41787
41945
|
}
|
|
41946
|
+
function normalizeBody(body) {
|
|
41947
|
+
if (typeof body === "string") {
|
|
41948
|
+
try {
|
|
41949
|
+
return JSON.parse(body);
|
|
41950
|
+
} catch {
|
|
41951
|
+
return body;
|
|
41952
|
+
}
|
|
41953
|
+
}
|
|
41954
|
+
return body;
|
|
41955
|
+
}
|
|
41956
|
+
function detectAPIError(data) {
|
|
41957
|
+
if (!data || typeof data !== "object") return null;
|
|
41958
|
+
const obj = data;
|
|
41959
|
+
if (obj.ok === false && typeof obj.error === "string") {
|
|
41960
|
+
return obj.error;
|
|
41961
|
+
}
|
|
41962
|
+
if (obj.success === false) {
|
|
41963
|
+
if (typeof obj.error === "string") return obj.error;
|
|
41964
|
+
if (typeof obj.message === "string") return obj.message;
|
|
41965
|
+
}
|
|
41966
|
+
if (obj.error && typeof obj.error === "object") {
|
|
41967
|
+
const err = obj.error;
|
|
41968
|
+
if (typeof err.message === "string") return err.message;
|
|
41969
|
+
}
|
|
41970
|
+
return null;
|
|
41971
|
+
}
|
|
41788
41972
|
var ConnectorTools = class {
|
|
41789
41973
|
/** Registry of service-specific tool factories */
|
|
41790
41974
|
static factories = /* @__PURE__ */ new Map();
|
|
@@ -42038,7 +42222,7 @@ var ConnectorTools = class {
|
|
|
42038
42222
|
},
|
|
42039
42223
|
body: {
|
|
42040
42224
|
type: "object",
|
|
42041
|
-
description: 'JSON request body for POST/PUT/PATCH requests.
|
|
42225
|
+
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.'
|
|
42042
42226
|
},
|
|
42043
42227
|
queryParams: {
|
|
42044
42228
|
type: "object",
|
|
@@ -42053,7 +42237,8 @@ var ConnectorTools = class {
|
|
|
42053
42237
|
}
|
|
42054
42238
|
}
|
|
42055
42239
|
},
|
|
42056
|
-
execute: async (args) => {
|
|
42240
|
+
execute: async (args, context) => {
|
|
42241
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
42057
42242
|
let url2 = args.endpoint;
|
|
42058
42243
|
if (args.queryParams && Object.keys(args.queryParams).length > 0) {
|
|
42059
42244
|
const params = new URLSearchParams();
|
|
@@ -42066,7 +42251,8 @@ var ConnectorTools = class {
|
|
|
42066
42251
|
let bodyStr;
|
|
42067
42252
|
if (args.body) {
|
|
42068
42253
|
try {
|
|
42069
|
-
|
|
42254
|
+
const normalized = normalizeBody(args.body);
|
|
42255
|
+
bodyStr = safeStringify2(normalized);
|
|
42070
42256
|
} catch (e) {
|
|
42071
42257
|
return {
|
|
42072
42258
|
success: false,
|
|
@@ -42085,7 +42271,7 @@ var ConnectorTools = class {
|
|
|
42085
42271
|
},
|
|
42086
42272
|
body: bodyStr
|
|
42087
42273
|
},
|
|
42088
|
-
|
|
42274
|
+
effectiveUserId
|
|
42089
42275
|
);
|
|
42090
42276
|
const text = await response.text();
|
|
42091
42277
|
let data;
|
|
@@ -42094,11 +42280,12 @@ var ConnectorTools = class {
|
|
|
42094
42280
|
} catch {
|
|
42095
42281
|
data = text;
|
|
42096
42282
|
}
|
|
42283
|
+
const apiError = detectAPIError(data);
|
|
42097
42284
|
return {
|
|
42098
|
-
success: response.ok,
|
|
42285
|
+
success: response.ok && !apiError,
|
|
42099
42286
|
status: response.status,
|
|
42100
|
-
data: response.ok ? data : void 0,
|
|
42101
|
-
error: response.ok ? void 0 : typeof data === "string" ? data : safeStringify2(data)
|
|
42287
|
+
data: response.ok && !apiError ? data : void 0,
|
|
42288
|
+
error: apiError ? apiError : response.ok ? void 0 : typeof data === "string" ? data : safeStringify2(data)
|
|
42102
42289
|
};
|
|
42103
42290
|
} catch (error) {
|
|
42104
42291
|
return {
|
|
@@ -45321,6 +45508,8 @@ __export(tools_exports, {
|
|
|
45321
45508
|
createSpeechToTextTool: () => createSpeechToTextTool,
|
|
45322
45509
|
createTextToSpeechTool: () => createTextToSpeechTool,
|
|
45323
45510
|
createVideoTools: () => createVideoTools,
|
|
45511
|
+
createWebScrapeTool: () => createWebScrapeTool,
|
|
45512
|
+
createWebSearchTool: () => createWebSearchTool,
|
|
45324
45513
|
createWriteFileTool: () => createWriteFileTool,
|
|
45325
45514
|
developerTools: () => developerTools,
|
|
45326
45515
|
editFile: () => editFile,
|
|
@@ -45350,9 +45539,6 @@ __export(tools_exports, {
|
|
|
45350
45539
|
toolRegistry: () => toolRegistry,
|
|
45351
45540
|
validatePath: () => validatePath,
|
|
45352
45541
|
webFetch: () => webFetch,
|
|
45353
|
-
webFetchJS: () => webFetchJS,
|
|
45354
|
-
webScrape: () => webScrape,
|
|
45355
|
-
webSearch: () => webSearch,
|
|
45356
45542
|
writeFile: () => writeFile5
|
|
45357
45543
|
});
|
|
45358
45544
|
var DEFAULT_FILESYSTEM_CONFIG = {
|
|
@@ -47075,6 +47261,111 @@ The tool returns a result object with:
|
|
|
47075
47261
|
}
|
|
47076
47262
|
};
|
|
47077
47263
|
|
|
47264
|
+
// src/tools/web/createWebSearchTool.ts
|
|
47265
|
+
init_Logger();
|
|
47266
|
+
var searchLogger = exports.logger.child({ component: "webSearch" });
|
|
47267
|
+
function createWebSearchTool(connector) {
|
|
47268
|
+
return {
|
|
47269
|
+
definition: {
|
|
47270
|
+
type: "function",
|
|
47271
|
+
function: {
|
|
47272
|
+
name: "web_search",
|
|
47273
|
+
description: `Search the web and get relevant results with snippets.
|
|
47274
|
+
|
|
47275
|
+
RETURNS:
|
|
47276
|
+
An array of search results, each containing:
|
|
47277
|
+
- title: Page title
|
|
47278
|
+
- url: Direct URL to the page
|
|
47279
|
+
- snippet: Short description/excerpt from the page
|
|
47280
|
+
- position: Search ranking position (1, 2, 3...)
|
|
47281
|
+
|
|
47282
|
+
USE CASES:
|
|
47283
|
+
- Find current information on any topic
|
|
47284
|
+
- Research multiple sources
|
|
47285
|
+
- Discover relevant websites
|
|
47286
|
+
- Find URLs to fetch with web_fetch tool
|
|
47287
|
+
|
|
47288
|
+
WORKFLOW PATTERN:
|
|
47289
|
+
1. Use web_search to find relevant URLs
|
|
47290
|
+
2. Use web_fetch to get full content from top results
|
|
47291
|
+
3. Process and summarize the information
|
|
47292
|
+
|
|
47293
|
+
EXAMPLE:
|
|
47294
|
+
{
|
|
47295
|
+
"query": "latest AI developments 2026",
|
|
47296
|
+
"numResults": 5
|
|
47297
|
+
}`,
|
|
47298
|
+
parameters: {
|
|
47299
|
+
type: "object",
|
|
47300
|
+
properties: {
|
|
47301
|
+
query: {
|
|
47302
|
+
type: "string",
|
|
47303
|
+
description: "The search query string. Be specific for better results."
|
|
47304
|
+
},
|
|
47305
|
+
numResults: {
|
|
47306
|
+
type: "number",
|
|
47307
|
+
description: "Number of results to return (default: 10, max: 100)."
|
|
47308
|
+
},
|
|
47309
|
+
country: {
|
|
47310
|
+
type: "string",
|
|
47311
|
+
description: 'Country/region code for localized results (e.g., "us", "gb", "de")'
|
|
47312
|
+
},
|
|
47313
|
+
language: {
|
|
47314
|
+
type: "string",
|
|
47315
|
+
description: 'Language code for results (e.g., "en", "fr", "de")'
|
|
47316
|
+
}
|
|
47317
|
+
},
|
|
47318
|
+
required: ["query"]
|
|
47319
|
+
}
|
|
47320
|
+
},
|
|
47321
|
+
blocking: true,
|
|
47322
|
+
timeout: 15e3
|
|
47323
|
+
},
|
|
47324
|
+
execute: async (args) => {
|
|
47325
|
+
const numResults = args.numResults || 10;
|
|
47326
|
+
searchLogger.debug({ connectorName: connector.name }, "Executing search with connector");
|
|
47327
|
+
try {
|
|
47328
|
+
const searchProvider = SearchProvider.create({ connector: connector.name });
|
|
47329
|
+
const response = await searchProvider.search(args.query, {
|
|
47330
|
+
numResults,
|
|
47331
|
+
country: args.country,
|
|
47332
|
+
language: args.language
|
|
47333
|
+
});
|
|
47334
|
+
if (response.success) {
|
|
47335
|
+
searchLogger.debug({
|
|
47336
|
+
provider: response.provider,
|
|
47337
|
+
count: response.count
|
|
47338
|
+
}, "Search completed successfully");
|
|
47339
|
+
} else {
|
|
47340
|
+
searchLogger.warn({
|
|
47341
|
+
provider: response.provider,
|
|
47342
|
+
error: response.error
|
|
47343
|
+
}, "Search failed");
|
|
47344
|
+
}
|
|
47345
|
+
return {
|
|
47346
|
+
success: response.success,
|
|
47347
|
+
query: response.query,
|
|
47348
|
+
provider: response.provider,
|
|
47349
|
+
results: response.results,
|
|
47350
|
+
count: response.count,
|
|
47351
|
+
error: response.error
|
|
47352
|
+
};
|
|
47353
|
+
} catch (error) {
|
|
47354
|
+
searchLogger.error({ error: error.message, connectorName: connector.name }, "Search threw exception");
|
|
47355
|
+
return {
|
|
47356
|
+
success: false,
|
|
47357
|
+
query: args.query,
|
|
47358
|
+
provider: connector.name,
|
|
47359
|
+
results: [],
|
|
47360
|
+
count: 0,
|
|
47361
|
+
error: error.message || "Unknown error"
|
|
47362
|
+
};
|
|
47363
|
+
}
|
|
47364
|
+
},
|
|
47365
|
+
describeCall: (args) => `"${args.query}"${args.numResults ? ` (${args.numResults} results)` : ""}`
|
|
47366
|
+
};
|
|
47367
|
+
}
|
|
47368
|
+
|
|
47078
47369
|
// src/tools/web/contentDetector.ts
|
|
47079
47370
|
function detectContentQuality(html, text, $) {
|
|
47080
47371
|
const issues = [];
|
|
@@ -47149,7 +47440,7 @@ function detectContentQuality(html, text, $) {
|
|
|
47149
47440
|
}
|
|
47150
47441
|
let suggestion;
|
|
47151
47442
|
if (requiresJS && score < 50) {
|
|
47152
|
-
suggestion = "Content quality is low. This appears to be a JavaScript-rendered site. Use
|
|
47443
|
+
suggestion = "Content quality is low. This appears to be a JavaScript-rendered site. Use a scraping service connector for better results.";
|
|
47153
47444
|
} else if (score < 30) {
|
|
47154
47445
|
suggestion = "Content extraction failed or page has errors. Check the URL and try again.";
|
|
47155
47446
|
}
|
|
@@ -47258,7 +47549,7 @@ The tool analyzes the fetched content and returns a quality score (0-100):
|
|
|
47258
47549
|
- 50-79: Moderate quality, some content extracted
|
|
47259
47550
|
- 0-49: Low quality, likely needs JavaScript or has errors
|
|
47260
47551
|
|
|
47261
|
-
If the quality score is low or requiresJS is true,
|
|
47552
|
+
If the quality score is low or requiresJS is true, consider using a scraping service connector for better results.
|
|
47262
47553
|
|
|
47263
47554
|
RETURNS:
|
|
47264
47555
|
{
|
|
@@ -47422,460 +47713,109 @@ With custom user agent:
|
|
|
47422
47713
|
}
|
|
47423
47714
|
};
|
|
47424
47715
|
|
|
47425
|
-
// src/tools/web/
|
|
47426
|
-
|
|
47427
|
-
var
|
|
47428
|
-
|
|
47429
|
-
|
|
47430
|
-
|
|
47431
|
-
|
|
47432
|
-
|
|
47433
|
-
|
|
47434
|
-
|
|
47435
|
-
}
|
|
47436
|
-
return puppeteerModule;
|
|
47437
|
-
}
|
|
47438
|
-
async function getBrowser() {
|
|
47439
|
-
if (!browserInstance) {
|
|
47440
|
-
const puppeteer = await loadPuppeteer();
|
|
47441
|
-
browserInstance = await puppeteer.launch({
|
|
47442
|
-
headless: true,
|
|
47443
|
-
args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"]
|
|
47444
|
-
});
|
|
47445
|
-
process.on("exit", async () => {
|
|
47446
|
-
if (browserInstance) {
|
|
47447
|
-
await browserInstance.close();
|
|
47448
|
-
}
|
|
47449
|
-
});
|
|
47450
|
-
}
|
|
47451
|
-
return browserInstance;
|
|
47452
|
-
}
|
|
47453
|
-
var webFetchJS = {
|
|
47454
|
-
definition: {
|
|
47455
|
-
type: "function",
|
|
47456
|
-
function: {
|
|
47457
|
-
name: "web_fetch_js",
|
|
47458
|
-
description: `Fetch and extract content from JavaScript-rendered websites using a headless browser (Puppeteer).
|
|
47459
|
-
|
|
47460
|
-
USE THIS TOOL WHEN:
|
|
47461
|
-
- The web_fetch tool returned a low quality score (<50)
|
|
47462
|
-
- The web_fetch tool suggested using JavaScript rendering
|
|
47463
|
-
- You know the website is built with React/Vue/Angular/Next.js
|
|
47464
|
-
- Content loads dynamically via JavaScript
|
|
47465
|
-
- The page requires interaction (though this tool doesn't support interaction yet)
|
|
47466
|
-
|
|
47467
|
-
HOW IT WORKS:
|
|
47468
|
-
- Launches a headless Chrome browser
|
|
47469
|
-
- Navigates to the URL
|
|
47470
|
-
- Waits for JavaScript to execute and content to load
|
|
47471
|
-
- Extracts the rendered HTML and text content
|
|
47472
|
-
- Optionally captures a screenshot
|
|
47473
|
-
|
|
47474
|
-
CAPABILITIES:
|
|
47475
|
-
- Executes all JavaScript on the page
|
|
47476
|
-
- Waits for network to be idle (all resources loaded)
|
|
47477
|
-
- Can wait for specific CSS selectors to appear
|
|
47478
|
-
- Handles React, Vue, Angular, Next.js, and other SPAs
|
|
47479
|
-
- Returns content after full JavaScript execution
|
|
47480
|
-
|
|
47481
|
-
LIMITATIONS:
|
|
47482
|
-
- Slower than web_fetch (typically 3-10 seconds vs <1 second)
|
|
47483
|
-
- Uses more system resources (runs a full browser)
|
|
47484
|
-
- May still fail on sites with aggressive bot detection
|
|
47485
|
-
- Requires puppeteer to be installed (npm install puppeteer)
|
|
47486
|
-
|
|
47487
|
-
PERFORMANCE:
|
|
47488
|
-
- First call: Slower (launches browser ~1-2s)
|
|
47489
|
-
- Subsequent calls: Faster (reuses browser instance)
|
|
47490
|
-
|
|
47491
|
-
RETURNS:
|
|
47492
|
-
{
|
|
47493
|
-
success: boolean,
|
|
47494
|
-
url: string,
|
|
47495
|
-
title: string,
|
|
47496
|
-
content: string, // Clean markdown (converted via Readability + Turndown)
|
|
47497
|
-
screenshot: string, // Base64 PNG screenshot (if requested)
|
|
47498
|
-
loadTime: number, // Time taken in milliseconds
|
|
47499
|
-
excerpt: string, // Short summary excerpt (if extracted)
|
|
47500
|
-
byline: string, // Author info (if extracted)
|
|
47501
|
-
wasTruncated: boolean, // True if content was truncated
|
|
47502
|
-
error: string // Error message if failed
|
|
47503
|
-
}
|
|
47504
|
-
|
|
47505
|
-
EXAMPLES:
|
|
47506
|
-
Basic usage:
|
|
47507
|
-
{
|
|
47508
|
-
url: "https://react-app.com/page"
|
|
47509
|
-
}
|
|
47510
|
-
|
|
47511
|
-
Wait for specific content:
|
|
47512
|
-
{
|
|
47513
|
-
url: "https://app.com/dashboard",
|
|
47514
|
-
waitForSelector: "#main-content", // Wait for this element
|
|
47515
|
-
timeout: 20000
|
|
47716
|
+
// src/tools/web/createWebScrapeTool.ts
|
|
47717
|
+
init_Logger();
|
|
47718
|
+
var scrapeLogger = exports.logger.child({ component: "webScrape" });
|
|
47719
|
+
var DEFAULT_MIN_QUALITY = 50;
|
|
47720
|
+
function stripBase64DataUris(content) {
|
|
47721
|
+
if (!content) return content;
|
|
47722
|
+
let cleaned = content.replace(/!\[[^\]]*\]\(data:[^)]+\)/g, "[image removed]");
|
|
47723
|
+
cleaned = cleaned.replace(/url\(['"]?data:[^)]+['"]?\)/gi, "url([data-uri-removed])");
|
|
47724
|
+
cleaned = cleaned.replace(/data:(?:image|font|application)\/[^;]+;base64,[A-Za-z0-9+/=]{100,}/g, "[base64-data-removed]");
|
|
47725
|
+
return cleaned;
|
|
47516
47726
|
}
|
|
47517
|
-
|
|
47518
|
-
|
|
47519
|
-
|
|
47520
|
-
|
|
47521
|
-
takeScreenshot: true
|
|
47522
|
-
}`,
|
|
47523
|
-
parameters: {
|
|
47524
|
-
type: "object",
|
|
47525
|
-
properties: {
|
|
47526
|
-
url: {
|
|
47527
|
-
type: "string",
|
|
47528
|
-
description: "The URL to fetch. Must start with http:// or https://"
|
|
47529
|
-
},
|
|
47530
|
-
waitForSelector: {
|
|
47531
|
-
type: "string",
|
|
47532
|
-
description: 'Optional CSS selector to wait for before extracting content. Example: "#main-content" or ".article-body"'
|
|
47533
|
-
},
|
|
47534
|
-
timeout: {
|
|
47535
|
-
type: "number",
|
|
47536
|
-
description: "Max wait time in milliseconds (default: 15000)"
|
|
47537
|
-
},
|
|
47538
|
-
takeScreenshot: {
|
|
47539
|
-
type: "boolean",
|
|
47540
|
-
description: "Whether to capture a screenshot of the page (default: false). Screenshot returned as base64 PNG."
|
|
47541
|
-
}
|
|
47542
|
-
},
|
|
47543
|
-
required: ["url"]
|
|
47544
|
-
}
|
|
47545
|
-
},
|
|
47546
|
-
blocking: true,
|
|
47547
|
-
timeout: 3e4
|
|
47548
|
-
// Allow extra time for browser operations
|
|
47549
|
-
},
|
|
47550
|
-
execute: async (args) => {
|
|
47551
|
-
let page = null;
|
|
47727
|
+
function createWebScrapeTool(connector) {
|
|
47728
|
+
async function tryNative(args, startTime, attemptedMethods) {
|
|
47729
|
+
attemptedMethods.push("native");
|
|
47730
|
+
scrapeLogger.debug({ url: args.url }, "Trying native fetch");
|
|
47552
47731
|
try {
|
|
47553
|
-
const
|
|
47554
|
-
|
|
47555
|
-
|
|
47556
|
-
await page.setUserAgent(
|
|
47557
|
-
"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"
|
|
47558
|
-
);
|
|
47559
|
-
const startTime = Date.now();
|
|
47560
|
-
await page.goto(args.url, {
|
|
47561
|
-
waitUntil: "networkidle2",
|
|
47562
|
-
// Wait until network is mostly idle
|
|
47563
|
-
timeout: args.timeout || 15e3
|
|
47732
|
+
const result = await webFetch.execute({
|
|
47733
|
+
url: args.url,
|
|
47734
|
+
timeout: args.timeout || 1e4
|
|
47564
47735
|
});
|
|
47565
|
-
|
|
47566
|
-
await page.waitForSelector(args.waitForSelector, {
|
|
47567
|
-
timeout: args.timeout || 15e3
|
|
47568
|
-
});
|
|
47569
|
-
}
|
|
47570
|
-
const html = await page.content();
|
|
47571
|
-
const browserTitle = await page.title();
|
|
47572
|
-
const loadTime = Date.now() - startTime;
|
|
47573
|
-
let screenshot;
|
|
47574
|
-
if (args.takeScreenshot) {
|
|
47575
|
-
const buffer = await page.screenshot({
|
|
47576
|
-
type: "png",
|
|
47577
|
-
fullPage: false
|
|
47578
|
-
// Just viewport
|
|
47579
|
-
});
|
|
47580
|
-
screenshot = buffer.toString("base64");
|
|
47581
|
-
}
|
|
47582
|
-
await page.close();
|
|
47583
|
-
const mdResult = await htmlToMarkdown(html, args.url);
|
|
47584
|
-
const title = browserTitle || mdResult.title || "Untitled";
|
|
47736
|
+
const cleanContent = stripBase64DataUris(result.content);
|
|
47585
47737
|
return {
|
|
47586
|
-
success:
|
|
47738
|
+
success: result.success,
|
|
47587
47739
|
url: args.url,
|
|
47588
|
-
|
|
47589
|
-
|
|
47590
|
-
|
|
47591
|
-
|
|
47592
|
-
|
|
47593
|
-
|
|
47594
|
-
|
|
47595
|
-
|
|
47740
|
+
finalUrl: args.url,
|
|
47741
|
+
method: "native",
|
|
47742
|
+
title: result.title,
|
|
47743
|
+
content: cleanContent,
|
|
47744
|
+
qualityScore: result.qualityScore,
|
|
47745
|
+
durationMs: Date.now() - startTime,
|
|
47746
|
+
attemptedMethods,
|
|
47747
|
+
error: result.error
|
|
47596
47748
|
};
|
|
47597
47749
|
} catch (error) {
|
|
47598
|
-
if (page) {
|
|
47599
|
-
try {
|
|
47600
|
-
await page.close();
|
|
47601
|
-
} catch {
|
|
47602
|
-
}
|
|
47603
|
-
}
|
|
47604
|
-
if (error.message === "Puppeteer not installed") {
|
|
47605
|
-
return {
|
|
47606
|
-
success: false,
|
|
47607
|
-
url: args.url,
|
|
47608
|
-
title: "",
|
|
47609
|
-
content: "",
|
|
47610
|
-
loadTime: 0,
|
|
47611
|
-
error: "Puppeteer is not installed",
|
|
47612
|
-
suggestion: "Install Puppeteer with: npm install puppeteer (note: downloads ~50MB Chrome binary)"
|
|
47613
|
-
};
|
|
47614
|
-
}
|
|
47615
47750
|
return {
|
|
47616
47751
|
success: false,
|
|
47617
47752
|
url: args.url,
|
|
47753
|
+
method: "native",
|
|
47618
47754
|
title: "",
|
|
47619
47755
|
content: "",
|
|
47620
|
-
|
|
47756
|
+
durationMs: Date.now() - startTime,
|
|
47757
|
+
attemptedMethods,
|
|
47621
47758
|
error: error.message
|
|
47622
47759
|
};
|
|
47623
47760
|
}
|
|
47624
47761
|
}
|
|
47625
|
-
|
|
47626
|
-
|
|
47627
|
-
|
|
47628
|
-
|
|
47629
|
-
|
|
47630
|
-
|
|
47631
|
-
|
|
47632
|
-
|
|
47633
|
-
|
|
47634
|
-
|
|
47635
|
-
|
|
47636
|
-
|
|
47637
|
-
|
|
47638
|
-
|
|
47639
|
-
|
|
47640
|
-
|
|
47641
|
-
|
|
47642
|
-
|
|
47643
|
-
|
|
47644
|
-
|
|
47645
|
-
|
|
47646
|
-
|
|
47647
|
-
|
|
47648
|
-
|
|
47649
|
-
|
|
47650
|
-
|
|
47651
|
-
|
|
47652
|
-
|
|
47653
|
-
|
|
47654
|
-
|
|
47655
|
-
|
|
47656
|
-
|
|
47657
|
-
|
|
47658
|
-
|
|
47659
|
-
|
|
47660
|
-
|
|
47661
|
-
|
|
47662
|
-
|
|
47663
|
-
|
|
47664
|
-
|
|
47665
|
-
|
|
47666
|
-
|
|
47667
|
-
|
|
47668
|
-
|
|
47669
|
-
|
|
47670
|
-
const data = await response.json();
|
|
47671
|
-
if (!data.web?.results || !Array.isArray(data.web.results)) {
|
|
47672
|
-
throw new Error("Invalid response from Brave API");
|
|
47673
|
-
}
|
|
47674
|
-
return data.web.results.slice(0, numResults).map((result, index) => ({
|
|
47675
|
-
title: result.title || "Untitled",
|
|
47676
|
-
url: result.url || "",
|
|
47677
|
-
snippet: result.description || "",
|
|
47678
|
-
position: index + 1
|
|
47679
|
-
}));
|
|
47680
|
-
}
|
|
47681
|
-
|
|
47682
|
-
// src/tools/web/searchProviders/tavily.ts
|
|
47683
|
-
async function searchWithTavily(query, numResults, apiKey) {
|
|
47684
|
-
const response = await fetch("https://api.tavily.com/search", {
|
|
47685
|
-
method: "POST",
|
|
47686
|
-
headers: {
|
|
47687
|
-
"Content-Type": "application/json"
|
|
47688
|
-
},
|
|
47689
|
-
body: JSON.stringify({
|
|
47690
|
-
api_key: apiKey,
|
|
47691
|
-
query,
|
|
47692
|
-
max_results: numResults,
|
|
47693
|
-
search_depth: "basic",
|
|
47694
|
-
// 'basic' or 'advanced'
|
|
47695
|
-
include_answer: false,
|
|
47696
|
-
include_raw_content: false
|
|
47697
|
-
})
|
|
47698
|
-
});
|
|
47699
|
-
if (!response.ok) {
|
|
47700
|
-
throw new Error(`Tavily API error: ${response.status} ${response.statusText}`);
|
|
47701
|
-
}
|
|
47702
|
-
const data = await response.json();
|
|
47703
|
-
if (!data.results || !Array.isArray(data.results)) {
|
|
47704
|
-
throw new Error("Invalid response from Tavily API");
|
|
47705
|
-
}
|
|
47706
|
-
return data.results.slice(0, numResults).map((result, index) => ({
|
|
47707
|
-
title: result.title || "Untitled",
|
|
47708
|
-
url: result.url || "",
|
|
47709
|
-
snippet: result.content || "",
|
|
47710
|
-
position: index + 1
|
|
47711
|
-
}));
|
|
47712
|
-
}
|
|
47713
|
-
|
|
47714
|
-
// src/tools/web/webSearch.ts
|
|
47715
|
-
var searchLogger = exports.logger.child({ component: "webSearch" });
|
|
47716
|
-
var SEARCH_SERVICE_TYPES = ["serper", "brave-search", "tavily", "rapidapi-search"];
|
|
47717
|
-
var webSearch = {
|
|
47718
|
-
definition: {
|
|
47719
|
-
type: "function",
|
|
47720
|
-
function: {
|
|
47721
|
-
name: "web_search",
|
|
47722
|
-
description: `Search the web and get relevant results with snippets.
|
|
47723
|
-
|
|
47724
|
-
RETURNS:
|
|
47725
|
-
An array of search results, each containing:
|
|
47726
|
-
- title: Page title
|
|
47727
|
-
- url: Direct URL to the page
|
|
47728
|
-
- snippet: Short description/excerpt from the page
|
|
47729
|
-
- position: Search ranking position (1, 2, 3...)
|
|
47730
|
-
|
|
47731
|
-
USE CASES:
|
|
47732
|
-
- Find current information on any topic
|
|
47733
|
-
- Research multiple sources
|
|
47734
|
-
- Discover relevant websites
|
|
47735
|
-
- Find URLs to fetch with web_fetch tool
|
|
47736
|
-
|
|
47737
|
-
WORKFLOW PATTERN:
|
|
47738
|
-
1. Use web_search to find relevant URLs
|
|
47739
|
-
2. Use web_fetch to get full content from top results
|
|
47740
|
-
3. Process and summarize the information
|
|
47741
|
-
|
|
47742
|
-
EXAMPLE:
|
|
47743
|
-
{
|
|
47744
|
-
"query": "latest AI developments 2026",
|
|
47745
|
-
"numResults": 5
|
|
47746
|
-
}`,
|
|
47747
|
-
parameters: {
|
|
47748
|
-
type: "object",
|
|
47749
|
-
properties: {
|
|
47750
|
-
query: {
|
|
47751
|
-
type: "string",
|
|
47752
|
-
description: "The search query string. Be specific for better results."
|
|
47753
|
-
},
|
|
47754
|
-
numResults: {
|
|
47755
|
-
type: "number",
|
|
47756
|
-
description: "Number of results to return (default: 10, max: 100)."
|
|
47757
|
-
},
|
|
47758
|
-
country: {
|
|
47759
|
-
type: "string",
|
|
47760
|
-
description: 'Country/region code for localized results (e.g., "us", "gb", "de")'
|
|
47761
|
-
},
|
|
47762
|
-
language: {
|
|
47763
|
-
type: "string",
|
|
47764
|
-
description: 'Language code for results (e.g., "en", "fr", "de")'
|
|
47765
|
-
}
|
|
47766
|
-
},
|
|
47767
|
-
required: ["query"]
|
|
47768
|
-
}
|
|
47769
|
-
},
|
|
47770
|
-
blocking: true,
|
|
47771
|
-
timeout: 15e3
|
|
47772
|
-
},
|
|
47773
|
-
execute: async (args) => {
|
|
47774
|
-
const numResults = args.numResults || 10;
|
|
47775
|
-
const connector = findConnectorByServiceTypes(SEARCH_SERVICE_TYPES);
|
|
47776
|
-
if (connector) {
|
|
47777
|
-
return await executeWithConnector(connector.name, args, numResults);
|
|
47778
|
-
}
|
|
47779
|
-
return await executeWithEnvVar(args, numResults);
|
|
47780
|
-
},
|
|
47781
|
-
describeCall: (args) => `"${args.query}"${args.numResults ? ` (${args.numResults} results)` : ""}`
|
|
47782
|
-
};
|
|
47783
|
-
async function executeWithConnector(connectorName, args, numResults) {
|
|
47784
|
-
searchLogger.debug({ connectorName }, "Executing search with connector");
|
|
47785
|
-
try {
|
|
47786
|
-
const searchProvider = SearchProvider.create({ connector: connectorName });
|
|
47787
|
-
const response = await searchProvider.search(args.query, {
|
|
47788
|
-
numResults,
|
|
47789
|
-
country: args.country,
|
|
47790
|
-
language: args.language
|
|
47791
|
-
});
|
|
47792
|
-
if (response.success) {
|
|
47793
|
-
searchLogger.debug({
|
|
47794
|
-
provider: response.provider,
|
|
47795
|
-
count: response.count
|
|
47796
|
-
}, "Search completed successfully");
|
|
47797
|
-
} else {
|
|
47798
|
-
searchLogger.warn({
|
|
47799
|
-
provider: response.provider,
|
|
47800
|
-
error: response.error
|
|
47801
|
-
}, "Search failed");
|
|
47802
|
-
}
|
|
47803
|
-
return {
|
|
47804
|
-
success: response.success,
|
|
47805
|
-
query: response.query,
|
|
47806
|
-
provider: response.provider,
|
|
47807
|
-
results: response.results,
|
|
47808
|
-
count: response.count,
|
|
47809
|
-
error: response.error
|
|
47810
|
-
};
|
|
47811
|
-
} catch (error) {
|
|
47812
|
-
searchLogger.error({ error: error.message, connectorName }, "Search threw exception");
|
|
47813
|
-
return {
|
|
47814
|
-
success: false,
|
|
47815
|
-
query: args.query,
|
|
47816
|
-
provider: connectorName,
|
|
47817
|
-
results: [],
|
|
47818
|
-
count: 0,
|
|
47819
|
-
error: error.message || "Unknown error"
|
|
47820
|
-
};
|
|
47821
|
-
}
|
|
47822
|
-
}
|
|
47823
|
-
async function executeWithEnvVar(args, numResults) {
|
|
47824
|
-
const providers = [
|
|
47825
|
-
{ name: "serper", key: process.env.SERPER_API_KEY, fn: searchWithSerper },
|
|
47826
|
-
{ name: "brave", key: process.env.BRAVE_API_KEY, fn: searchWithBrave },
|
|
47827
|
-
{ name: "tavily", key: process.env.TAVILY_API_KEY, fn: searchWithTavily }
|
|
47828
|
-
];
|
|
47829
|
-
for (const provider of providers) {
|
|
47830
|
-
if (provider.key) {
|
|
47831
|
-
searchLogger.debug({ provider: provider.name }, "Using environment variable fallback");
|
|
47832
|
-
try {
|
|
47833
|
-
const results = await provider.fn(args.query, numResults, provider.key);
|
|
47834
|
-
return {
|
|
47835
|
-
success: true,
|
|
47836
|
-
query: args.query,
|
|
47837
|
-
provider: provider.name,
|
|
47838
|
-
results,
|
|
47839
|
-
count: results.length
|
|
47840
|
-
};
|
|
47841
|
-
} catch (error) {
|
|
47842
|
-
searchLogger.warn({ provider: provider.name, error: error.message }, "Provider failed, trying next");
|
|
47843
|
-
}
|
|
47762
|
+
async function tryAPI(args, startTime, attemptedMethods) {
|
|
47763
|
+
attemptedMethods.push(`api:${connector.name}`);
|
|
47764
|
+
scrapeLogger.debug({ url: args.url, connectorName: connector.name }, "Trying external API");
|
|
47765
|
+
try {
|
|
47766
|
+
const provider = ScrapeProvider.create({ connector: connector.name });
|
|
47767
|
+
const options = {
|
|
47768
|
+
timeout: args.timeout,
|
|
47769
|
+
waitForSelector: args.waitForSelector,
|
|
47770
|
+
includeHtml: args.includeHtml,
|
|
47771
|
+
includeMarkdown: args.includeMarkdown,
|
|
47772
|
+
includeLinks: args.includeLinks
|
|
47773
|
+
};
|
|
47774
|
+
const result = await provider.scrape(args.url, options);
|
|
47775
|
+
const rawContent = result.result?.content || "";
|
|
47776
|
+
const rawMarkdown = result.result?.markdown;
|
|
47777
|
+
const cleanContent = stripBase64DataUris(rawContent);
|
|
47778
|
+
const cleanMarkdown = rawMarkdown ? stripBase64DataUris(rawMarkdown) : void 0;
|
|
47779
|
+
const isDuplicate = !!cleanMarkdown && cleanContent === cleanMarkdown;
|
|
47780
|
+
return {
|
|
47781
|
+
success: result.success,
|
|
47782
|
+
url: args.url,
|
|
47783
|
+
finalUrl: result.finalUrl,
|
|
47784
|
+
method: result.provider,
|
|
47785
|
+
title: result.result?.title || "",
|
|
47786
|
+
content: cleanContent,
|
|
47787
|
+
html: result.result?.html,
|
|
47788
|
+
markdown: isDuplicate ? void 0 : cleanMarkdown,
|
|
47789
|
+
metadata: result.result?.metadata,
|
|
47790
|
+
links: result.result?.links,
|
|
47791
|
+
qualityScore: result.success ? 90 : 0,
|
|
47792
|
+
durationMs: Date.now() - startTime,
|
|
47793
|
+
attemptedMethods,
|
|
47794
|
+
error: result.error
|
|
47795
|
+
};
|
|
47796
|
+
} catch (error) {
|
|
47797
|
+
return {
|
|
47798
|
+
success: false,
|
|
47799
|
+
url: args.url,
|
|
47800
|
+
method: "api",
|
|
47801
|
+
title: "",
|
|
47802
|
+
content: "",
|
|
47803
|
+
durationMs: Date.now() - startTime,
|
|
47804
|
+
attemptedMethods,
|
|
47805
|
+
error: error.message
|
|
47806
|
+
};
|
|
47844
47807
|
}
|
|
47845
47808
|
}
|
|
47846
47809
|
return {
|
|
47847
|
-
|
|
47848
|
-
|
|
47849
|
-
|
|
47850
|
-
|
|
47851
|
-
|
|
47852
|
-
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."
|
|
47853
|
-
};
|
|
47854
|
-
}
|
|
47855
|
-
|
|
47856
|
-
// src/tools/web/webScrape.ts
|
|
47857
|
-
init_Logger();
|
|
47858
|
-
var scrapeLogger = exports.logger.child({ component: "webScrape" });
|
|
47859
|
-
function stripBase64DataUris(content) {
|
|
47860
|
-
if (!content) return content;
|
|
47861
|
-
let cleaned = content.replace(/!\[[^\]]*\]\(data:[^)]+\)/g, "[image removed]");
|
|
47862
|
-
cleaned = cleaned.replace(/url\(['"]?data:[^)]+['"]?\)/gi, "url([data-uri-removed])");
|
|
47863
|
-
cleaned = cleaned.replace(/data:(?:image|font|application)\/[^;]+;base64,[A-Za-z0-9+/=]{100,}/g, "[base64-data-removed]");
|
|
47864
|
-
return cleaned;
|
|
47865
|
-
}
|
|
47866
|
-
var SCRAPE_SERVICE_TYPES = ["zenrows", "jina-reader", "firecrawl", "scrapingbee"];
|
|
47867
|
-
var DEFAULT_MIN_QUALITY = 50;
|
|
47868
|
-
var webScrape = {
|
|
47869
|
-
definition: {
|
|
47870
|
-
type: "function",
|
|
47871
|
-
function: {
|
|
47872
|
-
name: "web_scrape",
|
|
47873
|
-
description: `Scrape any URL with automatic fallback - guaranteed to work on most sites.
|
|
47810
|
+
definition: {
|
|
47811
|
+
type: "function",
|
|
47812
|
+
function: {
|
|
47813
|
+
name: "web_scrape",
|
|
47814
|
+
description: `Scrape any URL with automatic fallback - guaranteed to work on most sites.
|
|
47874
47815
|
|
|
47875
47816
|
Automatically tries multiple methods in sequence:
|
|
47876
47817
|
1. Native fetch - Fast (~1s), works for blogs/docs/articles
|
|
47877
|
-
2.
|
|
47878
|
-
3. External API - Handles bot protection, CAPTCHAs (if configured)
|
|
47818
|
+
2. External API - Handles bot protection, CAPTCHAs, SPAs (if configured)
|
|
47879
47819
|
|
|
47880
47820
|
RETURNS:
|
|
47881
47821
|
{
|
|
@@ -47910,46 +47850,64 @@ For JS-heavy sites:
|
|
|
47910
47850
|
"url": "https://spa-app.com",
|
|
47911
47851
|
"waitForSelector": ".main-content"
|
|
47912
47852
|
}`,
|
|
47913
|
-
|
|
47914
|
-
|
|
47915
|
-
|
|
47916
|
-
|
|
47917
|
-
|
|
47918
|
-
|
|
47919
|
-
|
|
47920
|
-
|
|
47921
|
-
|
|
47922
|
-
|
|
47923
|
-
|
|
47924
|
-
|
|
47925
|
-
|
|
47926
|
-
|
|
47927
|
-
|
|
47928
|
-
|
|
47929
|
-
|
|
47930
|
-
|
|
47931
|
-
|
|
47932
|
-
|
|
47933
|
-
|
|
47934
|
-
|
|
47853
|
+
parameters: {
|
|
47854
|
+
type: "object",
|
|
47855
|
+
properties: {
|
|
47856
|
+
url: {
|
|
47857
|
+
type: "string",
|
|
47858
|
+
description: "URL to scrape. Must start with http:// or https://"
|
|
47859
|
+
},
|
|
47860
|
+
timeout: {
|
|
47861
|
+
type: "number",
|
|
47862
|
+
description: "Timeout in milliseconds (default: 30000)"
|
|
47863
|
+
},
|
|
47864
|
+
includeHtml: {
|
|
47865
|
+
type: "boolean",
|
|
47866
|
+
description: "Include raw HTML in response (default: false)"
|
|
47867
|
+
},
|
|
47868
|
+
includeMarkdown: {
|
|
47869
|
+
type: "boolean",
|
|
47870
|
+
description: "Include markdown conversion (default: false)"
|
|
47871
|
+
},
|
|
47872
|
+
includeLinks: {
|
|
47873
|
+
type: "boolean",
|
|
47874
|
+
description: "Extract and include links (default: false)"
|
|
47875
|
+
},
|
|
47876
|
+
waitForSelector: {
|
|
47877
|
+
type: "string",
|
|
47878
|
+
description: "CSS selector to wait for before scraping (for JS-heavy sites)"
|
|
47879
|
+
}
|
|
47935
47880
|
},
|
|
47936
|
-
|
|
47937
|
-
|
|
47938
|
-
|
|
47939
|
-
|
|
47940
|
-
|
|
47941
|
-
required: ["url"]
|
|
47942
|
-
}
|
|
47881
|
+
required: ["url"]
|
|
47882
|
+
}
|
|
47883
|
+
},
|
|
47884
|
+
blocking: true,
|
|
47885
|
+
timeout: 6e4
|
|
47943
47886
|
},
|
|
47944
|
-
|
|
47945
|
-
|
|
47946
|
-
|
|
47947
|
-
|
|
47948
|
-
|
|
47949
|
-
|
|
47950
|
-
|
|
47951
|
-
|
|
47952
|
-
|
|
47887
|
+
execute: async (args) => {
|
|
47888
|
+
const startTime = Date.now();
|
|
47889
|
+
const attemptedMethods = [];
|
|
47890
|
+
try {
|
|
47891
|
+
new URL(args.url);
|
|
47892
|
+
} catch {
|
|
47893
|
+
return {
|
|
47894
|
+
success: false,
|
|
47895
|
+
url: args.url,
|
|
47896
|
+
method: "none",
|
|
47897
|
+
title: "",
|
|
47898
|
+
content: "",
|
|
47899
|
+
durationMs: Date.now() - startTime,
|
|
47900
|
+
attemptedMethods: [],
|
|
47901
|
+
error: "Invalid URL format"
|
|
47902
|
+
};
|
|
47903
|
+
}
|
|
47904
|
+
const native = await tryNative(args, startTime, attemptedMethods);
|
|
47905
|
+
if (native.success && (native.qualityScore ?? 0) >= DEFAULT_MIN_QUALITY) {
|
|
47906
|
+
return native;
|
|
47907
|
+
}
|
|
47908
|
+
const api = await tryAPI(args, startTime, attemptedMethods);
|
|
47909
|
+
if (api.success) return api;
|
|
47910
|
+
if (native.success) return native;
|
|
47953
47911
|
return {
|
|
47954
47912
|
success: false,
|
|
47955
47913
|
url: args.url,
|
|
@@ -47957,232 +47915,119 @@ For JS-heavy sites:
|
|
|
47957
47915
|
title: "",
|
|
47958
47916
|
content: "",
|
|
47959
47917
|
durationMs: Date.now() - startTime,
|
|
47960
|
-
attemptedMethods
|
|
47961
|
-
error: "
|
|
47918
|
+
attemptedMethods,
|
|
47919
|
+
error: "All scraping methods failed. Site may have bot protection."
|
|
47962
47920
|
};
|
|
47963
|
-
}
|
|
47964
|
-
|
|
47965
|
-
|
|
47966
|
-
return native;
|
|
47967
|
-
}
|
|
47968
|
-
const js = await tryJS(args, startTime, attemptedMethods);
|
|
47969
|
-
if (js.success && (js.qualityScore ?? 0) >= DEFAULT_MIN_QUALITY) {
|
|
47970
|
-
return js;
|
|
47971
|
-
}
|
|
47972
|
-
const connector = findConnectorByServiceTypes(SCRAPE_SERVICE_TYPES);
|
|
47973
|
-
if (connector) {
|
|
47974
|
-
const api = await tryAPI(connector.name, args, startTime, attemptedMethods);
|
|
47975
|
-
if (api.success) return api;
|
|
47976
|
-
}
|
|
47977
|
-
if (js.success) return js;
|
|
47978
|
-
if (native.success) return native;
|
|
47979
|
-
return {
|
|
47980
|
-
success: false,
|
|
47981
|
-
url: args.url,
|
|
47982
|
-
method: "none",
|
|
47983
|
-
title: "",
|
|
47984
|
-
content: "",
|
|
47985
|
-
durationMs: Date.now() - startTime,
|
|
47986
|
-
attemptedMethods,
|
|
47987
|
-
error: "All scraping methods failed. Site may have bot protection."
|
|
47988
|
-
};
|
|
47989
|
-
},
|
|
47990
|
-
describeCall: (args) => args.url
|
|
47991
|
-
};
|
|
47992
|
-
async function tryNative(args, startTime, attemptedMethods) {
|
|
47993
|
-
attemptedMethods.push("native");
|
|
47994
|
-
scrapeLogger.debug({ url: args.url }, "Trying native fetch");
|
|
47995
|
-
try {
|
|
47996
|
-
const result = await webFetch.execute({
|
|
47997
|
-
url: args.url,
|
|
47998
|
-
timeout: args.timeout || 1e4
|
|
47999
|
-
});
|
|
48000
|
-
const cleanContent = stripBase64DataUris(result.content);
|
|
48001
|
-
return {
|
|
48002
|
-
success: result.success,
|
|
48003
|
-
url: args.url,
|
|
48004
|
-
finalUrl: args.url,
|
|
48005
|
-
method: "native",
|
|
48006
|
-
title: result.title,
|
|
48007
|
-
content: cleanContent,
|
|
48008
|
-
// Native method already returns markdown-like content — no separate markdown field needed
|
|
48009
|
-
// (would just duplicate content and waste tokens)
|
|
48010
|
-
qualityScore: result.qualityScore,
|
|
48011
|
-
durationMs: Date.now() - startTime,
|
|
48012
|
-
attemptedMethods,
|
|
48013
|
-
error: result.error
|
|
48014
|
-
};
|
|
48015
|
-
} catch (error) {
|
|
48016
|
-
return {
|
|
48017
|
-
success: false,
|
|
48018
|
-
url: args.url,
|
|
48019
|
-
method: "native",
|
|
48020
|
-
title: "",
|
|
48021
|
-
content: "",
|
|
48022
|
-
durationMs: Date.now() - startTime,
|
|
48023
|
-
attemptedMethods,
|
|
48024
|
-
error: error.message
|
|
48025
|
-
};
|
|
48026
|
-
}
|
|
47921
|
+
},
|
|
47922
|
+
describeCall: (args) => args.url
|
|
47923
|
+
};
|
|
48027
47924
|
}
|
|
48028
|
-
|
|
48029
|
-
|
|
48030
|
-
|
|
48031
|
-
|
|
48032
|
-
|
|
48033
|
-
|
|
48034
|
-
|
|
48035
|
-
|
|
48036
|
-
|
|
48037
|
-
const cleanContent = stripBase64DataUris(result.content);
|
|
48038
|
-
return {
|
|
48039
|
-
success: result.success,
|
|
48040
|
-
url: args.url,
|
|
48041
|
-
finalUrl: args.url,
|
|
48042
|
-
method: "js",
|
|
48043
|
-
title: result.title,
|
|
48044
|
-
content: cleanContent,
|
|
48045
|
-
// JS method already returns markdown-like content — no separate markdown field needed
|
|
48046
|
-
qualityScore: result.success ? 80 : 0,
|
|
48047
|
-
durationMs: Date.now() - startTime,
|
|
48048
|
-
attemptedMethods,
|
|
48049
|
-
error: result.error
|
|
48050
|
-
};
|
|
48051
|
-
} catch (error) {
|
|
48052
|
-
return {
|
|
48053
|
-
success: false,
|
|
48054
|
-
url: args.url,
|
|
48055
|
-
method: "js",
|
|
48056
|
-
title: "",
|
|
48057
|
-
content: "",
|
|
48058
|
-
durationMs: Date.now() - startTime,
|
|
48059
|
-
attemptedMethods,
|
|
48060
|
-
error: error.message
|
|
48061
|
-
};
|
|
47925
|
+
|
|
47926
|
+
// src/tools/web/register.ts
|
|
47927
|
+
var SEARCH_SERVICE_TYPES = ["serper", "brave-search", "tavily", "rapidapi-search"];
|
|
47928
|
+
var SCRAPE_SERVICE_TYPES = ["zenrows", "jina-reader", "firecrawl", "scrapingbee"];
|
|
47929
|
+
function registerWebTools() {
|
|
47930
|
+
for (const st of SEARCH_SERVICE_TYPES) {
|
|
47931
|
+
ConnectorTools.registerService(st, (connector) => [
|
|
47932
|
+
createWebSearchTool(connector)
|
|
47933
|
+
]);
|
|
48062
47934
|
}
|
|
48063
|
-
|
|
48064
|
-
|
|
48065
|
-
|
|
48066
|
-
|
|
48067
|
-
try {
|
|
48068
|
-
const provider = ScrapeProvider.create({ connector: connectorName });
|
|
48069
|
-
const options = {
|
|
48070
|
-
timeout: args.timeout,
|
|
48071
|
-
waitForSelector: args.waitForSelector,
|
|
48072
|
-
includeHtml: args.includeHtml,
|
|
48073
|
-
includeMarkdown: args.includeMarkdown,
|
|
48074
|
-
includeLinks: args.includeLinks
|
|
48075
|
-
};
|
|
48076
|
-
const result = await provider.scrape(args.url, options);
|
|
48077
|
-
const rawContent = result.result?.content || "";
|
|
48078
|
-
const rawMarkdown = result.result?.markdown;
|
|
48079
|
-
const cleanContent = stripBase64DataUris(rawContent);
|
|
48080
|
-
const cleanMarkdown = rawMarkdown ? stripBase64DataUris(rawMarkdown) : void 0;
|
|
48081
|
-
const isDuplicate = !!cleanMarkdown && cleanContent === cleanMarkdown;
|
|
48082
|
-
return {
|
|
48083
|
-
success: result.success,
|
|
48084
|
-
url: args.url,
|
|
48085
|
-
finalUrl: result.finalUrl,
|
|
48086
|
-
method: result.provider,
|
|
48087
|
-
title: result.result?.title || "",
|
|
48088
|
-
content: cleanContent,
|
|
48089
|
-
html: result.result?.html,
|
|
48090
|
-
// Keep raw HTML as-is (only used if explicitly requested)
|
|
48091
|
-
markdown: isDuplicate ? void 0 : cleanMarkdown,
|
|
48092
|
-
metadata: result.result?.metadata,
|
|
48093
|
-
links: result.result?.links,
|
|
48094
|
-
qualityScore: result.success ? 90 : 0,
|
|
48095
|
-
durationMs: Date.now() - startTime,
|
|
48096
|
-
attemptedMethods,
|
|
48097
|
-
error: result.error
|
|
48098
|
-
};
|
|
48099
|
-
} catch (error) {
|
|
48100
|
-
return {
|
|
48101
|
-
success: false,
|
|
48102
|
-
url: args.url,
|
|
48103
|
-
method: "api",
|
|
48104
|
-
title: "",
|
|
48105
|
-
content: "",
|
|
48106
|
-
durationMs: Date.now() - startTime,
|
|
48107
|
-
attemptedMethods,
|
|
48108
|
-
error: error.message
|
|
48109
|
-
};
|
|
47935
|
+
for (const st of SCRAPE_SERVICE_TYPES) {
|
|
47936
|
+
ConnectorTools.registerService(st, (connector) => [
|
|
47937
|
+
createWebScrapeTool(connector)
|
|
47938
|
+
]);
|
|
48110
47939
|
}
|
|
48111
47940
|
}
|
|
48112
47941
|
|
|
47942
|
+
// src/tools/web/index.ts
|
|
47943
|
+
registerWebTools();
|
|
47944
|
+
|
|
48113
47945
|
// src/tools/code/executeJavaScript.ts
|
|
48114
47946
|
init_Connector();
|
|
48115
|
-
|
|
48116
|
-
|
|
48117
|
-
|
|
48118
|
-
|
|
48119
|
-
|
|
48120
|
-
|
|
48121
|
-
|
|
48122
|
-
|
|
48123
|
-
}).join("\n
|
|
48124
|
-
return `
|
|
48125
|
-
|
|
48126
|
-
|
|
48127
|
-
|
|
48128
|
-
|
|
48129
|
-
|
|
48130
|
-
|
|
47947
|
+
var DEFAULT_TIMEOUT = 1e4;
|
|
47948
|
+
var DEFAULT_MAX_TIMEOUT = 3e4;
|
|
47949
|
+
function formatConnectorEntry(c) {
|
|
47950
|
+
const parts = [];
|
|
47951
|
+
const serviceOrVendor = c.serviceType ?? c.vendor ?? void 0;
|
|
47952
|
+
if (serviceOrVendor) parts.push(`Service: ${serviceOrVendor}`);
|
|
47953
|
+
if (c.config.description) parts.push(c.config.description);
|
|
47954
|
+
if (c.baseURL) parts.push(`URL: ${c.baseURL}`);
|
|
47955
|
+
const details = parts.map((p) => ` ${p}`).join("\n");
|
|
47956
|
+
return ` \u2022 "${c.name}" (${c.displayName})
|
|
47957
|
+
${details}`;
|
|
47958
|
+
}
|
|
47959
|
+
function generateDescription(context, maxTimeout) {
|
|
47960
|
+
const registry = context?.connectorRegistry ?? exports.Connector.asRegistry();
|
|
47961
|
+
const connectors = registry.listAll();
|
|
47962
|
+
const connectorList = connectors.length > 0 ? connectors.map(formatConnectorEntry).join("\n\n") : " No connectors registered.";
|
|
47963
|
+
const timeoutSec = Math.round(maxTimeout / 1e3);
|
|
47964
|
+
return `Execute JavaScript code in a secure sandbox with authenticated API access to external services.
|
|
47965
|
+
|
|
47966
|
+
Use this tool when you need to:
|
|
47967
|
+
- Call external APIs (GitHub, Slack, Stripe, etc.) using registered connectors
|
|
47968
|
+
- Process, transform, or compute data that requires programmatic logic
|
|
47969
|
+
- Chain multiple API calls or perform complex data manipulation
|
|
47970
|
+
- Do anything that plain text generation cannot accomplish
|
|
47971
|
+
|
|
47972
|
+
SANDBOX API:
|
|
47973
|
+
|
|
47974
|
+
1. authenticatedFetch(url, options, connectorName)
|
|
47975
|
+
Makes authenticated HTTP requests using the connector's credentials.
|
|
47976
|
+
The current user's identity (userId) is automatically included \u2014 no need to pass it.
|
|
47977
|
+
Auth headers are added automatically \u2014 DO NOT set Authorization header manually.
|
|
48131
47978
|
|
|
48132
47979
|
Parameters:
|
|
48133
|
-
\u2022 url: Full URL or relative
|
|
47980
|
+
\u2022 url: Full URL or path relative to the connector's base URL
|
|
48134
47981
|
- Full: "https://api.github.com/user/repos"
|
|
48135
|
-
- Relative: "/user/repos" (
|
|
48136
|
-
\u2022 options: Standard fetch options { method,
|
|
48137
|
-
|
|
48138
|
-
\u2022
|
|
47982
|
+
- Relative: "/user/repos" (resolved against connector's base URL)
|
|
47983
|
+
\u2022 options: Standard fetch options { method, headers, body }
|
|
47984
|
+
- For POST/PUT: set body to JSON.stringify(data) and headers to { 'Content-Type': 'application/json' }
|
|
47985
|
+
\u2022 connectorName: Name of a registered connector (see list below)
|
|
48139
47986
|
|
|
48140
47987
|
Returns: Promise<Response>
|
|
48141
|
-
\u2022 response.ok
|
|
48142
|
-
\u2022 response.status
|
|
48143
|
-
\u2022 response.json()
|
|
48144
|
-
\u2022 response.text()
|
|
47988
|
+
\u2022 response.ok \u2014 true if status 200-299
|
|
47989
|
+
\u2022 response.status \u2014 HTTP status code
|
|
47990
|
+
\u2022 await response.json() \u2014 parse JSON body
|
|
47991
|
+
\u2022 await response.text() \u2014 get text body
|
|
48145
47992
|
|
|
48146
|
-
|
|
48147
|
-
\u2022 Bearer tokens (GitHub, Slack, Stripe)
|
|
48148
|
-
\u2022 Bot tokens (Discord)
|
|
48149
|
-
\u2022 Basic auth (Twilio, Zendesk)
|
|
48150
|
-
\u2022 Custom headers (Shopify uses X-Shopify-Access-Token)
|
|
47993
|
+
2. fetch(url, options) \u2014 Standard fetch without authentication
|
|
48151
47994
|
|
|
48152
|
-
|
|
48153
|
-
|
|
48154
|
-
4. fetch(url, options) - Standard fetch (no auth)
|
|
47995
|
+
3. connectors.list() \u2014 Array of available connector names
|
|
47996
|
+
4. connectors.get(name) \u2014 Connector info: { displayName, description, baseURL, serviceType }
|
|
48155
47997
|
|
|
48156
|
-
|
|
48157
|
-
\u2022 input
|
|
48158
|
-
\u2022 output
|
|
47998
|
+
VARIABLES:
|
|
47999
|
+
\u2022 input \u2014 data passed via the "input" parameter (default: {})
|
|
48000
|
+
\u2022 output \u2014 SET THIS to return your result to the caller
|
|
48159
48001
|
|
|
48160
|
-
|
|
48002
|
+
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
|
|
48161
48003
|
|
|
48162
48004
|
REGISTERED CONNECTORS:
|
|
48163
48005
|
${connectorList}
|
|
48164
48006
|
|
|
48165
|
-
|
|
48166
|
-
(async () => {
|
|
48167
|
-
const response = await authenticatedFetch(
|
|
48168
|
-
'/user/repos',
|
|
48169
|
-
{ method: 'GET' },
|
|
48170
|
-
'github'
|
|
48171
|
-
);
|
|
48007
|
+
EXAMPLES:
|
|
48172
48008
|
|
|
48173
|
-
|
|
48174
|
-
|
|
48175
|
-
|
|
48009
|
+
// GET request
|
|
48010
|
+
const resp = await authenticatedFetch('/user/repos', { method: 'GET' }, 'github');
|
|
48011
|
+
const repos = await resp.json();
|
|
48012
|
+
output = repos.map(r => r.full_name);
|
|
48176
48013
|
|
|
48177
|
-
|
|
48178
|
-
|
|
48014
|
+
// POST request with JSON body
|
|
48015
|
+
const resp = await authenticatedFetch('/chat.postMessage', {
|
|
48016
|
+
method: 'POST',
|
|
48017
|
+
headers: { 'Content-Type': 'application/json' },
|
|
48018
|
+
body: JSON.stringify({ channel: '#general', text: 'Hello!' })
|
|
48019
|
+
}, 'slack');
|
|
48020
|
+
output = await resp.json();
|
|
48179
48021
|
|
|
48180
|
-
|
|
48181
|
-
|
|
48022
|
+
// Data processing (no API needed)
|
|
48023
|
+
const items = input.data;
|
|
48024
|
+
output = items.filter(i => i.score > 0.8).sort((a, b) => b.score - a.score);
|
|
48182
48025
|
|
|
48183
|
-
|
|
48026
|
+
LIMITS: ${timeoutSec}s max timeout, no file system access, no require/import.`;
|
|
48184
48027
|
}
|
|
48185
|
-
function createExecuteJavaScriptTool() {
|
|
48028
|
+
function createExecuteJavaScriptTool(options) {
|
|
48029
|
+
const maxTimeout = options?.maxTimeout ?? DEFAULT_MAX_TIMEOUT;
|
|
48030
|
+
const defaultTimeout = options?.defaultTimeout ?? DEFAULT_TIMEOUT;
|
|
48186
48031
|
return {
|
|
48187
48032
|
definition: {
|
|
48188
48033
|
type: "function",
|
|
@@ -48195,32 +48040,40 @@ function createExecuteJavaScriptTool() {
|
|
|
48195
48040
|
properties: {
|
|
48196
48041
|
code: {
|
|
48197
48042
|
type: "string",
|
|
48198
|
-
description: 'JavaScript code to execute.
|
|
48043
|
+
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 () => { ... })().'
|
|
48199
48044
|
},
|
|
48200
48045
|
input: {
|
|
48201
|
-
description: 'Optional
|
|
48046
|
+
description: 'Optional data available as the "input" variable in your code. Can be any JSON value.'
|
|
48202
48047
|
},
|
|
48203
48048
|
timeout: {
|
|
48204
48049
|
type: "number",
|
|
48205
|
-
description:
|
|
48050
|
+
description: `Execution timeout in milliseconds. Default: ${defaultTimeout}ms, max: ${maxTimeout}ms. Increase for slow API calls or multiple sequential requests.`
|
|
48206
48051
|
}
|
|
48207
48052
|
},
|
|
48208
48053
|
required: ["code"]
|
|
48209
48054
|
}
|
|
48210
48055
|
},
|
|
48211
48056
|
blocking: true,
|
|
48212
|
-
timeout:
|
|
48213
|
-
// Tool timeout
|
|
48057
|
+
timeout: maxTimeout + 5e3
|
|
48058
|
+
// Tool-level timeout slightly above max code timeout
|
|
48214
48059
|
},
|
|
48215
|
-
// Dynamic description
|
|
48216
|
-
//
|
|
48217
|
-
descriptionFactory: generateDescription,
|
|
48218
|
-
execute: async (args) => {
|
|
48060
|
+
// Dynamic description — regenerated each time tool definitions are sent to LLM.
|
|
48061
|
+
// Receives ToolContext so connector list is scoped to current userId.
|
|
48062
|
+
descriptionFactory: (context) => generateDescription(context, maxTimeout),
|
|
48063
|
+
execute: async (args, context) => {
|
|
48219
48064
|
const logs = [];
|
|
48220
48065
|
const startTime = Date.now();
|
|
48221
48066
|
try {
|
|
48222
|
-
const timeout = Math.min(args.timeout ||
|
|
48223
|
-
const
|
|
48067
|
+
const timeout = Math.min(Math.max(args.timeout || defaultTimeout, 0), maxTimeout);
|
|
48068
|
+
const registry = context?.connectorRegistry ?? exports.Connector.asRegistry();
|
|
48069
|
+
const result = await executeInVM(
|
|
48070
|
+
args.code,
|
|
48071
|
+
args.input,
|
|
48072
|
+
timeout,
|
|
48073
|
+
logs,
|
|
48074
|
+
context?.userId,
|
|
48075
|
+
registry
|
|
48076
|
+
);
|
|
48224
48077
|
return {
|
|
48225
48078
|
success: true,
|
|
48226
48079
|
result,
|
|
@@ -48240,31 +48093,36 @@ function createExecuteJavaScriptTool() {
|
|
|
48240
48093
|
};
|
|
48241
48094
|
}
|
|
48242
48095
|
var executeJavaScript = createExecuteJavaScriptTool();
|
|
48243
|
-
async function executeInVM(code, input, timeout, logs) {
|
|
48096
|
+
async function executeInVM(code, input, timeout, logs, userId, registry) {
|
|
48244
48097
|
const sandbox = {
|
|
48245
48098
|
// Input/output
|
|
48246
|
-
input: input
|
|
48099
|
+
input: input ?? {},
|
|
48247
48100
|
output: null,
|
|
48248
|
-
// Console (captured)
|
|
48101
|
+
// Console (captured) — stringify objects for readable logs
|
|
48249
48102
|
console: {
|
|
48250
|
-
log: (...args) => logs.push(args.map((a) => String(a)).join(" ")),
|
|
48251
|
-
error: (...args) => logs.push("ERROR: " + args.map((a) => String(a)).join(" ")),
|
|
48252
|
-
warn: (...args) => logs.push("WARN: " + args.map((a) => String(a)).join(" "))
|
|
48103
|
+
log: (...args) => logs.push(args.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")),
|
|
48104
|
+
error: (...args) => logs.push("ERROR: " + args.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")),
|
|
48105
|
+
warn: (...args) => logs.push("WARN: " + args.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" "))
|
|
48253
48106
|
},
|
|
48254
|
-
// Authenticated fetch
|
|
48255
|
-
|
|
48256
|
-
|
|
48107
|
+
// Authenticated fetch — userId auto-injected from ToolContext.
|
|
48108
|
+
// Only connectors visible in the scoped registry are accessible.
|
|
48109
|
+
authenticatedFetch: (url2, options, connectorName) => {
|
|
48110
|
+
registry.get(connectorName);
|
|
48111
|
+
return authenticatedFetch(url2, options, connectorName, userId);
|
|
48112
|
+
},
|
|
48113
|
+
// Standard fetch (no auth)
|
|
48257
48114
|
fetch: globalThis.fetch,
|
|
48258
|
-
// Connector info
|
|
48115
|
+
// Connector info (userId-scoped)
|
|
48259
48116
|
connectors: {
|
|
48260
|
-
list: () =>
|
|
48117
|
+
list: () => registry.list(),
|
|
48261
48118
|
get: (name) => {
|
|
48262
48119
|
try {
|
|
48263
|
-
const connector =
|
|
48120
|
+
const connector = registry.get(name);
|
|
48264
48121
|
return {
|
|
48265
48122
|
displayName: connector.displayName,
|
|
48266
48123
|
description: connector.config.description || "",
|
|
48267
|
-
baseURL: connector.baseURL
|
|
48124
|
+
baseURL: connector.baseURL,
|
|
48125
|
+
serviceType: connector.serviceType
|
|
48268
48126
|
};
|
|
48269
48127
|
} catch {
|
|
48270
48128
|
return null;
|
|
@@ -48281,14 +48139,22 @@ async function executeInVM(code, input, timeout, logs) {
|
|
|
48281
48139
|
clearTimeout,
|
|
48282
48140
|
clearInterval,
|
|
48283
48141
|
Promise,
|
|
48284
|
-
//
|
|
48142
|
+
// Built-in types
|
|
48285
48143
|
Array,
|
|
48286
48144
|
Object,
|
|
48287
48145
|
String,
|
|
48288
48146
|
Number,
|
|
48289
|
-
Boolean
|
|
48147
|
+
Boolean,
|
|
48148
|
+
RegExp,
|
|
48149
|
+
Map,
|
|
48150
|
+
Set,
|
|
48151
|
+
Error,
|
|
48152
|
+
URL,
|
|
48153
|
+
URLSearchParams,
|
|
48154
|
+
TextEncoder,
|
|
48155
|
+
TextDecoder
|
|
48290
48156
|
};
|
|
48291
|
-
const
|
|
48157
|
+
const vmContext = vm__namespace.createContext(sandbox);
|
|
48292
48158
|
const wrappedCode = code.trim().startsWith("(async") ? code : `
|
|
48293
48159
|
(async () => {
|
|
48294
48160
|
${code}
|
|
@@ -48296,7 +48162,7 @@ async function executeInVM(code, input, timeout, logs) {
|
|
|
48296
48162
|
})()
|
|
48297
48163
|
`;
|
|
48298
48164
|
const script = new vm__namespace.Script(wrappedCode);
|
|
48299
|
-
const resultPromise = script.runInContext(
|
|
48165
|
+
const resultPromise = script.runInContext(vmContext, {
|
|
48300
48166
|
timeout,
|
|
48301
48167
|
displayErrors: true
|
|
48302
48168
|
});
|
|
@@ -48752,7 +48618,7 @@ function createTextToSpeechTool(connector, storage, userId) {
|
|
|
48752
48618
|
}
|
|
48753
48619
|
|
|
48754
48620
|
// src/tools/multimedia/speechToText.ts
|
|
48755
|
-
function createSpeechToTextTool(connector, storage
|
|
48621
|
+
function createSpeechToTextTool(connector, storage) {
|
|
48756
48622
|
const vendor = connector.vendor;
|
|
48757
48623
|
const handler = storage ?? getMediaStorage();
|
|
48758
48624
|
const vendorModels = vendor ? getSTTModelsByVendor(vendor) : [];
|
|
@@ -49013,7 +48879,8 @@ EXAMPLES:
|
|
|
49013
48879
|
riskLevel: "low",
|
|
49014
48880
|
approvalMessage: `Search files in a GitHub repository via ${connector.displayName}`
|
|
49015
48881
|
},
|
|
49016
|
-
execute: async (args) => {
|
|
48882
|
+
execute: async (args, context) => {
|
|
48883
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
49017
48884
|
const resolved = resolveRepository(args.repository, connector);
|
|
49018
48885
|
if (!resolved.success) {
|
|
49019
48886
|
return { success: false, error: resolved.error };
|
|
@@ -49025,14 +48892,14 @@ EXAMPLES:
|
|
|
49025
48892
|
const repoInfo = await githubFetch(
|
|
49026
48893
|
connector,
|
|
49027
48894
|
`/repos/${owner}/${repo}`,
|
|
49028
|
-
{ userId }
|
|
48895
|
+
{ userId: effectiveUserId }
|
|
49029
48896
|
);
|
|
49030
48897
|
ref = repoInfo.default_branch;
|
|
49031
48898
|
}
|
|
49032
48899
|
const tree = await githubFetch(
|
|
49033
48900
|
connector,
|
|
49034
48901
|
`/repos/${owner}/${repo}/git/trees/${ref}?recursive=1`,
|
|
49035
|
-
{ userId }
|
|
48902
|
+
{ userId: effectiveUserId }
|
|
49036
48903
|
);
|
|
49037
48904
|
const matching = tree.tree.filter(
|
|
49038
48905
|
(entry) => entry.type === "blob" && matchGlobPattern2(args.pattern, entry.path)
|
|
@@ -49123,7 +48990,8 @@ EXAMPLES:
|
|
|
49123
48990
|
riskLevel: "low",
|
|
49124
48991
|
approvalMessage: `Search code in a GitHub repository via ${connector.displayName}`
|
|
49125
48992
|
},
|
|
49126
|
-
execute: async (args) => {
|
|
48993
|
+
execute: async (args, context) => {
|
|
48994
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
49127
48995
|
const resolved = resolveRepository(args.repository, connector);
|
|
49128
48996
|
if (!resolved.success) {
|
|
49129
48997
|
return { success: false, error: resolved.error };
|
|
@@ -49140,7 +49008,7 @@ EXAMPLES:
|
|
|
49140
49008
|
connector,
|
|
49141
49009
|
`/search/code`,
|
|
49142
49010
|
{
|
|
49143
|
-
userId,
|
|
49011
|
+
userId: effectiveUserId,
|
|
49144
49012
|
// Request text-match fragments
|
|
49145
49013
|
accept: "application/vnd.github.text-match+json",
|
|
49146
49014
|
queryParams: { q, per_page: perPage }
|
|
@@ -49227,7 +49095,8 @@ NOTE: Files larger than 1MB are fetched via the Git Blob API. Very large files (
|
|
|
49227
49095
|
riskLevel: "low",
|
|
49228
49096
|
approvalMessage: `Read a file from a GitHub repository via ${connector.displayName}`
|
|
49229
49097
|
},
|
|
49230
|
-
execute: async (args) => {
|
|
49098
|
+
execute: async (args, context) => {
|
|
49099
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
49231
49100
|
const resolved = resolveRepository(args.repository, connector);
|
|
49232
49101
|
if (!resolved.success) {
|
|
49233
49102
|
return { success: false, error: resolved.error };
|
|
@@ -49241,7 +49110,7 @@ NOTE: Files larger than 1MB are fetched via the Git Blob API. Very large files (
|
|
|
49241
49110
|
const contentResp = await githubFetch(
|
|
49242
49111
|
connector,
|
|
49243
49112
|
`/repos/${owner}/${repo}/contents/${args.path}${refParam}`,
|
|
49244
|
-
{ userId }
|
|
49113
|
+
{ userId: effectiveUserId }
|
|
49245
49114
|
);
|
|
49246
49115
|
if (contentResp.type !== "file") {
|
|
49247
49116
|
return {
|
|
@@ -49258,7 +49127,7 @@ NOTE: Files larger than 1MB are fetched via the Git Blob API. Very large files (
|
|
|
49258
49127
|
const blob = await githubFetch(
|
|
49259
49128
|
connector,
|
|
49260
49129
|
contentResp.git_url,
|
|
49261
|
-
{ userId }
|
|
49130
|
+
{ userId: effectiveUserId }
|
|
49262
49131
|
);
|
|
49263
49132
|
fileContent = Buffer.from(blob.content, "base64").toString("utf-8");
|
|
49264
49133
|
fileSize = blob.size;
|
|
@@ -49345,7 +49214,8 @@ EXAMPLES:
|
|
|
49345
49214
|
riskLevel: "low",
|
|
49346
49215
|
approvalMessage: `Get pull request details from GitHub via ${connector.displayName}`
|
|
49347
49216
|
},
|
|
49348
|
-
execute: async (args) => {
|
|
49217
|
+
execute: async (args, context) => {
|
|
49218
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
49349
49219
|
const resolved = resolveRepository(args.repository, connector);
|
|
49350
49220
|
if (!resolved.success) {
|
|
49351
49221
|
return { success: false, error: resolved.error };
|
|
@@ -49355,7 +49225,7 @@ EXAMPLES:
|
|
|
49355
49225
|
const pr = await githubFetch(
|
|
49356
49226
|
connector,
|
|
49357
49227
|
`/repos/${owner}/${repo}/pulls/${args.pull_number}`,
|
|
49358
|
-
{ userId }
|
|
49228
|
+
{ userId: effectiveUserId }
|
|
49359
49229
|
);
|
|
49360
49230
|
return {
|
|
49361
49231
|
success: true,
|
|
@@ -49431,7 +49301,8 @@ NOTE: Very large diffs may be truncated by GitHub. Patch content may be absent f
|
|
|
49431
49301
|
riskLevel: "low",
|
|
49432
49302
|
approvalMessage: `Get PR changed files from GitHub via ${connector.displayName}`
|
|
49433
49303
|
},
|
|
49434
|
-
execute: async (args) => {
|
|
49304
|
+
execute: async (args, context) => {
|
|
49305
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
49435
49306
|
const resolved = resolveRepository(args.repository, connector);
|
|
49436
49307
|
if (!resolved.success) {
|
|
49437
49308
|
return { success: false, error: resolved.error };
|
|
@@ -49442,7 +49313,7 @@ NOTE: Very large diffs may be truncated by GitHub. Patch content may be absent f
|
|
|
49442
49313
|
connector,
|
|
49443
49314
|
`/repos/${owner}/${repo}/pulls/${args.pull_number}/files`,
|
|
49444
49315
|
{
|
|
49445
|
-
userId,
|
|
49316
|
+
userId: effectiveUserId,
|
|
49446
49317
|
queryParams: { per_page: 100 }
|
|
49447
49318
|
}
|
|
49448
49319
|
);
|
|
@@ -49513,7 +49384,8 @@ EXAMPLES:
|
|
|
49513
49384
|
riskLevel: "low",
|
|
49514
49385
|
approvalMessage: `Get PR comments and reviews from GitHub via ${connector.displayName}`
|
|
49515
49386
|
},
|
|
49516
|
-
execute: async (args) => {
|
|
49387
|
+
execute: async (args, context) => {
|
|
49388
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
49517
49389
|
const resolved = resolveRepository(args.repository, connector);
|
|
49518
49390
|
if (!resolved.success) {
|
|
49519
49391
|
return { success: false, error: resolved.error };
|
|
@@ -49521,7 +49393,7 @@ EXAMPLES:
|
|
|
49521
49393
|
const { owner, repo } = resolved.repo;
|
|
49522
49394
|
try {
|
|
49523
49395
|
const basePath = `/repos/${owner}/${repo}`;
|
|
49524
|
-
const queryOpts = { userId, queryParams: { per_page: 100 } };
|
|
49396
|
+
const queryOpts = { userId: effectiveUserId, queryParams: { per_page: 100 } };
|
|
49525
49397
|
const [reviewComments, reviews, issueComments] = await Promise.all([
|
|
49526
49398
|
githubFetch(
|
|
49527
49399
|
connector,
|
|
@@ -49648,7 +49520,8 @@ EXAMPLES:
|
|
|
49648
49520
|
riskLevel: "medium",
|
|
49649
49521
|
approvalMessage: `Create a pull request on GitHub via ${connector.displayName}`
|
|
49650
49522
|
},
|
|
49651
|
-
execute: async (args) => {
|
|
49523
|
+
execute: async (args, context) => {
|
|
49524
|
+
const effectiveUserId = context?.userId ?? userId;
|
|
49652
49525
|
const resolved = resolveRepository(args.repository, connector);
|
|
49653
49526
|
if (!resolved.success) {
|
|
49654
49527
|
return { success: false, error: resolved.error };
|
|
@@ -49660,7 +49533,7 @@ EXAMPLES:
|
|
|
49660
49533
|
`/repos/${owner}/${repo}/pulls`,
|
|
49661
49534
|
{
|
|
49662
49535
|
method: "POST",
|
|
49663
|
-
userId,
|
|
49536
|
+
userId: effectiveUserId,
|
|
49664
49537
|
body: {
|
|
49665
49538
|
title: args.title,
|
|
49666
49539
|
body: args.body,
|
|
@@ -49798,37 +49671,6 @@ var toolRegistry = [
|
|
|
49798
49671
|
description: "Fetch and extract text content from a web page URL.",
|
|
49799
49672
|
tool: webFetch,
|
|
49800
49673
|
safeByDefault: true
|
|
49801
|
-
},
|
|
49802
|
-
{
|
|
49803
|
-
name: "web_fetch_js",
|
|
49804
|
-
exportName: "webFetchJS",
|
|
49805
|
-
displayName: "Web Fetch Js",
|
|
49806
|
-
category: "web",
|
|
49807
|
-
description: "Fetch and extract content from JavaScript-rendered websites using a headless browser (Puppeteer).",
|
|
49808
|
-
tool: webFetchJS,
|
|
49809
|
-
safeByDefault: true
|
|
49810
|
-
},
|
|
49811
|
-
{
|
|
49812
|
-
name: "web_scrape",
|
|
49813
|
-
exportName: "webScrape",
|
|
49814
|
-
displayName: "Web Scrape",
|
|
49815
|
-
category: "web",
|
|
49816
|
-
description: "Scrape any URL with automatic fallback - guaranteed to work on most sites.",
|
|
49817
|
-
tool: webScrape,
|
|
49818
|
-
safeByDefault: true,
|
|
49819
|
-
requiresConnector: true,
|
|
49820
|
-
connectorServiceTypes: ["zenrows"]
|
|
49821
|
-
},
|
|
49822
|
-
{
|
|
49823
|
-
name: "web_search",
|
|
49824
|
-
exportName: "webSearch",
|
|
49825
|
-
displayName: "Web Search",
|
|
49826
|
-
category: "web",
|
|
49827
|
-
description: "Search the web and get relevant results with snippets.",
|
|
49828
|
-
tool: webSearch,
|
|
49829
|
-
safeByDefault: true,
|
|
49830
|
-
requiresConnector: true,
|
|
49831
|
-
connectorServiceTypes: ["serper", "brave-search", "tavily", "rapidapi-websearch"]
|
|
49832
49674
|
}
|
|
49833
49675
|
];
|
|
49834
49676
|
function getAllBuiltInTools() {
|