cascade-ai 0.51.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +195 -34
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +195 -34
- package/dist/cli.js.map +1 -1
- package/dist/desktop-core.cjs +194 -39
- package/dist/index.cjs +186 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -10
- package/dist/index.d.ts +44 -10
- package/dist/index.js +186 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -1793,7 +1793,7 @@ var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SEC
|
|
|
1793
1793
|
var init_constants = __esm({
|
|
1794
1794
|
"src/constants.ts"() {
|
|
1795
1795
|
init_pricing();
|
|
1796
|
-
CASCADE_VERSION = "0.
|
|
1796
|
+
CASCADE_VERSION = "0.53.0";
|
|
1797
1797
|
CASCADE_CONFIG_FILE = ".cascade/config.json";
|
|
1798
1798
|
CASCADE_DB_FILE = ".cascade/memory.db";
|
|
1799
1799
|
CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
|
|
@@ -2792,26 +2792,14 @@ var init_openai = __esm({
|
|
|
2792
2792
|
// src/providers/azure.ts
|
|
2793
2793
|
var azure_exports = {};
|
|
2794
2794
|
__export(azure_exports, {
|
|
2795
|
+
AZURE_BASE_MODELS: () => AZURE_BASE_MODELS,
|
|
2795
2796
|
AzureOpenAIProvider: () => AzureOpenAIProvider,
|
|
2796
2797
|
azureModelForDeployment: () => azureModelForDeployment,
|
|
2797
2798
|
inferAzureBaseModel: () => inferAzureBaseModel
|
|
2798
2799
|
});
|
|
2799
2800
|
function inferAzureBaseModel(deploymentName) {
|
|
2800
2801
|
const n = deploymentName.toLowerCase();
|
|
2801
|
-
const
|
|
2802
|
-
[/gpt-?5\.5/, "gpt-5.5"],
|
|
2803
|
-
[/gpt-?5\.4.*mini/, "gpt-5.4-mini"],
|
|
2804
|
-
[/gpt-?5\.4/, "gpt-5.4"],
|
|
2805
|
-
[/gpt-?5.*nano/, "gpt-5-nano"],
|
|
2806
|
-
[/gpt-?5.*mini/, "gpt-5-mini"],
|
|
2807
|
-
[/gpt-?5/, "gpt-5"],
|
|
2808
|
-
[/gpt-?4\.1-nano/, "gpt-4.1-nano"],
|
|
2809
|
-
[/gpt-?4\.1-mini/, "gpt-4.1-mini"],
|
|
2810
|
-
[/gpt-?4\.1/, "gpt-4.1"],
|
|
2811
|
-
[/gpt-?4o-mini/, "gpt-4o-mini"],
|
|
2812
|
-
[/gpt-?4o/, "gpt-4o"]
|
|
2813
|
-
];
|
|
2814
|
-
for (const [re, base] of rules) if (re.test(n)) return base;
|
|
2802
|
+
for (const [re, base] of AZURE_BASE_MODEL_RULES) if (re.test(n)) return base;
|
|
2815
2803
|
return null;
|
|
2816
2804
|
}
|
|
2817
2805
|
function azureModelForDeployment(cfg) {
|
|
@@ -2856,13 +2844,27 @@ function azureModelForDeployment(cfg) {
|
|
|
2856
2844
|
supportsToolUse: true
|
|
2857
2845
|
};
|
|
2858
2846
|
}
|
|
2859
|
-
var DEFAULT_AZURE_API_VERSION, AzureOpenAIProvider;
|
|
2847
|
+
var DEFAULT_AZURE_API_VERSION, AZURE_BASE_MODEL_RULES, AZURE_BASE_MODELS, AzureOpenAIProvider;
|
|
2860
2848
|
var init_azure = __esm({
|
|
2861
2849
|
"src/providers/azure.ts"() {
|
|
2862
2850
|
init_constants();
|
|
2863
2851
|
init_openai();
|
|
2864
2852
|
init_pricing();
|
|
2865
2853
|
DEFAULT_AZURE_API_VERSION = "2024-12-01-preview";
|
|
2854
|
+
AZURE_BASE_MODEL_RULES = [
|
|
2855
|
+
[/gpt-?5\.5/, "gpt-5.5"],
|
|
2856
|
+
[/gpt-?5\.4.*mini/, "gpt-5.4-mini"],
|
|
2857
|
+
[/gpt-?5\.4/, "gpt-5.4"],
|
|
2858
|
+
[/gpt-?5.*nano/, "gpt-5-nano"],
|
|
2859
|
+
[/gpt-?5.*mini/, "gpt-5-mini"],
|
|
2860
|
+
[/gpt-?5/, "gpt-5"],
|
|
2861
|
+
[/gpt-?4\.1-nano/, "gpt-4.1-nano"],
|
|
2862
|
+
[/gpt-?4\.1-mini/, "gpt-4.1-mini"],
|
|
2863
|
+
[/gpt-?4\.1/, "gpt-4.1"],
|
|
2864
|
+
[/gpt-?4o-mini/, "gpt-4o-mini"],
|
|
2865
|
+
[/gpt-?4o/, "gpt-4o"]
|
|
2866
|
+
];
|
|
2867
|
+
AZURE_BASE_MODELS = AZURE_BASE_MODEL_RULES.map(([, id]) => id);
|
|
2866
2868
|
AzureOpenAIProvider = class extends OpenAIProvider {
|
|
2867
2869
|
constructor(config, model) {
|
|
2868
2870
|
const rawUrl = config.baseUrl ?? AZURE_BASE_URL_TEMPLATE.replace("{resource}", "YOUR_RESOURCE");
|
|
@@ -5050,7 +5052,14 @@ var ToolsConfigSchema = zod.z.object({
|
|
|
5050
5052
|
* built-in tool (shell/file/git/…) from existing at all, rather than just
|
|
5051
5053
|
* gating it behind approval. Omitted = full default set (unchanged).
|
|
5052
5054
|
*/
|
|
5053
|
-
enabledTools: zod.z.array(zod.z.string()).optional()
|
|
5055
|
+
enabledTools: zod.z.array(zod.z.string()).optional(),
|
|
5056
|
+
/**
|
|
5057
|
+
* Tools to remove even when they would otherwise be registered. Unlike
|
|
5058
|
+
* `enabledTools` (an allowlist over the built-in set), this also reaches
|
|
5059
|
+
* tools registered outside it — MCP tools and media generation — which is
|
|
5060
|
+
* the only way to turn those off.
|
|
5061
|
+
*/
|
|
5062
|
+
disabledTools: zod.z.array(zod.z.string()).optional()
|
|
5054
5063
|
});
|
|
5055
5064
|
var HookDefinitionSchema = zod.z.object({
|
|
5056
5065
|
name: zod.z.string().optional(),
|
|
@@ -5556,6 +5565,26 @@ var ConfigManager = class {
|
|
|
5556
5565
|
}
|
|
5557
5566
|
};
|
|
5558
5567
|
|
|
5568
|
+
// src/tools/tool-name.ts
|
|
5569
|
+
var MCP_TOOL_PREFIX = "mcp__";
|
|
5570
|
+
function sanitizeToolNameSegment(segment) {
|
|
5571
|
+
const cleaned = segment.replace(/[^a-zA-Z0-9-]+/g, "_");
|
|
5572
|
+
let start = 0;
|
|
5573
|
+
let end = cleaned.length;
|
|
5574
|
+
while (start < end && cleaned[start] === "_") start++;
|
|
5575
|
+
while (end > start && cleaned[end - 1] === "_") end--;
|
|
5576
|
+
return cleaned.slice(start, end) || "_";
|
|
5577
|
+
}
|
|
5578
|
+
function mcpToolName(serverName, toolName) {
|
|
5579
|
+
return `${MCP_TOOL_PREFIX}${sanitizeToolNameSegment(serverName)}__${sanitizeToolNameSegment(toolName)}`;
|
|
5580
|
+
}
|
|
5581
|
+
function isMcpToolName(name) {
|
|
5582
|
+
return name.startsWith(MCP_TOOL_PREFIX);
|
|
5583
|
+
}
|
|
5584
|
+
function mcpServerPrefix(serverName) {
|
|
5585
|
+
return `${MCP_TOOL_PREFIX}${sanitizeToolNameSegment(serverName)}__`;
|
|
5586
|
+
}
|
|
5587
|
+
|
|
5559
5588
|
// src/cli/utils/terminal-input.ts
|
|
5560
5589
|
var PASTE_START = "\x1B[200~";
|
|
5561
5590
|
var PASTE_END = "\x1B[201~";
|
|
@@ -11075,17 +11104,13 @@ var CAPABILITIES = [
|
|
|
11075
11104
|
},
|
|
11076
11105
|
// ── Video ──
|
|
11077
11106
|
{
|
|
11078
|
-
//
|
|
11079
|
-
//
|
|
11080
|
-
//
|
|
11081
|
-
// signed URL. That is a different execution shape from every other entry
|
|
11082
|
-
// here and is not wired up — claiming otherwise would produce a tool that
|
|
11083
|
-
// fails at call time instead of an honest "not yet".
|
|
11107
|
+
// Long-running: submit returns an operation name, the video lands minutes
|
|
11108
|
+
// later, and the bytes come from a short-lived signed URL. generate.ts owns
|
|
11109
|
+
// that polling loop; the registry only records the shape via `api`.
|
|
11084
11110
|
modality: "video",
|
|
11085
11111
|
provider: "gemini",
|
|
11086
11112
|
modelId: "veo-3.1-generate-001",
|
|
11087
|
-
api: "gemini-predict"
|
|
11088
|
-
unsupported: "Veo runs as a long-running operation (submit \u2192 poll \u2192 fetch). Not wired up yet."
|
|
11113
|
+
api: "gemini-predict-lro"
|
|
11089
11114
|
}
|
|
11090
11115
|
];
|
|
11091
11116
|
function capabilityCost(cap) {
|
|
@@ -11398,6 +11423,75 @@ async function generateSpeech(cap, cfg, req, signal) {
|
|
|
11398
11423
|
};
|
|
11399
11424
|
}, cap.modelId);
|
|
11400
11425
|
}
|
|
11426
|
+
var VIDEO_POLL_INTERVAL_MS = 1e4;
|
|
11427
|
+
var VIDEO_TIMEOUT_MS = 8 * 6e4;
|
|
11428
|
+
function sleep(ms, signal) {
|
|
11429
|
+
return new Promise((resolve2, reject) => {
|
|
11430
|
+
if (signal?.aborted) return reject(new Error("Cancelled."));
|
|
11431
|
+
const timer = setTimeout(() => {
|
|
11432
|
+
signal?.removeEventListener("abort", onAbort);
|
|
11433
|
+
resolve2();
|
|
11434
|
+
}, ms);
|
|
11435
|
+
function onAbort() {
|
|
11436
|
+
clearTimeout(timer);
|
|
11437
|
+
reject(new Error("Cancelled."));
|
|
11438
|
+
}
|
|
11439
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
11440
|
+
});
|
|
11441
|
+
}
|
|
11442
|
+
async function generateVideo(cap, cfg, req, signal, onProgress) {
|
|
11443
|
+
return callProvider(async () => {
|
|
11444
|
+
if (cap.api !== "gemini-predict-lro") throw new Error(`${cap.modelId} is not a video generator.`);
|
|
11445
|
+
const base = baseUrlFor(cfg, "https://generativelanguage.googleapis.com/v1beta");
|
|
11446
|
+
const key = cfg.apiKey ?? "";
|
|
11447
|
+
const headers = { "x-goog-api-key": key };
|
|
11448
|
+
const submit = await postJson(
|
|
11449
|
+
`${base}/models/${encodeURIComponent(cap.modelId)}:predictLongRunning`,
|
|
11450
|
+
{
|
|
11451
|
+
instances: [{ prompt: req.prompt }],
|
|
11452
|
+
parameters: {
|
|
11453
|
+
aspectRatio: req.aspectRatio ?? "16:9",
|
|
11454
|
+
...req.seconds ? { durationSeconds: req.seconds } : {}
|
|
11455
|
+
}
|
|
11456
|
+
},
|
|
11457
|
+
headers,
|
|
11458
|
+
signal
|
|
11459
|
+
);
|
|
11460
|
+
const started = await submit.json();
|
|
11461
|
+
if (!started.name) throw new Error("Video request was accepted but returned no operation to poll.");
|
|
11462
|
+
onProgress?.(`Video generation started (${cap.modelId}). This usually takes 1-3 minutes.`);
|
|
11463
|
+
const deadline = Date.now() + VIDEO_TIMEOUT_MS;
|
|
11464
|
+
let operation = {};
|
|
11465
|
+
while (Date.now() < deadline) {
|
|
11466
|
+
await sleep(VIDEO_POLL_INTERVAL_MS, signal);
|
|
11467
|
+
const poll = await fetch(`${base}/${started.name}`, { headers, signal });
|
|
11468
|
+
if (!poll.ok) {
|
|
11469
|
+
const text = await poll.text().catch(() => "");
|
|
11470
|
+
throw Object.assign(new Error(text || poll.statusText), { status: poll.status });
|
|
11471
|
+
}
|
|
11472
|
+
operation = await poll.json();
|
|
11473
|
+
if (operation.done) break;
|
|
11474
|
+
onProgress?.("Still rendering\u2026");
|
|
11475
|
+
}
|
|
11476
|
+
if (!operation.done) {
|
|
11477
|
+
throw new Error(
|
|
11478
|
+
`Video was still rendering after ${Math.round(VIDEO_TIMEOUT_MS / 6e4)} minutes. It may still complete on the provider side, but Cascade stopped waiting.`
|
|
11479
|
+
);
|
|
11480
|
+
}
|
|
11481
|
+
if (operation.error?.message) throw new Error(operation.error.message);
|
|
11482
|
+
const uri = operation.response?.generateVideoResponse?.generatedSamples?.[0]?.video?.uri;
|
|
11483
|
+
if (!uri) throw new Error("Video finished but the response contained no video.");
|
|
11484
|
+
const media = await fetch(uri, { headers, signal });
|
|
11485
|
+
if (!media.ok) throw new Error(`Video finished but could not be downloaded (HTTP ${media.status}).`);
|
|
11486
|
+
return {
|
|
11487
|
+
data: Buffer.from(await media.arrayBuffer()),
|
|
11488
|
+
mimeType: "video/mp4",
|
|
11489
|
+
filename: stamp("mp4"),
|
|
11490
|
+
modelId: cap.modelId,
|
|
11491
|
+
provider: cap.provider
|
|
11492
|
+
};
|
|
11493
|
+
}, cap.modelId);
|
|
11494
|
+
}
|
|
11401
11495
|
async function transcribeAudio(cap, cfg, req, signal) {
|
|
11402
11496
|
return callProvider(async () => {
|
|
11403
11497
|
if (cap.api !== "openai-transcriptions") throw new Error(`${cap.modelId} is not a transcription model.`);
|
|
@@ -11522,6 +11616,58 @@ var GenerateSpeechTool = class extends BaseTool {
|
|
|
11522
11616
|
Model: ${asset.modelId} \u2014 ${r.selected.reason}`;
|
|
11523
11617
|
}
|
|
11524
11618
|
};
|
|
11619
|
+
var DEFAULT_VIDEO_SECONDS = 5;
|
|
11620
|
+
var MAX_VIDEO_SECONDS = 8;
|
|
11621
|
+
var GenerateVideoTool = class extends BaseTool {
|
|
11622
|
+
constructor(deps) {
|
|
11623
|
+
super();
|
|
11624
|
+
this.deps = deps;
|
|
11625
|
+
}
|
|
11626
|
+
deps;
|
|
11627
|
+
name = "generate_video";
|
|
11628
|
+
// The price is in the description on purpose. Video is ~1000x the cost of an
|
|
11629
|
+
// image per call, it is billed per SECOND, and the model choosing the
|
|
11630
|
+
// duration is the one deciding the bill — so the number has to be where the
|
|
11631
|
+
// model reads before calling, not only in the result afterwards.
|
|
11632
|
+
description = "Generate a short video clip from a text description. EXPENSIVE and slow: billed per second of output (about $0.40/second, so a 5-second clip costs roughly $2) and takes 1-3 minutes. Only use when the user explicitly asks for a video, and prefer the shortest duration that satisfies the request.";
|
|
11633
|
+
inputSchema = {
|
|
11634
|
+
type: "object",
|
|
11635
|
+
properties: {
|
|
11636
|
+
prompt: { type: "string", description: "What the video should show: subject, action, camera movement, style." },
|
|
11637
|
+
seconds: { type: "number", description: `Clip length, 1-${MAX_VIDEO_SECONDS} seconds (default ${DEFAULT_VIDEO_SECONDS}). Billed per second \u2014 keep it short.` },
|
|
11638
|
+
aspectRatio: { type: "string", description: 'Frame shape: "16:9" (default) or "9:16" for vertical.' },
|
|
11639
|
+
model: { type: "string", description: "Optional: force a specific video model." }
|
|
11640
|
+
},
|
|
11641
|
+
required: ["prompt"]
|
|
11642
|
+
};
|
|
11643
|
+
async execute(input, options) {
|
|
11644
|
+
const prompt = String(input["prompt"] ?? "").trim();
|
|
11645
|
+
if (!prompt) return 'Provide a "prompt" describing the video.';
|
|
11646
|
+
const r = resolve(this.deps, "video", input["model"] ? String(input["model"]) : void 0);
|
|
11647
|
+
if (!r.ok) return r.error;
|
|
11648
|
+
const asked = typeof input["seconds"] === "number" ? input["seconds"] : DEFAULT_VIDEO_SECONDS;
|
|
11649
|
+
const seconds = Math.max(1, Math.min(MAX_VIDEO_SECONDS, Math.round(asked)));
|
|
11650
|
+
const asset = await generateVideo(
|
|
11651
|
+
r.selected.capability,
|
|
11652
|
+
r.cfg,
|
|
11653
|
+
{
|
|
11654
|
+
prompt,
|
|
11655
|
+
seconds,
|
|
11656
|
+
...input["aspectRatio"] ? { aspectRatio: String(input["aspectRatio"]) } : {}
|
|
11657
|
+
},
|
|
11658
|
+
options.signal,
|
|
11659
|
+
// Progress goes to the run log so a two-minute wait doesn't look like a
|
|
11660
|
+
// hang — the single most likely reason someone kills a working render.
|
|
11661
|
+
(note) => options.onProgress?.(note)
|
|
11662
|
+
);
|
|
11663
|
+
const location = await this.deps.sink(asset);
|
|
11664
|
+
const unit = r.selected.cost;
|
|
11665
|
+
const estimate = unit ? ` Estimated cost: $${(unit.amount * seconds).toFixed(2)} (${seconds}s x ${unit.label}).` : "";
|
|
11666
|
+
const clamped = seconds !== Math.round(asked) ? ` Duration was clamped to ${seconds}s (max ${MAX_VIDEO_SECONDS}).` : "";
|
|
11667
|
+
return `Video generated and saved to ${location}.
|
|
11668
|
+
Model: ${asset.modelId}.${estimate}${clamped}`;
|
|
11669
|
+
}
|
|
11670
|
+
};
|
|
11525
11671
|
var TranscribeAudioTool = class extends BaseTool {
|
|
11526
11672
|
constructor(deps, readFile) {
|
|
11527
11673
|
super();
|
|
@@ -11572,6 +11718,7 @@ function buildMediaTools(deps, readFile) {
|
|
|
11572
11718
|
const modalities = new Set(deps.registry.availableModalities());
|
|
11573
11719
|
if (modalities.has("image")) tools.push(new GenerateImageTool(deps));
|
|
11574
11720
|
if (modalities.has("speech")) tools.push(new GenerateSpeechTool(deps));
|
|
11721
|
+
if (modalities.has("video")) tools.push(new GenerateVideoTool(deps));
|
|
11575
11722
|
if (modalities.has("transcription") && readFile) tools.push(new TranscribeAudioTool(deps, readFile));
|
|
11576
11723
|
return tools;
|
|
11577
11724
|
}
|
|
@@ -13072,7 +13219,7 @@ var McpToolWrapper = class extends BaseTool {
|
|
|
13072
13219
|
this.mcpClient = mcpClient;
|
|
13073
13220
|
this.serverName = serverName;
|
|
13074
13221
|
this.toolName = toolName;
|
|
13075
|
-
this.name =
|
|
13222
|
+
this.name = mcpToolName(serverName, toolName);
|
|
13076
13223
|
this.description = `[MCP:${serverName}] ${description}`;
|
|
13077
13224
|
this.inputSchema = inputSchema;
|
|
13078
13225
|
}
|
|
@@ -15991,6 +16138,7 @@ var Cascade = class _Cascade extends EventEmitter__default.default {
|
|
|
15991
16138
|
router;
|
|
15992
16139
|
toolRegistry;
|
|
15993
16140
|
multimodal;
|
|
16141
|
+
mediaSink;
|
|
15994
16142
|
mcpClient;
|
|
15995
16143
|
codeIndex;
|
|
15996
16144
|
config;
|
|
@@ -16310,14 +16458,27 @@ ${input}` }],
|
|
|
16310
16458
|
* server stores a file row) constructs the tools itself via buildMediaTools
|
|
16311
16459
|
* with its own sink.
|
|
16312
16460
|
*/
|
|
16461
|
+
/**
|
|
16462
|
+
* Replace where generated media is written.
|
|
16463
|
+
*
|
|
16464
|
+
* The default sink writes into the workspace, which is what a desktop or CLI
|
|
16465
|
+
* run wants. A host with different storage — the cloud server keeps a file
|
|
16466
|
+
* row plus bytes under the tenant's directory — supplies its own here, and
|
|
16467
|
+
* the media tools are rebuilt around it. Re-registering by name replaces the
|
|
16468
|
+
* existing tools rather than duplicating them.
|
|
16469
|
+
*/
|
|
16470
|
+
setMediaSink(sink) {
|
|
16471
|
+
this.mediaSink = sink;
|
|
16472
|
+
this.registerMediaTools(this.workspacePath);
|
|
16473
|
+
}
|
|
16313
16474
|
registerMediaTools(workspacePath) {
|
|
16314
|
-
const
|
|
16315
|
-
const permitted = (name) => !
|
|
16475
|
+
const denied = new Set(this.config.tools?.disabledTools ?? []);
|
|
16476
|
+
const permitted = (name) => !denied.has(name);
|
|
16316
16477
|
const tools = buildMediaTools(
|
|
16317
16478
|
{
|
|
16318
16479
|
registry: this.multimodal,
|
|
16319
16480
|
lookupProvider: (provider) => (this.config.providers ?? []).find((p) => p.type === provider),
|
|
16320
|
-
sink: async (asset) => {
|
|
16481
|
+
sink: this.mediaSink ?? (async (asset) => {
|
|
16321
16482
|
const fsp = await import('fs/promises');
|
|
16322
16483
|
const nodePath = await import('path');
|
|
16323
16484
|
const dir = nodePath.join(workspacePath, "generated");
|
|
@@ -16325,7 +16486,7 @@ ${input}` }],
|
|
|
16325
16486
|
const dest = nodePath.join(dir, asset.filename);
|
|
16326
16487
|
await fsp.writeFile(dest, asset.data);
|
|
16327
16488
|
return nodePath.relative(workspacePath, dest);
|
|
16328
|
-
}
|
|
16489
|
+
})
|
|
16329
16490
|
},
|
|
16330
16491
|
async (p) => {
|
|
16331
16492
|
const fsp = await import('fs/promises');
|
|
@@ -17329,7 +17490,7 @@ var CloudClient = class _CloudClient {
|
|
|
17329
17490
|
const deadline = Date.now() + start.expiresIn * 1e3;
|
|
17330
17491
|
while (Date.now() < deadline) {
|
|
17331
17492
|
if (opts.signal?.aborted) throw new Error("Sign-in cancelled.");
|
|
17332
|
-
await
|
|
17493
|
+
await sleep2(interval * 1e3, opts.signal);
|
|
17333
17494
|
const r = await this.pollDevice(start.deviceCode);
|
|
17334
17495
|
if (r === "slow_down") {
|
|
17335
17496
|
interval += 5;
|
|
@@ -17540,7 +17701,7 @@ var CloudClient = class _CloudClient {
|
|
|
17540
17701
|
this.store.clear();
|
|
17541
17702
|
}
|
|
17542
17703
|
};
|
|
17543
|
-
function
|
|
17704
|
+
function sleep2(ms, signal) {
|
|
17544
17705
|
return new Promise((resolve2, reject) => {
|
|
17545
17706
|
const t = setTimeout(resolve2, ms);
|
|
17546
17707
|
signal?.addEventListener("abort", () => {
|
|
@@ -19419,13 +19580,13 @@ ${msg.content}`).join("\n\n");
|
|
|
19419
19580
|
const cascade = cascadeRef.current;
|
|
19420
19581
|
if (!cascade) return "Cascade not initialized.";
|
|
19421
19582
|
const toolRegistry = cascade.getToolRegistry();
|
|
19422
|
-
const tools = toolRegistry.getToolDefinitions().filter((t) => t.name
|
|
19583
|
+
const tools = toolRegistry.getToolDefinitions().filter((t) => isMcpToolName(t.name));
|
|
19423
19584
|
if (tools.length === 0) return "No MCP tools connected.";
|
|
19424
19585
|
const servers = /* @__PURE__ */ new Set();
|
|
19425
19586
|
tools.forEach((t) => servers.add(t.name.split("::")[1]));
|
|
19426
19587
|
const lines = [`Connected MCP Servers (${servers.size}):`];
|
|
19427
19588
|
for (const server of servers) {
|
|
19428
|
-
const serverTools = tools.filter((t) => t.name.startsWith(
|
|
19589
|
+
const serverTools = tools.filter((t) => t.name.startsWith(mcpServerPrefix(server)));
|
|
19429
19590
|
lines.push(`
|
|
19430
19591
|
\u25CF ${server} (${serverTools.length} tools)`);
|
|
19431
19592
|
serverTools.forEach((t) => lines.push(` - ${t.name.split("::")[2]} \u2014 ${t.description.replace(`[MCP:${server}] `, "")}`));
|