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/dist/index.d.cts CHANGED
@@ -540,6 +540,13 @@ declare class ModelSelector {
540
540
  private getPriorityList;
541
541
  isProviderAvailable(provider: ProviderType): boolean;
542
542
  markProviderUnavailable(provider: ProviderType): void;
543
+ /**
544
+ * Re-add a provider to the available set after it has recovered (e.g. after
545
+ * a failover timeout expires or a successful call confirms recovery). Only
546
+ * re-enables providers that were originally configured — callers should
547
+ * guard against enabling providers that were never configured.
548
+ */
549
+ markProviderAvailable(provider: ProviderType): void;
543
550
  private resolveDynamicModel;
544
551
  }
545
552
 
@@ -1527,7 +1534,7 @@ declare class Telemetry {
1527
1534
  shutdown(): Promise<void>;
1528
1535
  }
1529
1536
 
1530
- declare const CASCADE_VERSION = "0.1.2";
1537
+ declare const CASCADE_VERSION = "0.2.2";
1531
1538
  declare const CASCADE_CONFIG_DIR = ".cascade";
1532
1539
  declare const CASCADE_MD_FILE = "CASCADE.md";
1533
1540
  declare const CASCADE_IGNORE_FILE = ".cascadeignore";
package/dist/index.d.ts CHANGED
@@ -540,6 +540,13 @@ declare class ModelSelector {
540
540
  private getPriorityList;
541
541
  isProviderAvailable(provider: ProviderType): boolean;
542
542
  markProviderUnavailable(provider: ProviderType): void;
543
+ /**
544
+ * Re-add a provider to the available set after it has recovered (e.g. after
545
+ * a failover timeout expires or a successful call confirms recovery). Only
546
+ * re-enables providers that were originally configured — callers should
547
+ * guard against enabling providers that were never configured.
548
+ */
549
+ markProviderAvailable(provider: ProviderType): void;
543
550
  private resolveDynamicModel;
544
551
  }
545
552
 
@@ -1527,7 +1534,7 @@ declare class Telemetry {
1527
1534
  shutdown(): Promise<void>;
1528
1535
  }
1529
1536
 
1530
- declare const CASCADE_VERSION = "0.1.2";
1537
+ declare const CASCADE_VERSION = "0.2.2";
1531
1538
  declare const CASCADE_CONFIG_DIR = ".cascade";
1532
1539
  declare const CASCADE_MD_FILE = "CASCADE.md";
1533
1540
  declare const CASCADE_IGNORE_FILE = ".cascadeignore";
package/dist/index.js CHANGED
@@ -123,7 +123,7 @@ var require_keytar2 = __commonJS({
123
123
  });
124
124
 
125
125
  // src/constants.ts
126
- var CASCADE_VERSION = "0.1.2";
126
+ var CASCADE_VERSION = "0.2.2";
127
127
  var CASCADE_CONFIG_DIR = ".cascade";
128
128
  var CASCADE_MD_FILE = "CASCADE.md";
129
129
  var CASCADE_IGNORE_FILE = ".cascadeignore";
@@ -1243,6 +1243,15 @@ var ModelSelector = class {
1243
1243
  markProviderUnavailable(provider) {
1244
1244
  this.availableProviders.delete(provider);
1245
1245
  }
1246
+ /**
1247
+ * Re-add a provider to the available set after it has recovered (e.g. after
1248
+ * a failover timeout expires or a successful call confirms recovery). Only
1249
+ * re-enables providers that were originally configured — callers should
1250
+ * guard against enabling providers that were never configured.
1251
+ */
1252
+ markProviderAvailable(provider) {
1253
+ this.availableProviders.add(provider);
1254
+ }
1246
1255
  resolveDynamicModel(overrideModelId) {
1247
1256
  let providerStr = null;
1248
1257
  let actualId = overrideModelId;
@@ -1312,10 +1321,23 @@ var FailoverManager = class {
1312
1321
  if (!failure) return true;
1313
1322
  if (Date.now() - failure.failedAt >= failure.retryAfterMs) {
1314
1323
  this.failures.delete(provider);
1324
+ this.selector.markProviderAvailable(provider);
1315
1325
  return true;
1316
1326
  }
1317
1327
  return false;
1318
1328
  }
1329
+ /**
1330
+ * Call after a successful generation to immediately re-enable a provider
1331
+ * that had previously been marked unavailable. This allows fast recovery
1332
+ * when a transient rate-limit clears before the backoff window expires,
1333
+ * preventing unnecessary routing to more expensive fallback models.
1334
+ */
1335
+ recordSuccess(provider) {
1336
+ if (this.failures.has(provider)) {
1337
+ this.failures.delete(provider);
1338
+ this.selector.markProviderAvailable(provider);
1339
+ }
1340
+ }
1319
1341
  getFallbackModel(currentModel, tier) {
1320
1342
  return this.selector.getNextFallback(currentModel.id, tier);
1321
1343
  }
@@ -1332,6 +1354,7 @@ var FailoverManager = class {
1332
1354
  }
1333
1355
  clearFailure(provider) {
1334
1356
  this.failures.delete(provider);
1357
+ this.selector.markProviderAvailable(provider);
1335
1358
  }
1336
1359
  };
1337
1360
 
@@ -1544,6 +1567,7 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
1544
1567
  throw new Error(`Provider ${model.provider}:${model.id} returned an invalid generation result.`);
1545
1568
  }
