cascade-ai 0.2.0 → 0.2.2
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 +10 -3
- package/bin/cascade.js +3 -3
- package/completions/cascade.bash +23 -23
- package/completions/cascade.fish +20 -20
- package/completions/cascade.zsh +32 -32
- package/dist/cli.cjs +317 -106
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +317 -106
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +47 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +47 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/web/dist/assets/{index-qNI_hqt1.js → index-BdaS_Mbj.js} +69 -69
- package/web/dist/index.html +13 -13
package/dist/index.cjs
CHANGED
|
@@ -164,7 +164,7 @@ var require_keytar2 = __commonJS({
|
|
|
164
164
|
});
|
|
165
165
|
|
|
166
166
|
// src/constants.ts
|
|
167
|
-
var CASCADE_VERSION = "0.
|
|
167
|
+
var CASCADE_VERSION = "0.2.2";
|
|
168
168
|
var CASCADE_CONFIG_DIR = ".cascade";
|
|
169
169
|
var CASCADE_MD_FILE = "CASCADE.md";
|
|
170
170
|
var CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
@@ -1284,6 +1284,15 @@ var ModelSelector = class {
|
|
|
1284
1284
|
markProviderUnavailable(provider) {
|
|
1285
1285
|
this.availableProviders.delete(provider);
|
|
1286
1286
|
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Re-add a provider to the available set after it has recovered (e.g. after
|
|
1289
|
+
* a failover timeout expires or a successful call confirms recovery). Only
|
|
1290
|
+
* re-enables providers that were originally configured — callers should
|
|
1291
|
+
* guard against enabling providers that were never configured.
|
|
1292
|
+
*/
|
|
1293
|
+
markProviderAvailable(provider) {
|
|
1294
|
+
this.availableProviders.add(provider);
|
|
1295
|
+
}
|
|
1287
1296
|
resolveDynamicModel(overrideModelId) {
|
|
1288
1297
|
let providerStr = null;
|
|
1289
1298
|
let actualId = overrideModelId;
|
|
@@ -1353,10 +1362,23 @@ var FailoverManager = class {
|
|
|
1353
1362
|
if (!failure) return true;
|
|
1354
1363
|
if (Date.now() - failure.failedAt >= failure.retryAfterMs) {
|
|
1355
1364
|
this.failures.delete(provider);
|
|
1365
|
+
this.selector.markProviderAvailable(provider);
|
|
1356
1366
|
return true;
|
|
1357
1367
|
}
|
|
1358
1368
|
return false;
|
|
1359
1369
|
}
|
|
1370
|
+
/**
|
|
1371
|
+
* Call after a successful generation to immediately re-enable a provider
|
|
1372
|
+
* that had previously been marked unavailable. This allows fast recovery
|
|
1373
|
+
* when a transient rate-limit clears before the backoff window expires,
|
|
1374
|
+
* preventing unnecessary routing to more expensive fallback models.
|
|
1375
|
+
*/
|
|
1376
|
+
recordSuccess(provider) {
|
|
1377
|
+
if (this.failures.has(provider)) {
|
|
1378
|
+
this.failures.delete(provider);
|
|
1379
|
+
this.selector.markProviderAvailable(provider);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1360
1382
|
getFallbackModel(currentModel, tier) {
|
|
1361
1383
|
return this.selector.getNextFallback(currentModel.id, tier);
|
|
1362
1384
|
}
|
|
@@ -1373,6 +1395,7 @@ var FailoverManager = class {
|
|
|
1373
1395
|
}
|
|
1374
1396
|
clearFailure(provider) {
|
|
1375
1397
|
this.failures.delete(provider);
|
|
1398
|
+
this.selector.markProviderAvailable(provider);
|
|
1376
1399
|
}
|
|
1377
1400
|
};
|
|
1378
1401
|
|
|
@@ -1585,6 +1608,7 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
1585
1608
|
throw new Error(`Provider ${model.provider}:${model.id} returned an invalid generation result.`);
|
|
1586
1609
|
}
|
|
1587
1610
|
this.recordStats(tier, model, result.usage);
|
|
1611
|
+
this.failover.recordSuccess(model.provider);
|
|
1588
1612
|
return result;
|
|
1589
1613
|
} catch (err) {
|
|
1590
1614
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -2283,14 +2307,13 @@ Now execute your subtask using this context where relevant.`
|
|
|
2283
2307
|
await this.peerBus.barrier(this.id, barrierName, total);
|
|
2284
2308
|
}
|
|
2285
2309
|
receivePeerSync(fromId, content) {
|
|
2286
|
-
|
|
2287
|
-
if (existing) {
|
|
2288
|
-
existing.content = content;
|
|
2289
|
-
existing.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2290
|
-
} else {
|
|
2291
|
-
this.peerSyncBuffer.push({ fromId, content, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2292
|
-
}
|
|
2310
|
+
this.peerSyncBuffer.push({ fromId, content, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2293
2311
|
this.emit("peer-sync-received", { fromId, content });
|
|
2312
|
+
this.context.addMessage({
|
|
2313
|
+
role: "user",
|
|
2314
|
+
content: `[SYSTEM_NOTIFICATION]: You received a new peer message from ${fromId}. Use the "peer_message" tool with action="receive" to read it.`
|
|
2315
|
+
}).catch(() => {
|
|
2316
|
+
});
|
|
2294
2317
|
}
|
|
2295
2318
|
// ── Private ──────────────────────────────────
|
|
2296
2319
|
async runAgentLoop(systemPrompt, tools) {
|
|
@@ -2421,7 +2444,11 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
2421
2444
|
sendPeerSync: (to, syncType, content) => {
|
|
2422
2445
|
this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
|
|
2423
2446
|
},
|
|
2424
|
-
getPeerMessages: () =>
|
|
2447
|
+
getPeerMessages: () => {
|
|
2448
|
+
const msgs = [...this.peerSyncBuffer];
|
|
2449
|
+
this.peerSyncBuffer = [];
|
|
2450
|
+
return msgs;
|
|
2451
|
+
}
|
|
2425
2452
|
});
|
|
2426
2453
|
if (this.audit) {
|
|
2427
2454
|
this.audit.toolCall(this.id, tc.name, tc.input);
|
|
@@ -2963,13 +2990,17 @@ var T2Manager = class extends BaseTier {
|
|
|
2963
2990
|
}
|
|
2964
2991
|
// ── Private ──────────────────────────────────
|
|
2965
2992
|
async decomposeSection(assignment) {
|
|
2993
|
+
const peerPlans = this.peerSyncBuffer.filter((p) => p.content?.type === "T2_PLAN_ANNOUNCEMENT").map((p) => `[Peer ${p.fromId} Plan]: ${p.content.sectionTitle} - ${p.content.subtaskTitles?.join(", ")}`).join("\n");
|
|
2966
2994
|
const prompt = `Decompose this section into 2-5 concrete subtasks for T3 workers.
|
|
2967
2995
|
|
|
2968
2996
|
Section: ${assignment.sectionTitle}
|
|
2969
2997
|
Description: ${assignment.description}
|
|
2970
2998
|
Expected output: ${assignment.expectedOutput}
|
|
2971
2999
|
Constraints: ${assignment.constraints.join("; ")}
|
|
2972
|
-
|
|
3000
|
+
${peerPlans ? `
|
|
3001
|
+
Context from sibling T2 plans (use this to align execution and avoid overlaps):
|
|
3002
|
+
${peerPlans}
|
|
3003
|
+
` : ""}
|
|
2973
3004
|
Return a JSON array of subtask objects, each with:
|
|
2974
3005
|
- subtaskId: string (unique)
|
|
2975
3006
|
- subtaskTitle: string
|
|
@@ -3183,9 +3214,14 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
3183
3214
|
const completed = results.filter((r) => r.status === "COMPLETED");
|
|
3184
3215
|
if (!completed.length) return `Section ${assignment.sectionTitle} failed \u2014 no T3 workers completed.`;
|
|
3185
3216
|
const outputs = completed.map((r, i) => `[T3-${i + 1}]: ${r.output}`).join("\n\n");
|
|
3217
|
+
const peerOutputs = this.peerSyncBuffer.filter((p) => p.content?.type === "T2_SECTION_OUTPUT").map((p) => `[Peer ${p.fromId} Output]: ${p.content.output}`).join("\n\n");
|
|
3186
3218
|
const prompt = `Summarize these T3 worker outputs for section "${assignment.sectionTitle}" in 2-3 sentences:
|
|
3187
3219
|
|
|
3188
|
-
${outputs}
|
|
3220
|
+
${outputs}
|
|
3221
|
+
${peerOutputs ? `
|
|
3222
|
+
|
|
3223
|
+
Context from sibling T2 completed sections (use this to ensure your summary aligns with the overall state):
|
|
3224
|
+
${peerOutputs}` : ""}`;
|
|
3189
3225
|
const messages = [{ role: "user", content: prompt }];
|
|
3190
3226
|
try {
|
|
3191
3227
|
const result = await this.router.generate("T2", {
|