@cosmicstack/mercury-agent 1.0.6 → 1.1.1
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 +78 -3
- package/README.zh-CN.md +240 -0
- package/dist/index.js +221 -152
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1535,9 +1535,9 @@ var OpenAICompatProvider = class extends BaseProvider {
|
|
|
1535
1535
|
});
|
|
1536
1536
|
return {
|
|
1537
1537
|
text: result.text,
|
|
1538
|
-
inputTokens: result.usage?.
|
|
1539
|
-
outputTokens: result.usage?.
|
|
1540
|
-
totalTokens: (result.usage?.
|
|
1538
|
+
inputTokens: result.usage?.inputTokens ?? 0,
|
|
1539
|
+
outputTokens: result.usage?.outputTokens ?? 0,
|
|
1540
|
+
totalTokens: (result.usage?.inputTokens ?? 0) + (result.usage?.outputTokens ?? 0),
|
|
1541
1541
|
model: this.model,
|
|
1542
1542
|
provider: this.name
|
|
1543
1543
|
};
|
|
@@ -1585,9 +1585,9 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
1585
1585
|
});
|
|
1586
1586
|
return {
|
|
1587
1587
|
text: result.text,
|
|
1588
|
-
inputTokens: result.usage?.
|
|
1589
|
-
outputTokens: result.usage?.
|
|
1590
|
-
totalTokens: (result.usage?.
|
|
1588
|
+
inputTokens: result.usage?.inputTokens ?? 0,
|
|
1589
|
+
outputTokens: result.usage?.outputTokens ?? 0,
|
|
1590
|
+
totalTokens: (result.usage?.inputTokens ?? 0) + (result.usage?.outputTokens ?? 0),
|
|
1591
1591
|
model: this.model,
|
|
1592
1592
|
provider: this.name
|
|
1593
1593
|
};
|
|
@@ -1611,6 +1611,38 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
1611
1611
|
}
|
|
1612
1612
|
};
|
|
1613
1613
|
|
|
1614
|
+
// src/providers/deepseek.ts
|
|
1615
|
+
import { createDeepSeek } from "@ai-sdk/deepseek";
|
|
1616
|
+
var DeepSeekProvider = class extends BaseProvider {
|
|
1617
|
+
name;
|
|
1618
|
+
model;
|
|
1619
|
+
modelInstance;
|
|
1620
|
+
isReasoner;
|
|
1621
|
+
constructor(config) {
|
|
1622
|
+
super(config);
|
|
1623
|
+
this.name = config.name;
|
|
1624
|
+
this.model = config.model;
|
|
1625
|
+
this.isReasoner = config.model === "deepseek-reasoner";
|
|
1626
|
+
const client = createDeepSeek({
|
|
1627
|
+
apiKey: config.apiKey,
|
|
1628
|
+
baseURL: config.baseUrl
|
|
1629
|
+
});
|
|
1630
|
+
this.modelInstance = client(config.model);
|
|
1631
|
+
}
|
|
1632
|
+
async generateText(_prompt, _systemPrompt) {
|
|
1633
|
+
throw new Error("Use getModelInstance() with the AI SDK agent loop");
|
|
1634
|
+
}
|
|
1635
|
+
async *streamText(_prompt, _systemPrompt) {
|
|
1636
|
+
throw new Error("Use getModelInstance() with the AI SDK agent loop");
|
|
1637
|
+
}
|
|
1638
|
+
isAvailable() {
|
|
1639
|
+
return this.config.apiKey.length > 0;
|
|
1640
|
+
}
|
|
1641
|
+
getModelInstance() {
|
|
1642
|
+
return this.modelInstance;
|
|
1643
|
+
}
|
|
1644
|
+
};
|
|
1645
|
+
|
|
1614
1646
|
// src/providers/ollama.ts
|
|
1615
1647
|
import { createOllama } from "ollama-ai-provider";
|
|
1616
1648
|
var OllamaProvider = class extends BaseProvider {
|
|
@@ -1668,6 +1700,8 @@ var ProviderRegistry = class {
|
|
|
1668
1700
|
let provider;
|
|
1669
1701
|
if (pc.name === "anthropic") {
|
|
1670
1702
|
provider = new AnthropicProvider(pc);
|
|
1703
|
+
} else if (pc.name === "deepseek") {
|
|
1704
|
+
provider = new DeepSeekProvider(pc);
|
|
1671
1705
|
} else if (pc.name === "ollamaCloud" || pc.name === "ollamaLocal") {
|
|
1672
1706
|
provider = new OllamaProvider(pc);
|
|
1673
1707
|
} else {
|
|
@@ -1720,7 +1754,7 @@ var ProviderRegistry = class {
|
|
|
1720
1754
|
};
|
|
1721
1755
|
|
|
1722
1756
|
// src/core/agent.ts
|
|
1723
|
-
import { generateText as generateText3, streamText as streamText3 } from "ai";
|
|
1757
|
+
import { generateText as generateText3, streamText as streamText3, stepCountIs } from "ai";
|
|
1724
1758
|
|
|
1725
1759
|
// src/core/lifecycle.ts
|
|
1726
1760
|
var VALID_TRANSITIONS = [
|
|
@@ -3885,6 +3919,7 @@ You can override this:
|
|
|
3885
3919
|
}
|
|
3886
3920
|
for (const provider of fallbackIterator) {
|
|
3887
3921
|
try {
|
|
3922
|
+
const deepseekProviderOptions = provider instanceof DeepSeekProvider && provider.isReasoner ? { deepseek: { thinking: { type: "enabled" } } } : void 0;
|
|
3888
3923
|
logger.info({ provider: provider.name, model: provider.getModel(), steps: MAX_STEPS, stream: canStream }, "Generating agentic response");
|
|
3889
3924
|
if (canStream && channel) {
|
|
3890
3925
|
const streamResult = streamText3({
|
|
@@ -3892,8 +3927,9 @@ You can override this:
|
|
|
3892
3927
|
system: systemPrompt,
|
|
3893
3928
|
messages,
|
|
3894
3929
|
tools: this.capabilities.getTools(),
|
|
3895
|
-
|
|
3930
|
+
stopWhen: stepCountIs(MAX_STEPS),
|
|
3896
3931
|
abortSignal: loopAbortController.signal,
|
|
3932
|
+
...deepseekProviderOptions ? { providerOptions: deepseekProviderOptions } : {},
|
|
3897
3933
|
onStepFinish: async ({ toolCalls, toolResults }) => {
|
|
3898
3934
|
if (toolCalls && toolResults && toolCalls.length > 0) {
|
|
3899
3935
|
const names = toolCalls.map((tc) => tc.toolName).join(", ");
|
|
@@ -3903,7 +3939,7 @@ You can override this:
|
|
|
3903
3939
|
const tr = toolResults[i];
|
|
3904
3940
|
const resultStr = typeof tr?.result === "string" ? tr.result : JSON.stringify(tr?.result ?? "");
|
|
3905
3941
|
const failed = resultStr.length < 5e3 && (resultStr.startsWith("Error:") || resultStr.startsWith("\u26A0") || resultStr.includes("exited with code") || resultStr.includes("Command failed") || resultStr.startsWith("Command exited with code"));
|
|
3906
|
-
loopDetector.record(tc.toolName, tc.
|
|
3942
|
+
loopDetector.record(tc.toolName, tc.input, failed);
|
|
3907
3943
|
}
|
|
3908
3944
|
if (loopDetector.detectAbsoluteLimit()) {
|
|
3909
3945
|
logger.warn("Absolute tool call limit reached \u2014 aborting");
|
|
@@ -3961,7 +3997,7 @@ You can override this:
|
|
|
3961
3997
|
if (channel && msg.channelType !== "internal") {
|
|
3962
3998
|
if (channel instanceof CLIChannel) {
|
|
3963
3999
|
for (const tc of toolCalls) {
|
|
3964
|
-
await channel.sendToolFeedback(tc.toolName, tc.
|
|
4000
|
+
await channel.sendToolFeedback(tc.toolName, tc.input).catch(() => {
|
|
3965
4001
|
});
|
|
3966
4002
|
}
|
|
3967
4003
|
if (toolResults) {
|
|
@@ -3976,7 +4012,7 @@ You can override this:
|
|
|
3976
4012
|
} else if (channel instanceof TelegramChannel) {
|
|
3977
4013
|
const tgCh = channel;
|
|
3978
4014
|
for (const tc of toolCalls) {
|
|
3979
|
-
await tgCh.sendToolFeedback(tc.toolName, tc.
|
|
4015
|
+
await tgCh.sendToolFeedback(tc.toolName, tc.input, msg.channelId).catch(() => {
|
|
3980
4016
|
});
|
|
3981
4017
|
}
|
|
3982
4018
|
if (toolResults) {
|
|
@@ -4042,7 +4078,8 @@ You can override this:
|
|
|
4042
4078
|
const [usage] = await Promise.all([
|
|
4043
4079
|
streamResult.usage
|
|
4044
4080
|
]);
|
|
4045
|
-
|
|
4081
|
+
const streamReasoning = await streamResult.reasoning;
|
|
4082
|
+
result = { text: fullText, usage, reasoning: streamReasoning };
|
|
4046
4083
|
streamedText = fullText;
|
|
4047
4084
|
loopDetector.recordStepText(fullText);
|
|
4048
4085
|
} else {
|
|
@@ -4051,8 +4088,9 @@ You can override this:
|
|
|
4051
4088
|
system: systemPrompt,
|
|
4052
4089
|
messages,
|
|
4053
4090
|
tools: this.capabilities.getTools(),
|
|
4054
|
-
|
|
4091
|
+
stopWhen: stepCountIs(MAX_STEPS),
|
|
4055
4092
|
abortSignal: loopAbortController.signal,
|
|
4093
|
+
...deepseekProviderOptions ? { providerOptions: deepseekProviderOptions } : {},
|
|
4056
4094
|
onStepFinish: async ({ toolCalls, toolResults }) => {
|
|
4057
4095
|
if (toolCalls && toolResults && toolCalls.length > 0) {
|
|
4058
4096
|
const names = toolCalls.map((tc) => tc.toolName).join(", ");
|
|
@@ -4062,7 +4100,7 @@ You can override this:
|
|
|
4062
4100
|
const tr = toolResults[i];
|
|
4063
4101
|
const resultStr = typeof tr?.result === "string" ? tr.result : JSON.stringify(tr?.result ?? "");
|
|
4064
4102
|
const failed = resultStr.length < 5e3 && (resultStr.startsWith("Error:") || resultStr.startsWith("\u26A0") || resultStr.includes("exited with code") || resultStr.includes("Command failed") || resultStr.startsWith("Command exited with code"));
|
|
4065
|
-
loopDetector.record(tc.toolName, tc.
|
|
4103
|
+
loopDetector.record(tc.toolName, tc.input, failed);
|
|
4066
4104
|
}
|
|
4067
4105
|
if (loopDetector.detectAbsoluteLimit()) {
|
|
4068
4106
|
logger.warn("Absolute tool call limit reached \u2014 aborting");
|
|
@@ -4120,7 +4158,7 @@ You can override this:
|
|
|
4120
4158
|
if (channel && msg.channelType !== "internal") {
|
|
4121
4159
|
if (channel instanceof CLIChannel) {
|
|
4122
4160
|
for (const tc of toolCalls) {
|
|
4123
|
-
await channel.sendToolFeedback(tc.toolName, tc.
|
|
4161
|
+
await channel.sendToolFeedback(tc.toolName, tc.input).catch(() => {
|
|
4124
4162
|
});
|
|
4125
4163
|
}
|
|
4126
4164
|
if (toolResults) {
|
|
@@ -4135,7 +4173,7 @@ You can override this:
|
|
|
4135
4173
|
} else if (channel instanceof TelegramChannel) {
|
|
4136
4174
|
const tgCh = channel;
|
|
4137
4175
|
for (const tc of toolCalls) {
|
|
4138
|
-
await tgCh.sendToolFeedback(tc.toolName, tc.
|
|
4176
|
+
await tgCh.sendToolFeedback(tc.toolName, tc.input, msg.channelId).catch(() => {
|
|
4139
4177
|
});
|
|
4140
4178
|
}
|
|
4141
4179
|
if (toolResults) {
|
|
@@ -4221,9 +4259,9 @@ You can override this:
|
|
|
4221
4259
|
this.tokenBudget.recordUsage({
|
|
4222
4260
|
provider: usedProvider.name,
|
|
4223
4261
|
model: usedProvider.model,
|
|
4224
|
-
inputTokens: result.usage?.
|
|
4225
|
-
outputTokens: result.usage?.
|
|
4226
|
-
totalTokens: (result.usage?.
|
|
4262
|
+
inputTokens: result.usage?.inputTokens ?? 0,
|
|
4263
|
+
outputTokens: result.usage?.outputTokens ?? 0,
|
|
4264
|
+
totalTokens: (result.usage?.inputTokens ?? 0) + (result.usage?.outputTokens ?? 0),
|
|
4227
4265
|
channelType: msg.channelType
|
|
4228
4266
|
});
|
|
4229
4267
|
this.shortTerm.add(msg.channelId, {
|
|
@@ -4237,7 +4275,8 @@ You can override this:
|
|
|
4237
4275
|
timestamp: Date.now(),
|
|
4238
4276
|
role: "assistant",
|
|
4239
4277
|
content: finalText,
|
|
4240
|
-
tokenCount: (result.usage?.
|
|
4278
|
+
tokenCount: (result.usage?.inputTokens ?? 0) + (result.usage?.outputTokens ?? 0),
|
|
4279
|
+
reasoning: result.reasoning || void 0
|
|
4241
4280
|
});
|
|
4242
4281
|
this.episodic.record({
|
|
4243
4282
|
type: "message",
|
|
@@ -4433,14 +4472,14 @@ All actions auto-approved for this run.`,
|
|
|
4433
4472
|
{ role: "user", content: `User: ${userMessage}
|
|
4434
4473
|
Assistant: ${agentResponse}` }
|
|
4435
4474
|
],
|
|
4436
|
-
|
|
4475
|
+
maxOutputTokens: 400
|
|
4437
4476
|
});
|
|
4438
4477
|
this.tokenBudget.recordUsage({
|
|
4439
4478
|
provider: provider.name,
|
|
4440
4479
|
model: provider.getModel(),
|
|
4441
|
-
inputTokens: result.usage?.
|
|
4442
|
-
outputTokens: result.usage?.
|
|
4443
|
-
totalTokens: (result.usage?.
|
|
4480
|
+
inputTokens: result.usage?.inputTokens ?? 0,
|
|
4481
|
+
outputTokens: result.usage?.outputTokens ?? 0,
|
|
4482
|
+
totalTokens: (result.usage?.inputTokens ?? 0) + (result.usage?.outputTokens ?? 0),
|
|
4444
4483
|
channelType: "internal"
|
|
4445
4484
|
});
|
|
4446
4485
|
const text = result.text.trim();
|
|
@@ -4537,6 +4576,27 @@ Or use /budget override, /budget reset, /budget set <number> anytime.`,
|
|
|
4537
4576
|
await channel.send(ctx.manual(), channelId);
|
|
4538
4577
|
return true;
|
|
4539
4578
|
}
|
|
4579
|
+
if (cmd === "/exit" || cmd === "/quit") {
|
|
4580
|
+
await channel.send("Goodbye! Shutting down Mercury...", channelId);
|
|
4581
|
+
this.shutdown();
|
|
4582
|
+
return true;
|
|
4583
|
+
}
|
|
4584
|
+
if (cmd === "/permissions") {
|
|
4585
|
+
if (channelType === "cli" && channel instanceof CLIChannel) {
|
|
4586
|
+
const mode = await channel.askPermissionMode?.();
|
|
4587
|
+
if (mode === "allow-all") {
|
|
4588
|
+
this.capabilities.permissions.setAutoApproveAll(true);
|
|
4589
|
+
this.capabilities.permissions.addTempScope("/", true, true);
|
|
4590
|
+
await channel.send("Allow All mode active for this session. All scopes, commands, and loops auto-approved. Resets on restart.", channelId);
|
|
4591
|
+
} else {
|
|
4592
|
+
this.capabilities.permissions.setAutoApproveAll(false);
|
|
4593
|
+
await channel.send("Ask Me mode active. Risky actions will prompt for confirmation.", channelId);
|
|
4594
|
+
}
|
|
4595
|
+
return true;
|
|
4596
|
+
}
|
|
4597
|
+
await channel.send("Use /permissions in CLI to switch permission mode. On Telegram, use the /permissions button or command.", channelId);
|
|
4598
|
+
return true;
|
|
4599
|
+
}
|
|
4540
4600
|
if (cmd === "/status") {
|
|
4541
4601
|
const config = ctx.config();
|
|
4542
4602
|
const budget = ctx.tokenBudget();
|
|
@@ -4789,9 +4849,11 @@ Or use /budget override, /budget reset, /budget set <number> anytime.`,
|
|
|
4789
4849
|
await channel.withMenu(async (select) => {
|
|
4790
4850
|
while (true) {
|
|
4791
4851
|
const streamLabel = this.telegramStreaming ? "Disable Telegram Streaming" : "Enable Telegram Streaming";
|
|
4852
|
+
const permLabel = this.capabilities.permissions.isAutoApproveAll() ? "Switch to Ask Me" : "Switch to Allow All";
|
|
4792
4853
|
const action = await select("Mercury Commands", [
|
|
4793
4854
|
{ value: "status", label: "Status" },
|
|
4794
4855
|
{ value: "memory", label: "Memory" },
|
|
4856
|
+
{ value: "permissions", label: permLabel },
|
|
4795
4857
|
{ value: "telegram", label: "Telegram" },
|
|
4796
4858
|
{ value: "tools", label: "Tools" },
|
|
4797
4859
|
{ value: "skills", label: "Skills" },
|
|
@@ -4814,6 +4876,10 @@ Or use /budget override, /budget reset, /budget set <number> anytime.`,
|
|
|
4814
4876
|
}
|
|
4815
4877
|
continue;
|
|
4816
4878
|
}
|
|
4879
|
+
if (action === "permissions") {
|
|
4880
|
+
await this.handleChatCommand("/permissions", "cli", channelId);
|
|
4881
|
+
continue;
|
|
4882
|
+
}
|
|
4817
4883
|
if (action === "telegram") {
|
|
4818
4884
|
await this.openCliTelegramMenu(channel, channelId, select);
|
|
4819
4885
|
continue;
|
|
@@ -5321,6 +5387,11 @@ var ChannelRegistry = class {
|
|
|
5321
5387
|
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
5322
5388
|
import { join as join7 } from "path";
|
|
5323
5389
|
var TOKEN_FILE = "token-usage.json";
|
|
5390
|
+
function safeNumber(value) {
|
|
5391
|
+
if (value === null || value === void 0) return 0;
|
|
5392
|
+
const n = Number(value);
|
|
5393
|
+
return isNaN(n) ? 0 : n;
|
|
5394
|
+
}
|
|
5324
5395
|
var TokenBudget = class {
|
|
5325
5396
|
constructor(config) {
|
|
5326
5397
|
this.config = config;
|
|
@@ -5336,7 +5407,7 @@ var TokenBudget = class {
|
|
|
5336
5407
|
forceNext = false;
|
|
5337
5408
|
canAfford(estimatedTokens) {
|
|
5338
5409
|
this.resetIfNewDay();
|
|
5339
|
-
return this.dailyUsed + estimatedTokens <= this.dailyBudget;
|
|
5410
|
+
return safeNumber(this.dailyUsed) + estimatedTokens <= safeNumber(this.dailyBudget);
|
|
5340
5411
|
}
|
|
5341
5412
|
isOverBudget() {
|
|
5342
5413
|
this.resetIfNewDay();
|
|
@@ -5344,7 +5415,7 @@ var TokenBudget = class {
|
|
|
5344
5415
|
this.forceNext = false;
|
|
5345
5416
|
return false;
|
|
5346
5417
|
}
|
|
5347
|
-
return this.dailyUsed >= this.dailyBudget;
|
|
5418
|
+
return safeNumber(this.dailyUsed) >= safeNumber(this.dailyBudget);
|
|
5348
5419
|
}
|
|
5349
5420
|
forceAllowNext() {
|
|
5350
5421
|
this.forceNext = true;
|
|
@@ -5372,18 +5443,24 @@ var TokenBudget = class {
|
|
|
5372
5443
|
}
|
|
5373
5444
|
recordUsage(entry) {
|
|
5374
5445
|
this.resetIfNewDay();
|
|
5375
|
-
const
|
|
5376
|
-
|
|
5446
|
+
const inputTokens = safeNumber(entry.inputTokens);
|
|
5447
|
+
const outputTokens = safeNumber(entry.outputTokens);
|
|
5448
|
+
const totalTokens = safeNumber(entry.totalTokens) || inputTokens + outputTokens;
|
|
5449
|
+
const safeEntry = { ...entry, inputTokens, outputTokens, totalTokens };
|
|
5450
|
+
const logEntry = { ...safeEntry, timestamp: Date.now() };
|
|
5451
|
+
this.dailyUsed += totalTokens;
|
|
5377
5452
|
this.requestLog.push(logEntry);
|
|
5378
5453
|
this.persist();
|
|
5379
5454
|
}
|
|
5380
5455
|
getRemaining() {
|
|
5381
5456
|
this.resetIfNewDay();
|
|
5382
|
-
return Math.max(0, this.dailyBudget - this.dailyUsed);
|
|
5457
|
+
return Math.max(0, safeNumber(this.dailyBudget) - safeNumber(this.dailyUsed));
|
|
5383
5458
|
}
|
|
5384
5459
|
getUsagePercentage() {
|
|
5385
5460
|
this.resetIfNewDay();
|
|
5386
|
-
|
|
5461
|
+
const budget = safeNumber(this.dailyBudget);
|
|
5462
|
+
const used = safeNumber(this.dailyUsed);
|
|
5463
|
+
return budget > 0 ? used / budget * 100 : 0;
|
|
5387
5464
|
}
|
|
5388
5465
|
getStatusText() {
|
|
5389
5466
|
const pct = Math.round(this.getUsagePercentage());
|
|
@@ -5422,8 +5499,16 @@ var TokenBudget = class {
|
|
|
5422
5499
|
const data = JSON.parse(raw);
|
|
5423
5500
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
5424
5501
|
if (data.lastResetDate === today) {
|
|
5425
|
-
|
|
5426
|
-
|
|
5502
|
+
const restored = safeNumber(data.dailyUsed);
|
|
5503
|
+
if (!isNaN(restored)) {
|
|
5504
|
+
this.dailyUsed = restored;
|
|
5505
|
+
}
|
|
5506
|
+
this.requestLog = (data.requestLog ?? []).map((entry) => ({
|
|
5507
|
+
...entry,
|
|
5508
|
+
inputTokens: safeNumber(entry.inputTokens),
|
|
5509
|
+
outputTokens: safeNumber(entry.outputTokens),
|
|
5510
|
+
totalTokens: safeNumber(entry.totalTokens)
|
|
5511
|
+
}));
|
|
5427
5512
|
}
|
|
5428
5513
|
this.lastResetDate = data.lastResetDate ?? today;
|
|
5429
5514
|
} catch (err) {
|
|
@@ -5548,7 +5633,6 @@ var PermissionManager = class {
|
|
|
5548
5633
|
askHandler;
|
|
5549
5634
|
autoApproveAll = false;
|
|
5550
5635
|
elevatedCommands = /* @__PURE__ */ new Set();
|
|
5551
|
-
pendingApprovals = /* @__PURE__ */ new Set();
|
|
5552
5636
|
currentChannelType = "cli";
|
|
5553
5637
|
tempScopes = [];
|
|
5554
5638
|
constructor() {
|
|
@@ -5591,12 +5675,6 @@ var PermissionManager = class {
|
|
|
5591
5675
|
isShellElevated() {
|
|
5592
5676
|
return this.elevatedCommands.has("run_command");
|
|
5593
5677
|
}
|
|
5594
|
-
addPendingApproval(baseCommand) {
|
|
5595
|
-
this.pendingApprovals.add(baseCommand);
|
|
5596
|
-
}
|
|
5597
|
-
clearPendingApprovals() {
|
|
5598
|
-
this.pendingApprovals.clear();
|
|
5599
|
-
}
|
|
5600
5678
|
load() {
|
|
5601
5679
|
if (existsSync7(PERMISSIONS_FILE)) {
|
|
5602
5680
|
try {
|
|
@@ -5655,6 +5733,9 @@ var PermissionManager = class {
|
|
|
5655
5733
|
if (mode === "write" && tempScope.write) return { allowed: true };
|
|
5656
5734
|
return { allowed: false, reason: `Permission denied: ${mode} access to ${path3}` };
|
|
5657
5735
|
}
|
|
5736
|
+
if (!this.autoApproveAll && this.askHandler && this.currentChannelType !== "internal") {
|
|
5737
|
+
return this.requestScopeExternal(path3, mode);
|
|
5738
|
+
}
|
|
5658
5739
|
return { allowed: false, reason: `Permission denied for ${mode} access to ${path3}` };
|
|
5659
5740
|
}
|
|
5660
5741
|
async checkShellCommand(command) {
|
|
@@ -5672,10 +5753,6 @@ var PermissionManager = class {
|
|
|
5672
5753
|
}
|
|
5673
5754
|
const trimmed = command.trim();
|
|
5674
5755
|
const baseCmd = trimmed.split(/\s+/)[0];
|
|
5675
|
-
if (this.pendingApprovals.has(baseCmd)) {
|
|
5676
|
-
logger.info({ cmd: trimmed }, "Shell command auto-approved (pending approval)");
|
|
5677
|
-
return { allowed: true, needsApproval: false };
|
|
5678
|
-
}
|
|
5679
5756
|
for (const pattern of shell.blocked) {
|
|
5680
5757
|
if (this.matchPattern(trimmed, pattern)) {
|
|
5681
5758
|
return { allowed: false, reason: `Blocked command: matches "${pattern}"`, needsApproval: false };
|
|
@@ -5698,7 +5775,7 @@ var PermissionManager = class {
|
|
|
5698
5775
|
}
|
|
5699
5776
|
for (const pattern of shell.needsApproval) {
|
|
5700
5777
|
if (this.matchPattern(trimmed, pattern)) {
|
|
5701
|
-
if (this.currentChannelType
|
|
5778
|
+
if (this.askHandler && this.currentChannelType !== "internal") {
|
|
5702
5779
|
const result = await this.askHandler(`Run command: ${trimmed}`);
|
|
5703
5780
|
if (result === "yes") {
|
|
5704
5781
|
return { allowed: true, needsApproval: false };
|
|
@@ -5712,7 +5789,7 @@ var PermissionManager = class {
|
|
|
5712
5789
|
return { allowed: false, reason: `Command requires approval: matches "${pattern}"`, needsApproval: true };
|
|
5713
5790
|
}
|
|
5714
5791
|
}
|
|
5715
|
-
if (this.currentChannelType
|
|
5792
|
+
if (this.askHandler && this.currentChannelType !== "internal") {
|
|
5716
5793
|
const result = await this.askHandler(`Run command: ${trimmed}`);
|
|
5717
5794
|
if (result === "yes") {
|
|
5718
5795
|
return { allowed: true, needsApproval: false };
|
|
@@ -5818,6 +5895,11 @@ Allow access?`;
|
|
|
5818
5895
|
return null;
|
|
5819
5896
|
}
|
|
5820
5897
|
mergeDefaults(parsed) {
|
|
5898
|
+
const mergeArray = (existing, defaults) => {
|
|
5899
|
+
if (!existing) return [...defaults];
|
|
5900
|
+
const combined = /* @__PURE__ */ new Set([...defaults, ...existing]);
|
|
5901
|
+
return [...combined];
|
|
5902
|
+
};
|
|
5821
5903
|
return {
|
|
5822
5904
|
capabilities: {
|
|
5823
5905
|
filesystem: {
|
|
@@ -5826,9 +5908,9 @@ Allow access?`;
|
|
|
5826
5908
|
},
|
|
5827
5909
|
shell: {
|
|
5828
5910
|
enabled: parsed.capabilities?.shell?.enabled ?? DEFAULT_MANIFEST.capabilities.shell.enabled,
|
|
5829
|
-
blocked: parsed.capabilities?.shell?.blocked
|
|
5830
|
-
autoApproved: parsed.capabilities?.shell?.autoApproved
|
|
5831
|
-
needsApproval: parsed.capabilities?.shell?.needsApproval
|
|
5911
|
+
blocked: mergeArray(parsed.capabilities?.shell?.blocked, DEFAULT_MANIFEST.capabilities.shell.blocked),
|
|
5912
|
+
autoApproved: mergeArray(parsed.capabilities?.shell?.autoApproved, DEFAULT_MANIFEST.capabilities.shell.autoApproved),
|
|
5913
|
+
needsApproval: mergeArray(parsed.capabilities?.shell?.needsApproval, DEFAULT_MANIFEST.capabilities.shell.needsApproval),
|
|
5832
5914
|
cwdOnly: parsed.capabilities?.shell?.cwdOnly ?? DEFAULT_MANIFEST.capabilities.shell.cwdOnly
|
|
5833
5915
|
},
|
|
5834
5916
|
git: {
|
|
@@ -5842,16 +5924,16 @@ Allow access?`;
|
|
|
5842
5924
|
};
|
|
5843
5925
|
|
|
5844
5926
|
// src/capabilities/filesystem/read-file.ts
|
|
5845
|
-
import { tool } from "ai";
|
|
5927
|
+
import { tool, zodSchema } from "ai";
|
|
5846
5928
|
import { z } from "zod";
|
|
5847
5929
|
import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
|
|
5848
5930
|
import { resolve as resolve4, isAbsolute } from "path";
|
|
5849
5931
|
function createReadFileTool(permissions, getCwd) {
|
|
5850
5932
|
return tool({
|
|
5851
5933
|
description: "Read the contents of a file. The path must be within an allowed scope.",
|
|
5852
|
-
|
|
5934
|
+
inputSchema: zodSchema(z.object({
|
|
5853
5935
|
path: z.string().describe("Absolute or relative path to the file")
|
|
5854
|
-
}),
|
|
5936
|
+
})),
|
|
5855
5937
|
execute: async ({ path: path3 }) => {
|
|
5856
5938
|
const resolved = isAbsolute(path3) ? resolve4(path3) : resolve4(getCwd(), path3);
|
|
5857
5939
|
const check = await permissions.checkFsAccess(resolved, "read");
|
|
@@ -5879,17 +5961,17 @@ function createReadFileTool(permissions, getCwd) {
|
|
|
5879
5961
|
}
|
|
5880
5962
|
|
|
5881
5963
|
// src/capabilities/filesystem/write-file.ts
|
|
5882
|
-
import { tool as tool2 } from "ai";
|
|
5964
|
+
import { tool as tool2, zodSchema as zodSchema2 } from "ai";
|
|
5883
5965
|
import { z as z2 } from "zod";
|
|
5884
5966
|
import { existsSync as existsSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
5885
5967
|
import { resolve as resolve5, isAbsolute as isAbsolute2 } from "path";
|
|
5886
5968
|
function createWriteFileTool(permissions, getCwd) {
|
|
5887
5969
|
return tool2({
|
|
5888
5970
|
description: "Write content to an existing file. The path must be within a writable scope.",
|
|
5889
|
-
|
|
5971
|
+
inputSchema: zodSchema2(z2.object({
|
|
5890
5972
|
path: z2.string().describe("Absolute or relative path to the file"),
|
|
5891
5973
|
content: z2.string().describe("The content to write to the file")
|
|
5892
|
-
}),
|
|
5974
|
+
})),
|
|
5893
5975
|
execute: async ({ path: path3, content }) => {
|
|
5894
5976
|
const resolved = isAbsolute2(path3) ? resolve5(path3) : resolve5(getCwd(), path3);
|
|
5895
5977
|
const check = await permissions.checkFsAccess(resolved, "write");
|
|
@@ -5911,17 +5993,17 @@ function createWriteFileTool(permissions, getCwd) {
|
|
|
5911
5993
|
}
|
|
5912
5994
|
|
|
5913
5995
|
// src/capabilities/filesystem/create-file.ts
|
|
5914
|
-
import { tool as tool3 } from "ai";
|
|
5996
|
+
import { tool as tool3, zodSchema as zodSchema3 } from "ai";
|
|
5915
5997
|
import { z as z3 } from "zod";
|
|
5916
5998
|
import { existsSync as existsSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8 } from "fs";
|
|
5917
5999
|
import { resolve as resolve6, dirname as dirname3, isAbsolute as isAbsolute3 } from "path";
|
|
5918
6000
|
function createCreateFileTool(permissions, getCwd) {
|
|
5919
6001
|
return tool3({
|
|
5920
6002
|
description: "Create a new file with the given content. Also creates parent directories if needed. The path must be within a writable scope.",
|
|
5921
|
-
|
|
6003
|
+
inputSchema: zodSchema3(z3.object({
|
|
5922
6004
|
path: z3.string().describe("Absolute or relative path for the new file"),
|
|
5923
6005
|
content: z3.string().describe("The content of the new file")
|
|
5924
|
-
}),
|
|
6006
|
+
})),
|
|
5925
6007
|
execute: async ({ path: path3, content }) => {
|
|
5926
6008
|
const resolved = isAbsolute3(path3) ? resolve6(path3) : resolve6(getCwd(), path3);
|
|
5927
6009
|
const check = await permissions.checkFsAccess(resolved, "write");
|
|
@@ -5947,16 +6029,16 @@ function createCreateFileTool(permissions, getCwd) {
|
|
|
5947
6029
|
}
|
|
5948
6030
|
|
|
5949
6031
|
// src/capabilities/filesystem/list-dir.ts
|
|
5950
|
-
import { tool as tool4 } from "ai";
|
|
6032
|
+
import { tool as tool4, zodSchema as zodSchema4 } from "ai";
|
|
5951
6033
|
import { z as z4 } from "zod";
|
|
5952
6034
|
import { existsSync as existsSync11, readdirSync as readdirSync2, statSync } from "fs";
|
|
5953
6035
|
import { resolve as resolve7, isAbsolute as isAbsolute4, join as join9 } from "path";
|
|
5954
6036
|
function createListDirTool(permissions, getCwd) {
|
|
5955
6037
|
return tool4({
|
|
5956
6038
|
description: "List the contents of a directory. Shows file names, types, and sizes.",
|
|
5957
|
-
|
|
6039
|
+
inputSchema: zodSchema4(z4.object({
|
|
5958
6040
|
path: z4.string().describe("Absolute or relative path to the directory")
|
|
5959
|
-
}),
|
|
6041
|
+
})),
|
|
5960
6042
|
execute: async ({ path: path3 }) => {
|
|
5961
6043
|
const resolved = isAbsolute4(path3) ? resolve7(path3) : resolve7(getCwd(), path3);
|
|
5962
6044
|
const check = await permissions.checkFsAccess(resolved, "read");
|
|
@@ -6002,16 +6084,16 @@ function formatSize(bytes) {
|
|
|
6002
6084
|
}
|
|
6003
6085
|
|
|
6004
6086
|
// src/capabilities/filesystem/delete-file.ts
|
|
6005
|
-
import { tool as tool5 } from "ai";
|
|
6087
|
+
import { tool as tool5, zodSchema as zodSchema5 } from "ai";
|
|
6006
6088
|
import { z as z5 } from "zod";
|
|
6007
6089
|
import { existsSync as existsSync12, unlinkSync as unlinkSync2 } from "fs";
|
|
6008
6090
|
import { resolve as resolve8, isAbsolute as isAbsolute5 } from "path";
|
|
6009
6091
|
function createDeleteFileTool(permissions, getCwd) {
|
|
6010
6092
|
return tool5({
|
|
6011
6093
|
description: "Delete a file. This action cannot be undone. The path must be within a writable scope. Always asks for confirmation.",
|
|
6012
|
-
|
|
6094
|
+
inputSchema: zodSchema5(z5.object({
|
|
6013
6095
|
path: z5.string().describe("Absolute or relative path to the file to delete")
|
|
6014
|
-
}),
|
|
6096
|
+
})),
|
|
6015
6097
|
execute: async ({ path: path3 }) => {
|
|
6016
6098
|
const resolved = isAbsolute5(path3) ? resolve8(path3) : resolve8(getCwd(), path3);
|
|
6017
6099
|
const check = await permissions.checkFsAccess(resolved, "write");
|
|
@@ -6037,18 +6119,18 @@ function createDeleteFileTool(permissions, getCwd) {
|
|
|
6037
6119
|
}
|
|
6038
6120
|
|
|
6039
6121
|
// src/capabilities/filesystem/edit-file.ts
|
|
6040
|
-
import { tool as tool6 } from "ai";
|
|
6122
|
+
import { tool as tool6, zodSchema as zodSchema6 } from "ai";
|
|
6041
6123
|
import { z as z6 } from "zod";
|
|
6042
6124
|
import { readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
6043
6125
|
import { resolve as resolve9, isAbsolute as isAbsolute6 } from "path";
|
|
6044
6126
|
function createEditFileTool(permissions, getCwd) {
|
|
6045
6127
|
return tool6({
|
|
6046
6128
|
description: "Edit a file by replacing an exact string match with new content. Use this instead of write_file when you only need to change part of a file. The old_string must match exactly (including whitespace and indentation). Fails if old_string is not found or found multiple times.",
|
|
6047
|
-
|
|
6129
|
+
inputSchema: zodSchema6(z6.object({
|
|
6048
6130
|
path: z6.string().describe("Absolute or relative path to the file"),
|
|
6049
6131
|
old_string: z6.string().describe("The exact text to find in the file (must match exactly)"),
|
|
6050
6132
|
new_string: z6.string().describe("The text to replace it with")
|
|
6051
|
-
}),
|
|
6133
|
+
})),
|
|
6052
6134
|
execute: async ({ path: path3, old_string, new_string }) => {
|
|
6053
6135
|
const resolved = isAbsolute6(path3) ? resolve9(path3) : resolve9(getCwd(), path3);
|
|
6054
6136
|
const fsCheck = await permissions.checkFsAccess(resolved, "write");
|
|
@@ -6081,16 +6163,16 @@ function createEditFileTool(permissions, getCwd) {
|
|
|
6081
6163
|
}
|
|
6082
6164
|
|
|
6083
6165
|
// src/capabilities/filesystem/send-file.ts
|
|
6084
|
-
import { tool as tool7 } from "ai";
|
|
6166
|
+
import { tool as tool7, zodSchema as zodSchema7 } from "ai";
|
|
6085
6167
|
import { z as z7 } from "zod";
|
|
6086
6168
|
import { existsSync as existsSync13, statSync as statSync2 } from "fs";
|
|
6087
6169
|
import { resolve as resolve10, basename as basename2, isAbsolute as isAbsolute7 } from "path";
|
|
6088
6170
|
function createSendFileTool(permissions, getCwd, sendFile) {
|
|
6089
6171
|
return tool7({
|
|
6090
6172
|
description: "Send a file to the user. On Telegram the file is uploaded as an attachment to the relevant approved recipients. On CLI the file path and size are displayed. The path must be within an allowed read scope.",
|
|
6091
|
-
|
|
6173
|
+
inputSchema: zodSchema7(z7.object({
|
|
6092
6174
|
path: z7.string().describe("Absolute or relative path to the file to send")
|
|
6093
|
-
}),
|
|
6175
|
+
})),
|
|
6094
6176
|
execute: async ({ path: path3 }) => {
|
|
6095
6177
|
const resolved = isAbsolute7(path3) ? resolve10(path3) : resolve10(getCwd(), path3);
|
|
6096
6178
|
const check = await permissions.checkFsAccess(resolved, "read");
|
|
@@ -6121,14 +6203,14 @@ function createSendFileTool(permissions, getCwd, sendFile) {
|
|
|
6121
6203
|
}
|
|
6122
6204
|
|
|
6123
6205
|
// src/capabilities/messaging/send-message.ts
|
|
6124
|
-
import { tool as tool8 } from "ai";
|
|
6206
|
+
import { tool as tool8, zodSchema as zodSchema8 } from "ai";
|
|
6125
6207
|
import { z as z8 } from "zod";
|
|
6126
6208
|
function createSendMessageTool(sendMessage) {
|
|
6127
6209
|
return tool8({
|
|
6128
6210
|
description: "Send a message through the configured outbound channel. For Telegram this sends to the approved Telegram recipients. Use this only when the user explicitly asks you to send something to Telegram or asks for scheduled results to be sent there.",
|
|
6129
|
-
|
|
6211
|
+
inputSchema: zodSchema8(z8.object({
|
|
6130
6212
|
content: z8.string().describe("The message content to send to the approved Telegram recipients")
|
|
6131
|
-
}),
|
|
6213
|
+
})),
|
|
6132
6214
|
execute: async ({ content }) => {
|
|
6133
6215
|
const trimmed = content.trim();
|
|
6134
6216
|
if (!trimmed) {
|
|
@@ -6145,16 +6227,16 @@ function createSendMessageTool(sendMessage) {
|
|
|
6145
6227
|
}
|
|
6146
6228
|
|
|
6147
6229
|
// src/capabilities/filesystem/approve-scope.ts
|
|
6148
|
-
import { tool as tool9 } from "ai";
|
|
6230
|
+
import { tool as tool9, zodSchema as zodSchema9 } from "ai";
|
|
6149
6231
|
import { z as z9 } from "zod";
|
|
6150
6232
|
import { resolve as resolve11, isAbsolute as isAbsolute8 } from "path";
|
|
6151
6233
|
function createApproveScopeTool(permissions, getCwd) {
|
|
6152
6234
|
return tool9({
|
|
6153
6235
|
description: 'Request user approval to access a directory outside current scopes. Use this when a file tool returns a permission denied error. The user gets an approval prompt (Allow/Always/Deny buttons on Telegram, yes/always/no on CLI). "Allow" grants session-only access. "Always" persists to disk. After approval, retry the original file operation.',
|
|
6154
|
-
|
|
6236
|
+
inputSchema: zodSchema9(z9.object({
|
|
6155
6237
|
path: z9.string().describe("The directory path to request access to"),
|
|
6156
6238
|
mode: z9.enum(["read", "write"]).describe("The access mode needed")
|
|
6157
|
-
}),
|
|
6239
|
+
})),
|
|
6158
6240
|
execute: async ({ path: path3, mode }) => {
|
|
6159
6241
|
const resolved = isAbsolute8(path3) ? resolve11(path3) : resolve11(getCwd(), path3);
|
|
6160
6242
|
const result = await permissions.requestScopeExternal(resolved, mode);
|
|
@@ -6167,7 +6249,7 @@ function createApproveScopeTool(permissions, getCwd) {
|
|
|
6167
6249
|
}
|
|
6168
6250
|
|
|
6169
6251
|
// src/capabilities/shell/run-command.ts
|
|
6170
|
-
import { tool as tool10 } from "ai";
|
|
6252
|
+
import { tool as tool10, zodSchema as zodSchema10 } from "ai";
|
|
6171
6253
|
import { z as z10 } from "zod";
|
|
6172
6254
|
import { execSync } from "child_process";
|
|
6173
6255
|
import { resolve as resolve12, isAbsolute as isAbsolute9 } from "path";
|
|
@@ -6178,20 +6260,13 @@ function createRunCommandTool(permissions, getCwd, setCwd) {
|
|
|
6178
6260
|
description: `Run a shell command in the current working directory. Use the cd tool to change directories first \u2014 cd commands within this tool only affect chained commands (e.g., "cd /path && ls"), not subsequent calls.
|
|
6179
6261
|
Blocked commands (sudo, rm -rf /, etc.) are never executed.
|
|
6180
6262
|
Auto-approved commands (ls, cat, git status, curl, etc.) run without asking.
|
|
6181
|
-
Other commands
|
|
6182
|
-
|
|
6263
|
+
Other commands prompt the user for approval before execution.`,
|
|
6264
|
+
inputSchema: zodSchema10(z10.object({
|
|
6183
6265
|
command: z10.string().describe("The shell command to execute")
|
|
6184
|
-
}),
|
|
6266
|
+
})),
|
|
6185
6267
|
execute: async ({ command }) => {
|
|
6186
6268
|
const check = await permissions.checkShellCommand(command);
|
|
6187
6269
|
if (!check.allowed) {
|
|
6188
|
-
if (check.needsApproval) {
|
|
6189
|
-
const baseCmd = command.trim().split(/\s+/)[0];
|
|
6190
|
-
permissions.addPendingApproval(baseCmd);
|
|
6191
|
-
return `\u26A0 Command requires approval: ${command}
|
|
6192
|
-
|
|
6193
|
-
Tell the user what this command does and ask for permission. If they approve, try running it again. If they say "always", use the approve_command tool to permanently approve this command type.`;
|
|
6194
|
-
}
|
|
6195
6270
|
return `Error: ${check.reason}`;
|
|
6196
6271
|
}
|
|
6197
6272
|
const cwd = getCwd();
|
|
@@ -6245,16 +6320,16 @@ function detectCd(command, currentCwd, setCwd) {
|
|
|
6245
6320
|
}
|
|
6246
6321
|
|
|
6247
6322
|
// src/capabilities/shell/cd.ts
|
|
6248
|
-
import { tool as tool11 } from "ai";
|
|
6323
|
+
import { tool as tool11, zodSchema as zodSchema11 } from "ai";
|
|
6249
6324
|
import { z as z11 } from "zod";
|
|
6250
6325
|
import { resolve as resolve13, isAbsolute as isAbsolute10 } from "path";
|
|
6251
6326
|
import { existsSync as existsSync15, statSync as statSync3 } from "fs";
|
|
6252
6327
|
function createCdTool(getCwd, setCwd) {
|
|
6253
6328
|
return tool11({
|
|
6254
6329
|
description: "Change the current working directory. All subsequent file operations, shell commands, and git operations will use this directory. Use this before running commands in a specific project folder.",
|
|
6255
|
-
|
|
6330
|
+
inputSchema: zodSchema11(z11.object({
|
|
6256
6331
|
path: z11.string().describe("The directory to change to. Can be absolute or relative to the current directory.")
|
|
6257
|
-
}),
|
|
6332
|
+
})),
|
|
6258
6333
|
execute: async ({ path: path3 }) => {
|
|
6259
6334
|
const cwd = getCwd();
|
|
6260
6335
|
const resolved = isAbsolute10(path3) ? resolve13(path3) : resolve13(cwd, path3);
|
|
@@ -6276,14 +6351,14 @@ function createCdTool(getCwd, setCwd) {
|
|
|
6276
6351
|
}
|
|
6277
6352
|
|
|
6278
6353
|
// src/capabilities/shell/approve-command.ts
|
|
6279
|
-
import { tool as tool12 } from "ai";
|
|
6354
|
+
import { tool as tool12, zodSchema as zodSchema12 } from "ai";
|
|
6280
6355
|
import { z as z12 } from "zod";
|
|
6281
6356
|
function createApproveCommandTool(permissions) {
|
|
6282
6357
|
return tool12({
|
|
6283
6358
|
description: 'Permanently approve a command type so it runs without asking in the future. Use this when the user says "always" or "always approve" for a command. For example, if the user says "always approve curl", call this with command="curl".',
|
|
6284
|
-
|
|
6359
|
+
inputSchema: zodSchema12(z12.object({
|
|
6285
6360
|
command: z12.string().describe('The base command to permanently approve (e.g. "curl", "docker", "npm")')
|
|
6286
|
-
}),
|
|
6361
|
+
})),
|
|
6287
6362
|
execute: async ({ command }) => {
|
|
6288
6363
|
const baseCmd = command.trim().split(/\s+/)[0];
|
|
6289
6364
|
permissions.addApprovedCommand(baseCmd);
|
|
@@ -6293,16 +6368,16 @@ function createApproveCommandTool(permissions) {
|
|
|
6293
6368
|
}
|
|
6294
6369
|
|
|
6295
6370
|
// src/capabilities/skills/install-skill.ts
|
|
6296
|
-
import { tool as tool13 } from "ai";
|
|
6371
|
+
import { tool as tool13, zodSchema as zodSchema13 } from "ai";
|
|
6297
6372
|
import { z as z13 } from "zod";
|
|
6298
6373
|
import { parse as parseYaml4 } from "yaml";
|
|
6299
6374
|
function createInstallSkillTool(skillLoader) {
|
|
6300
6375
|
return tool13({
|
|
6301
6376
|
description: "Install a new skill by providing SKILL.md markdown content or a URL. The content must have YAML frontmatter (---) with at least name and description fields.",
|
|
6302
|
-
|
|
6377
|
+
inputSchema: zodSchema13(z13.object({
|
|
6303
6378
|
content: z13.string().optional().describe("Raw SKILL.md markdown content with YAML frontmatter"),
|
|
6304
6379
|
url: z13.string().optional().describe("URL to fetch a SKILL.md from")
|
|
6305
|
-
}),
|
|
6380
|
+
})),
|
|
6306
6381
|
execute: async ({ content, url }) => {
|
|
6307
6382
|
let skillContent;
|
|
6308
6383
|
if (url && !content) {
|
|
@@ -6341,12 +6416,12 @@ function createInstallSkillTool(skillLoader) {
|
|
|
6341
6416
|
}
|
|
6342
6417
|
|
|
6343
6418
|
// src/capabilities/skills/list-skills.ts
|
|
6344
|
-
import { tool as tool14 } from "ai";
|
|
6419
|
+
import { tool as tool14, zodSchema as zodSchema14 } from "ai";
|
|
6345
6420
|
import { z as z14 } from "zod";
|
|
6346
6421
|
function createListSkillsTool(skillLoader) {
|
|
6347
6422
|
return tool14({
|
|
6348
6423
|
description: "List all installed skills with their names and descriptions.",
|
|
6349
|
-
|
|
6424
|
+
inputSchema: zodSchema14(z14.object({})),
|
|
6350
6425
|
execute: async () => {
|
|
6351
6426
|
const skills = skillLoader.getDiscovered();
|
|
6352
6427
|
if (skills.length === 0) {
|
|
@@ -6358,14 +6433,14 @@ function createListSkillsTool(skillLoader) {
|
|
|
6358
6433
|
}
|
|
6359
6434
|
|
|
6360
6435
|
// src/capabilities/skills/use-skill.ts
|
|
6361
|
-
import { tool as tool15 } from "ai";
|
|
6436
|
+
import { tool as tool15, zodSchema as zodSchema15 } from "ai";
|
|
6362
6437
|
import { z as z15 } from "zod";
|
|
6363
6438
|
function createUseSkillTool(skillLoader, permissions) {
|
|
6364
6439
|
return tool15({
|
|
6365
6440
|
description: "Load and invoke a skill by name. Returns the skill's full instructions which should be followed as guidance for the current task.",
|
|
6366
|
-
|
|
6441
|
+
inputSchema: zodSchema15(z15.object({
|
|
6367
6442
|
name: z15.string().describe("Name of the skill to invoke")
|
|
6368
|
-
}),
|
|
6443
|
+
})),
|
|
6369
6444
|
execute: async ({ name }) => {
|
|
6370
6445
|
const skill = skillLoader.load(name);
|
|
6371
6446
|
if (!skill) {
|
|
@@ -6391,19 +6466,19 @@ Allowed tools: ${skill["allowed-tools"].join(", ")}`;
|
|
|
6391
6466
|
}
|
|
6392
6467
|
|
|
6393
6468
|
// src/capabilities/scheduler/schedule-task.ts
|
|
6394
|
-
import { tool as tool16 } from "ai";
|
|
6469
|
+
import { tool as tool16, zodSchema as zodSchema16 } from "ai";
|
|
6395
6470
|
import { z as z16 } from "zod";
|
|
6396
6471
|
import cron2 from "node-cron";
|
|
6397
6472
|
function createScheduleTaskTool(scheduler, getContext) {
|
|
6398
6473
|
return tool16({
|
|
6399
6474
|
description: 'Schedule a task. Use "cron" for recurring tasks (e.g. "0 9 * * *" for daily at 9am) or "delay_seconds" for one-shot delayed tasks (e.g. 15 for "remind me in 15 seconds"). Provide exactly one of cron or delay_seconds.',
|
|
6400
|
-
|
|
6475
|
+
inputSchema: zodSchema16(z16.object({
|
|
6401
6476
|
cron: z16.string().optional().describe('Cron expression for recurring tasks (e.g. "0 9 * * *" for daily at 9am)'),
|
|
6402
6477
|
delay_seconds: z16.number().optional().describe('Delay in seconds for one-shot tasks (e.g. 15 for "remind me in 15 seconds")'),
|
|
6403
6478
|
description: z16.string().describe("Human-readable description of what this task does"),
|
|
6404
6479
|
prompt: z16.string().optional().describe("Prompt to send to the agent when the task fires"),
|
|
6405
6480
|
skill_name: z16.string().optional().describe("Name of a skill to invoke when the task fires")
|
|
6406
|
-
}),
|
|
6481
|
+
})),
|
|
6407
6482
|
execute: async ({ cron: cronExpr, delay_seconds, description, prompt, skill_name }) => {
|
|
6408
6483
|
if (!cronExpr && !delay_seconds) {
|
|
6409
6484
|
return "Either cron or delay_seconds must be provided.";
|
|
@@ -6455,12 +6530,12 @@ function createScheduleTaskTool(scheduler, getContext) {
|
|
|
6455
6530
|
}
|
|
6456
6531
|
|
|
6457
6532
|
// src/capabilities/scheduler/list-tasks.ts
|
|
6458
|
-
import { tool as tool17 } from "ai";
|
|
6533
|
+
import { tool as tool17, zodSchema as zodSchema17 } from "ai";
|
|
6459
6534
|
import { z as z17 } from "zod";
|
|
6460
6535
|
function createListTasksTool(scheduler) {
|
|
6461
6536
|
return tool17({
|
|
6462
6537
|
description: "List all scheduled tasks with their cron expressions and descriptions.",
|
|
6463
|
-
|
|
6538
|
+
inputSchema: zodSchema17(z17.object({})),
|
|
6464
6539
|
execute: async () => {
|
|
6465
6540
|
const manifests = scheduler.getManifests();
|
|
6466
6541
|
if (manifests.length === 0) {
|
|
@@ -6475,14 +6550,14 @@ function createListTasksTool(scheduler) {
|
|
|
6475
6550
|
}
|
|
6476
6551
|
|
|
6477
6552
|
// src/capabilities/scheduler/cancel-task.ts
|
|
6478
|
-
import { tool as tool18 } from "ai";
|
|
6553
|
+
import { tool as tool18, zodSchema as zodSchema18 } from "ai";
|
|
6479
6554
|
import { z as z18 } from "zod";
|
|
6480
6555
|
function createCancelTaskTool(scheduler) {
|
|
6481
6556
|
return tool18({
|
|
6482
6557
|
description: "Cancel and remove a scheduled task by its ID.",
|
|
6483
|
-
|
|
6558
|
+
inputSchema: zodSchema18(z18.object({
|
|
6484
6559
|
id: z18.string().describe("ID of the scheduled task to cancel")
|
|
6485
|
-
}),
|
|
6560
|
+
})),
|
|
6486
6561
|
execute: async ({ id }) => {
|
|
6487
6562
|
const manifests = scheduler.getManifests();
|
|
6488
6563
|
const exists = manifests.some((m) => m.id === id);
|
|
@@ -6497,12 +6572,12 @@ function createCancelTaskTool(scheduler) {
|
|
|
6497
6572
|
}
|
|
6498
6573
|
|
|
6499
6574
|
// src/capabilities/system/budget-status.ts
|
|
6500
|
-
import { tool as tool19 } from "ai";
|
|
6575
|
+
import { tool as tool19, zodSchema as zodSchema19 } from "ai";
|
|
6501
6576
|
import { z as z19 } from "zod";
|
|
6502
6577
|
function createBudgetStatusTool(tokenBudget) {
|
|
6503
6578
|
return tool19({
|
|
6504
6579
|
description: "Check the current token budget status \u2014 how many tokens have been used today, how many remain, and what percentage is consumed.",
|
|
6505
|
-
|
|
6580
|
+
inputSchema: zodSchema19(z19.object({})),
|
|
6506
6581
|
execute: async () => {
|
|
6507
6582
|
return tokenBudget.getStatusText();
|
|
6508
6583
|
}
|
|
@@ -6510,15 +6585,15 @@ function createBudgetStatusTool(tokenBudget) {
|
|
|
6510
6585
|
}
|
|
6511
6586
|
|
|
6512
6587
|
// src/capabilities/git/git-status.ts
|
|
6513
|
-
import { tool as tool20 } from "ai";
|
|
6588
|
+
import { tool as tool20, zodSchema as zodSchema20 } from "ai";
|
|
6514
6589
|
import { z as z20 } from "zod";
|
|
6515
6590
|
import { execSync as execSync2 } from "child_process";
|
|
6516
6591
|
function createGitStatusTool(getCwd) {
|
|
6517
6592
|
return tool20({
|
|
6518
6593
|
description: "Show the working tree status. Returns staged, unstaged, and untracked files.",
|
|
6519
|
-
|
|
6594
|
+
inputSchema: zodSchema20(z20.object({
|
|
6520
6595
|
path: z20.string().optional().describe("Path to check (defaults to current directory)")
|
|
6521
|
-
}),
|
|
6596
|
+
})),
|
|
6522
6597
|
execute: async ({ path: path3 }) => {
|
|
6523
6598
|
try {
|
|
6524
6599
|
const cmd = path3 ? `git -C "${path3}" status --porcelain` : "git status --porcelain";
|
|
@@ -6533,16 +6608,16 @@ function createGitStatusTool(getCwd) {
|
|
|
6533
6608
|
}
|
|
6534
6609
|
|
|
6535
6610
|
// src/capabilities/git/git-diff.ts
|
|
6536
|
-
import { tool as tool21 } from "ai";
|
|
6611
|
+
import { tool as tool21, zodSchema as zodSchema21 } from "ai";
|
|
6537
6612
|
import { z as z21 } from "zod";
|
|
6538
6613
|
import { execSync as execSync3 } from "child_process";
|
|
6539
6614
|
function createGitDiffTool(getCwd) {
|
|
6540
6615
|
return tool21({
|
|
6541
6616
|
description: "Show changes between commits, commit and working tree, etc. Shows what has been modified.",
|
|
6542
|
-
|
|
6617
|
+
inputSchema: zodSchema21(z21.object({
|
|
6543
6618
|
path: z21.string().optional().describe("File or directory to diff"),
|
|
6544
6619
|
staged: z21.boolean().optional().describe("Show staged changes (cached) instead of unstaged")
|
|
6545
|
-
}),
|
|
6620
|
+
})),
|
|
6546
6621
|
execute: async ({ path: path3, staged }) => {
|
|
6547
6622
|
try {
|
|
6548
6623
|
let cmd = "git diff";
|
|
@@ -6560,16 +6635,16 @@ function createGitDiffTool(getCwd) {
|
|
|
6560
6635
|
}
|
|
6561
6636
|
|
|
6562
6637
|
// src/capabilities/git/git-log.ts
|
|
6563
|
-
import { tool as tool22 } from "ai";
|
|
6638
|
+
import { tool as tool22, zodSchema as zodSchema22 } from "ai";
|
|
6564
6639
|
import { z as z22 } from "zod";
|
|
6565
6640
|
import { execSync as execSync4 } from "child_process";
|
|
6566
6641
|
function createGitLogTool(getCwd) {
|
|
6567
6642
|
return tool22({
|
|
6568
6643
|
description: "Show commit logs. Returns recent commit history with hash, author, date, and message.",
|
|
6569
|
-
|
|
6644
|
+
inputSchema: zodSchema22(z22.object({
|
|
6570
6645
|
count: z22.number().optional().describe("Number of commits to show (default 10)"),
|
|
6571
6646
|
path: z22.string().optional().describe("File or directory to show log for")
|
|
6572
|
-
}),
|
|
6647
|
+
})),
|
|
6573
6648
|
execute: async ({ count, path: path3 }) => {
|
|
6574
6649
|
try {
|
|
6575
6650
|
const n = count ?? 10;
|
|
@@ -6586,15 +6661,15 @@ function createGitLogTool(getCwd) {
|
|
|
6586
6661
|
}
|
|
6587
6662
|
|
|
6588
6663
|
// src/capabilities/git/git-add.ts
|
|
6589
|
-
import { tool as tool23 } from "ai";
|
|
6664
|
+
import { tool as tool23, zodSchema as zodSchema23 } from "ai";
|
|
6590
6665
|
import { z as z23 } from "zod";
|
|
6591
6666
|
import { execSync as execSync5 } from "child_process";
|
|
6592
6667
|
function createGitAddTool(getCwd) {
|
|
6593
6668
|
return tool23({
|
|
6594
6669
|
description: "Add file contents to the index (staging area). Prepares files for commit.",
|
|
6595
|
-
|
|
6670
|
+
inputSchema: zodSchema23(z23.object({
|
|
6596
6671
|
paths: z23.array(z23.string()).describe("File paths to stage")
|
|
6597
|
-
}),
|
|
6672
|
+
})),
|
|
6598
6673
|
execute: async ({ paths }) => {
|
|
6599
6674
|
try {
|
|
6600
6675
|
const fileArgs = paths.map((p) => `"${p}"`).join(" ");
|
|
@@ -6608,7 +6683,7 @@ function createGitAddTool(getCwd) {
|
|
|
6608
6683
|
}
|
|
6609
6684
|
|
|
6610
6685
|
// src/capabilities/git/git-commit.ts
|
|
6611
|
-
import { tool as tool24 } from "ai";
|
|
6686
|
+
import { tool as tool24, zodSchema as zodSchema24 } from "ai";
|
|
6612
6687
|
import { z as z24 } from "zod";
|
|
6613
6688
|
import { execSync as execSync6 } from "child_process";
|
|
6614
6689
|
import { writeFileSync as writeFileSync10, unlinkSync as unlinkSync3 } from "fs";
|
|
@@ -6617,9 +6692,9 @@ var CO_AUTHOR = "Mercury <mercury@cosmicstack.org>";
|
|
|
6617
6692
|
function createGitCommitTool(getCwd) {
|
|
6618
6693
|
return tool24({
|
|
6619
6694
|
description: "Record changes to the repository. Creates a new commit with staged changes. Automatically includes a Co-authored-by trailer for attribution.",
|
|
6620
|
-
|
|
6695
|
+
inputSchema: zodSchema24(z24.object({
|
|
6621
6696
|
message: z24.string().describe("Commit message")
|
|
6622
|
-
}),
|
|
6697
|
+
})),
|
|
6623
6698
|
execute: async ({ message }) => {
|
|
6624
6699
|
try {
|
|
6625
6700
|
const fullMessage = `${message}
|
|
@@ -6654,25 +6729,19 @@ Co-authored-by: ${CO_AUTHOR}`;
|
|
|
6654
6729
|
}
|
|
6655
6730
|
|
|
6656
6731
|
// src/capabilities/git/git-push.ts
|
|
6657
|
-
import { tool as tool25 } from "ai";
|
|
6732
|
+
import { tool as tool25, zodSchema as zodSchema25 } from "ai";
|
|
6658
6733
|
import { z as z25 } from "zod";
|
|
6659
6734
|
import { execSync as execSync7 } from "child_process";
|
|
6660
6735
|
function createGitPushTool(permissions, getCwd) {
|
|
6661
6736
|
return tool25({
|
|
6662
6737
|
description: "Push commits to a remote repository. This modifies a remote and requires approval.",
|
|
6663
|
-
|
|
6738
|
+
inputSchema: zodSchema25(z25.object({
|
|
6664
6739
|
remote: z25.string().optional().describe("Remote name (default: origin)"),
|
|
6665
6740
|
branch: z25.string().optional().describe("Branch name (default: current branch)")
|
|
6666
|
-
}),
|
|
6741
|
+
})),
|
|
6667
6742
|
execute: async ({ remote, branch }) => {
|
|
6668
6743
|
const cmd = `git push ${remote || "origin"} ${branch || ""}`.trim();
|
|
6669
6744
|
const check = await permissions.checkShellCommand(cmd);
|
|
6670
|
-
if (!check.allowed && check.needsApproval) {
|
|
6671
|
-
const baseCmd = "git";
|
|
6672
|
-
permissions.addPendingApproval(baseCmd);
|
|
6673
|
-
return `\u26A0 This command pushes to a remote: ${cmd}
|
|
6674
|
-
Ask the user for permission. If they approve, try again. If they say "always", use the approve_command tool.`;
|
|
6675
|
-
}
|
|
6676
6745
|
if (!check.allowed) {
|
|
6677
6746
|
return `Error: ${check.reason}`;
|
|
6678
6747
|
}
|
|
@@ -6687,7 +6756,7 @@ Ask the user for permission. If they approve, try again. If they say "always", u
|
|
|
6687
6756
|
}
|
|
6688
6757
|
|
|
6689
6758
|
// src/capabilities/github/create-pr.ts
|
|
6690
|
-
import { tool as tool26 } from "ai";
|
|
6759
|
+
import { tool as tool26, zodSchema as zodSchema26 } from "ai";
|
|
6691
6760
|
import { z as z26 } from "zod";
|
|
6692
6761
|
|
|
6693
6762
|
// src/utils/github.ts
|
|
@@ -6745,7 +6814,7 @@ async function githubRequest(path3, options = {}) {
|
|
|
6745
6814
|
function createCreatePrTool() {
|
|
6746
6815
|
return tool26({
|
|
6747
6816
|
description: "Create a pull request on GitHub. Requires GITHUB_TOKEN to be configured.",
|
|
6748
|
-
|
|
6817
|
+
inputSchema: zodSchema26(z26.object({
|
|
6749
6818
|
owner: z26.string().describe("Repository owner (username or org)"),
|
|
6750
6819
|
repo: z26.string().describe("Repository name"),
|
|
6751
6820
|
title: z26.string().describe("PR title"),
|
|
@@ -6753,7 +6822,7 @@ function createCreatePrTool() {
|
|
|
6753
6822
|
head: z26.string().describe("The branch containing the changes"),
|
|
6754
6823
|
base: z26.string().describe("The branch to merge into").default("main"),
|
|
6755
6824
|
draft: z26.boolean().describe("Create as draft PR").default(false)
|
|
6756
|
-
}),
|
|
6825
|
+
})),
|
|
6757
6826
|
execute: async ({ owner, repo, title, body, head, base, draft }) => {
|
|
6758
6827
|
try {
|
|
6759
6828
|
const result = await githubRequest(`/repos/${owner}/${repo}/pulls`, {
|
|
@@ -6771,17 +6840,17 @@ ${draft ? "(draft)" : ""} ${result.state}`;
|
|
|
6771
6840
|
}
|
|
6772
6841
|
|
|
6773
6842
|
// src/capabilities/github/review-pr.ts
|
|
6774
|
-
import { tool as tool27 } from "ai";
|
|
6843
|
+
import { tool as tool27, zodSchema as zodSchema27 } from "ai";
|
|
6775
6844
|
import { z as z27 } from "zod";
|
|
6776
6845
|
function createReviewPrTool() {
|
|
6777
6846
|
return tool27({
|
|
6778
6847
|
description: "Get details of a pull request including the diff. Reviews the PR and returns the title, body, changed files, and diff. Optionally post a review comment.",
|
|
6779
|
-
|
|
6848
|
+
inputSchema: zodSchema27(z27.object({
|
|
6780
6849
|
owner: z27.string().describe("Repository owner (username or org)"),
|
|
6781
6850
|
repo: z27.string().describe("Repository name"),
|
|
6782
6851
|
number: z27.number().describe("PR number"),
|
|
6783
6852
|
comment: z27.string().describe("Review comment to post on the PR (optional)").optional()
|
|
6784
|
-
}),
|
|
6853
|
+
})),
|
|
6785
6854
|
execute: async ({ owner, repo, number, comment }) => {
|
|
6786
6855
|
try {
|
|
6787
6856
|
const pr = await githubRequest(`/repos/${owner}/${repo}/pulls/${number}`);
|
|
@@ -6846,18 +6915,18 @@ Failed to post review comment: ${err.message}`;
|
|
|
6846
6915
|
}
|
|
6847
6916
|
|
|
6848
6917
|
// src/capabilities/github/list-issues.ts
|
|
6849
|
-
import { tool as tool28 } from "ai";
|
|
6918
|
+
import { tool as tool28, zodSchema as zodSchema28 } from "ai";
|
|
6850
6919
|
import { z as z28 } from "zod";
|
|
6851
6920
|
function createListIssuesTool() {
|
|
6852
6921
|
return tool28({
|
|
6853
6922
|
description: "List GitHub issues for a repository. Requires GITHUB_TOKEN.",
|
|
6854
|
-
|
|
6923
|
+
inputSchema: zodSchema28(z28.object({
|
|
6855
6924
|
owner: z28.string().describe("Repository owner (username or org)"),
|
|
6856
6925
|
repo: z28.string().describe("Repository name"),
|
|
6857
6926
|
state: z28.enum(["open", "closed", "all"]).describe("Filter by issue state").default("open"),
|
|
6858
6927
|
labels: z28.string().describe("Comma-separated label names to filter by (optional)").optional(),
|
|
6859
6928
|
limit: z28.number().describe("Maximum number of issues to return").default(10)
|
|
6860
|
-
}),
|
|
6929
|
+
})),
|
|
6861
6930
|
execute: async ({ owner, repo, state, labels, limit }) => {
|
|
6862
6931
|
try {
|
|
6863
6932
|
const params = new URLSearchParams();
|
|
@@ -6884,18 +6953,18 @@ ${lines.join("\n")}`;
|
|
|
6884
6953
|
}
|
|
6885
6954
|
|
|
6886
6955
|
// src/capabilities/github/create-issue.ts
|
|
6887
|
-
import { tool as tool29 } from "ai";
|
|
6956
|
+
import { tool as tool29, zodSchema as zodSchema29 } from "ai";
|
|
6888
6957
|
import { z as z29 } from "zod";
|
|
6889
6958
|
function createCreateIssueTool() {
|
|
6890
6959
|
return tool29({
|
|
6891
6960
|
description: "Create a new GitHub issue in a repository. Requires GITHUB_TOKEN.",
|
|
6892
|
-
|
|
6961
|
+
inputSchema: zodSchema29(z29.object({
|
|
6893
6962
|
owner: z29.string().describe("Repository owner (username or org)"),
|
|
6894
6963
|
repo: z29.string().describe("Repository name"),
|
|
6895
6964
|
title: z29.string().describe("Issue title"),
|
|
6896
6965
|
body: z29.string().describe("Issue description (markdown supported)").default(""),
|
|
6897
6966
|
labels: z29.array(z29.string()).describe("Label names to apply").optional()
|
|
6898
|
-
}),
|
|
6967
|
+
})),
|
|
6899
6968
|
execute: async ({ owner, repo, title, body, labels }) => {
|
|
6900
6969
|
try {
|
|
6901
6970
|
const payload = { title, body };
|
|
@@ -6914,7 +6983,7 @@ function createCreateIssueTool() {
|
|
|
6914
6983
|
}
|
|
6915
6984
|
|
|
6916
6985
|
// src/capabilities/github/github-api.ts
|
|
6917
|
-
import { tool as tool30 } from "ai";
|
|
6986
|
+
import { tool as tool30, zodSchema as zodSchema30 } from "ai";
|
|
6918
6987
|
import { z as z30 } from "zod";
|
|
6919
6988
|
var CO_AUTHOR_NAME = "Mercury";
|
|
6920
6989
|
var CO_AUTHOR_EMAIL = "mercury@cosmicstack.org";
|
|
@@ -6950,11 +7019,11 @@ Common operations you can perform:
|
|
|
6950
7019
|
- Any other GitHub API v3 endpoint.
|
|
6951
7020
|
|
|
6952
7021
|
IMPORTANT: When the user wants to push code or files to GitHub and git push fails (auth issues, no SSH key, etc.), use PUT /repos/{owner}/{repo}/contents/{path} to create or update files directly through the API. This bypasses local git and creates a commit with Mercury as co-author.`,
|
|
6953
|
-
|
|
7022
|
+
inputSchema: zodSchema30(z30.object({
|
|
6954
7023
|
path: z30.string().describe("Full API path (e.g., /repos/owner/repo/issues or /repos/owner/repo/contents/path/to/file)"),
|
|
6955
7024
|
method: z30.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).describe("HTTP method").default("GET"),
|
|
6956
7025
|
body: z30.string().describe("JSON body for write requests (as a JSON string)").optional()
|
|
6957
|
-
}),
|
|
7026
|
+
})),
|
|
6958
7027
|
execute: async ({ path: path3, method, body }) => {
|
|
6959
7028
|
try {
|
|
6960
7029
|
let parsedBody;
|
|
@@ -6983,7 +7052,7 @@ IMPORTANT: When the user wants to push code or files to GitHub and git push fail
|
|
|
6983
7052
|
}
|
|
6984
7053
|
|
|
6985
7054
|
// src/capabilities/web/fetch-url.ts
|
|
6986
|
-
import { tool as tool31 } from "ai";
|
|
7055
|
+
import { tool as tool31, zodSchema as zodSchema31 } from "ai";
|
|
6987
7056
|
import { z as z31 } from "zod";
|
|
6988
7057
|
var MAX_CONTENT_LENGTH = 15e3;
|
|
6989
7058
|
function stripHtml(html) {
|
|
@@ -7019,10 +7088,10 @@ function stripHtml(html) {
|
|
|
7019
7088
|
function createFetchUrlTool() {
|
|
7020
7089
|
return tool31({
|
|
7021
7090
|
description: "Fetch a URL and return its content as text. Strips HTML to readable markdown-like format. Useful for reading documentation, APIs, or web pages.",
|
|
7022
|
-
|
|
7091
|
+
inputSchema: zodSchema31(z31.object({
|
|
7023
7092
|
url: z31.string().describe("The URL to fetch"),
|
|
7024
7093
|
format: z31.enum(["text", "markdown"]).optional().describe("Output format (default: markdown)")
|
|
7025
|
-
}),
|
|
7094
|
+
})),
|
|
7026
7095
|
execute: async ({ url, format }) => {
|
|
7027
7096
|
const outputFormat = format ?? "markdown";
|
|
7028
7097
|
try {
|