1546
1569
  this.recordStats(tier, model, result.usage);
1570
+ this.failover.recordSuccess(model.provider);
1547
1571
  return result;
1548
1572
  } catch (err) {
1549
1573
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -2242,14 +2266,13 @@ Now execute your subtask using this context where relevant.`
2242
2266
  await this.peerBus.barrier(this.id, barrierName, total);
2243
2267
  }
2244
2268
  receivePeerSync(fromId, content) {
2245
- const existing = this.peerSyncBuffer.find((p) => p.fromId === fromId);
2246
- if (existing) {
2247
- existing.content = content;
2248
- existing.timestamp = (/* @__PURE__ */ new Date()).toISOString();
2249
- } else {
2250
- this.peerSyncBuffer.push({ fromId, content, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
2251
- }
2269
+ this.peerSyncBuffer.push({ fromId, content, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
2252
2270
  this.emit("peer-sync-received", { fromId, content });
2271
+ this.context.addMessage({
2272
+ role: "user",
2273
+ content: `[SYSTEM_NOTIFICATION]: You received a new peer message from ${fromId}. Use the "peer_message" tool with action="receive" to read it.`
2274
+ }).catch(() => {
2275
+ });
2253
2276
  }
2254
2277
  // ── Private ──────────────────────────────────
2255
2278
  async runAgentLoop(systemPrompt, tools) {
@@ -2380,7 +2403,11 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
2380
2403
  sendPeerSync: (to, syncType, content) => {
2381
2404
  this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
2382
2405
  },
2383
- getPeerMessages: () => [...this.peerSyncBuffer]
2406
+ getPeerMessages: () => {
2407
+ const msgs = [...this.peerSyncBuffer];
2408
+ this.peerSyncBuffer = [];
2409
+ return msgs;
2410
+ }
2384
2411
  });
2385
2412
  if (this.audit) {
2386
2413
  this.audit.toolCall(this.id, tc.name, tc.input);
@@ -2922,13 +2949,17 @@ var T2Manager = class extends BaseTier {
2922
2949
  }
2923
2950
  // ── Private ──────────────────────────────────
2924
2951
  async decomposeSection(assignment) {
2952
+ 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");
2925
2953
  const prompt = `Decompose this section into 2-5 concrete subtasks for T3 workers.
2926
2954
 
2927
2955
  Section: ${assignment.sectionTitle}
2928
2956
  Description: ${assignment.description}
2929
2957
  Expected output: ${assignment.expectedOutput}
2930
2958
  Constraints: ${assignment.constraints.join("; ")}
2931
-
2959
+ ${peerPlans ? `
2960
+ Context from sibling T2 plans (use this to align execution and avoid overlaps):
2961
+ ${peerPlans}
2962
+ ` : ""}
2932
2963
  Return a JSON array of subtask objects, each with:
2933
2964
  - subtaskId: string (unique)
2934
2965
  - subtaskTitle: string
@@ -3142,9 +3173,14 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
3142
3173
  const completed = results.filter((r) => r.status === "COMPLETED");
3143
3174
  if (!completed.length) return `Section ${assignment.sectionTitle} failed \u2014 no T3 workers completed.`;
3144
3175
  const outputs = completed.map((r, i) => `[T3-${i + 1}]: ${r.output}`).join("\n\n");
3176
+ const peerOutputs = this.peerSyncBuffer.filter((p) => p.content?.type === "T2_SECTION_OUTPUT").map((p) => `[Peer ${p.fromId} Output]: ${p.content.output}`).join("\n\n");
3145
3177
  const prompt = `Summarize these T3 worker outputs for section "${assignment.sectionTitle}" in 2-3 sentences:
3146
3178
 
3147
- ${outputs}`;
3179
+ ${outputs}
3180
+ ${peerOutputs ? `
3181
+
3182
+ Context from sibling T2 completed sections (use this to ensure your summary aligns with the overall state):
3183
+ ${peerOutputs}` : ""}`;
3148
3184
  const messages = [{ role: "user", content: prompt }];
3149
3185
  try {
3150
3186
  const result = await this.router.generate("T2", {