cascade-ai 0.15.2 → 0.16.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.
@@ -222595,7 +222595,7 @@ Anthropic.Models = Models2;
222595
222595
  Anthropic.Beta = Beta;
222596
222596
 
222597
222597
  // src/constants.ts
222598
- var CASCADE_VERSION = "0.15.2";
222598
+ var CASCADE_VERSION = "0.16.0";
222599
222599
  var CASCADE_CONFIG_DIR = ".cascade";
222600
222600
  var CASCADE_MD_FILE = "CASCADE.md";
222601
222601
  var CASCADE_IGNORE_FILE = ".cascadeignore";
@@ -253816,6 +253816,10 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
253816
253816
  const worker = new T3Worker(this.router, this.toolRegistry, this.id);
253817
253817
  if (this.store) worker.setStore(this.store, taskId);
253818
253818
  worker.setPeerBus(this.t3PeerBus);
253819
+ if (this.permissionEscalator) worker.setPermissionEscalator(this.permissionEscalator);
253820
+ if (this.toolCreator) worker.setToolCreator(this.toolCreator);
253821
+ worker.on("log", (e3) => this.emit("log", e3));
253822
+ worker.on("tier:status", (e3) => this.emit("tier:status", e3));
253819
253823
  worker.on("stream:token", (e3) => this.emit("stream:token", e3));
253820
253824
  worker.on("tool:approval-request", (e3) => this.emit("tool:approval-request", {
253821
253825
  ...e3,
@@ -254164,12 +254168,14 @@ Board guidance (must be followed in the plan): ${decision.note}`,
254164
254168
  status: "IN_PROGRESS"
254165
254169
  });
254166
254170
  const okBefore = okCount(allT2Results);
254171
+ const completedSummary = this.summarizeCompletedSections(allT2Results);
254172
+ const correctionContext = [systemContext, completedSummary].filter(Boolean).join("\n\n") || void 0;
254167
254173
  const correctionPlan = await this.decomposeTask(`The previous execution plan failed to fully satisfy the original goal or encountered errors.
254168
254174
  Review reason: ${reviewResult.reason}
254169
254175
 
254170
254176
  Original goal: ${enrichedPrompt}
254171
254177
 
254172
- Create a CORRECTION PLAN that contains only the new sections needed to fix the issues. Do not repeat successful sections.`);
254178
+ Create a CORRECTION PLAN that contains only the new sections needed to fix the issues. Do not repeat successful sections.`, correctionContext);
254173
254179
  const correctionResults = await this.dispatchT2Managers(correctionPlan.sections);
254174
254180
  allT2Results = [...allT2Results, ...correctionResults];
254175
254181
  if (okCount(allT2Results) <= okBefore) {
@@ -254272,6 +254278,15 @@ In 3-5 terse bullets, flag the most important RISKS, GAPS, or over-/under-decomp
254272
254278
  return null;
254273
254279
  }
254274
254280
  }
254281
+ /** Structured, grounded summary of what's already done — used to keep
254282
+ * corrective replan passes from re-emitting completed sections. */
254283
+ summarizeCompletedSections(results) {
254284
+ const done = results.filter((r4) => r4.status === "COMPLETED" || r4.status === "PARTIAL");
254285
+ if (done.length === 0) return "";
254286
+ const lines = done.map((r4) => `- "${r4.sectionTitle}" [${r4.status}]: ${(r4.sectionSummary || "(no summary)").slice(0, 300)}`);
254287
+ return `ALREADY COMPLETED SECTIONS (do not redo these \u2014 plan only what's still missing):
254288
+ ${lines.join("\n")}`;
254289
+ }
254275
254290
  async decomposeTask(prompt, systemContext) {
254276
254291
  const db = this.router.getWorldStateDB?.();
254277
254292
  let worldStateContext = "";
@@ -254539,7 +254554,7 @@ Leave dependsOn empty for sections that can run immediately in parallel.`;
254539
254554
  }
254540
254555
  }
254541
254556
  if (readyIds.length === 0) return;
254542
- await Promise.all(readyIds.map(async (id) => {
254557
+ const runOne = async (id) => {
254543
254558
  resultMap.set(id, null);
254544
254559
  const index = sections.findIndex((s3) => s3.sectionId === id);
254545
254560
  const section = sections[index];
@@ -254579,7 +254594,14 @@ Leave dependsOn empty for sections that can run immediately in parallel.`;
254579
254594
  for (const dependentId of adj.get(id) ?? /* @__PURE__ */ new Set()) {
254580
254595
  inDegree.set(dependentId, Math.max(0, (inDegree.get(dependentId) ?? 1) - 1));
254581
254596
  }
254582
- }));
254597
+ };
254598
+ if (this.router.getT3ExecutionMode?.() === "sequential") {
254599
+ for (const id of readyIds) {
254600
+ await runOne(id);
254601
+ }
254602
+ } else {
254603
+ await Promise.all(readyIds.map(runOne));
254604
+ }
254583
254605
  if (Array.from(inDegree.values()).some((deg) => deg === 0) && resultMap.size < totalSections) {
254584
254606
  await executeWave();
254585
254607
  }
@@ -294549,6 +294571,13 @@ var PermissionEscalator = class extends EventEmitter__default.default {
294549
294571
  * All T3 workers under the same T2 share cached decisions for the same tool.
294550
294572
  */
294551
294573
  sessionCache = /* @__PURE__ */ new Map();
294574
+ /**
294575
+ * Task-wide cache keyed by `toolName` alone, for USER- and T1-level
294576
+ * "always" decisions — these are meant to cover every sibling T2/T3 in the
294577
+ * run, not just the one that happened to ask first (see PermissionDecision
294578
+ * doc comment: "task-wide for T1").
294579
+ */
294580
+ taskWideCache = /* @__PURE__ */ new Map();
294552
294581
  t2Evaluator;
294553
294582
  t1Evaluator;
294554
294583
  /** Pending user-decision resolvers keyed by request ID */
@@ -294577,6 +294606,15 @@ var PermissionEscalator = class extends EventEmitter__default.default {
294577
294606
  * Returns a PermissionDecision from whichever tier was able to decide.
294578
294607
  */
294579
294608
  async requestPermission(req) {
294609
+ if (!req.forceReprompt && this.taskWideCache.has(req.toolName)) {
294610
+ return {
294611
+ requestId: req.id,
294612
+ approved: this.taskWideCache.get(req.toolName),
294613
+ always: true,
294614
+ decidedBy: "T1",
294615
+ reasoning: "Cached from a previous task-wide decision in this session"
294616
+ };
294617
+ }
294580
294618
  const cacheKey = `${req.parentT2Id}:${req.toolName}`;
294581
294619
  if (!req.forceReprompt && this.sessionCache.has(cacheKey)) {
294582
294620
  return {
@@ -294621,7 +294659,7 @@ var PermissionEscalator = class extends EventEmitter__default.default {
294621
294659
  try {
294622
294660
  const t1Decision = await this.t1Evaluator(req);
294623
294661
  if (t1Decision !== null) {
294624
- if (t1Decision.always) this.sessionCache.set(cacheKey, t1Decision.approved);
294662
+ if (t1Decision.always) this.taskWideCache.set(req.toolName, t1Decision.approved);
294625
294663
  return t1Decision;
294626
294664
  }
294627
294665
  } catch {
@@ -294651,7 +294689,7 @@ var PermissionEscalator = class extends EventEmitter__default.default {
294651
294689
  const wrappedResolver = (decision) => {
294652
294690
  if (timer) clearTimeout(timer);
294653
294691
  if (decision.always) {
294654
- this.sessionCache.set(`${req.parentT2Id}:${req.toolName}`, decision.approved);
294692
+ this.taskWideCache.set(req.toolName, decision.approved);
294655
294693
  }
294656
294694
  resolve(decision);
294657
294695
  };
@@ -299359,6 +299397,16 @@ var DashboardServer = class {
299359
299397
  getSocket() {
299360
299398
  return this.socket;
299361
299399
  }
299400
+ /**
299401
+ * Rebind the workspace tasks execute in — e.g. the desktop app's Code view
299402
+ * opening a different project folder — without tearing down the socket
299403
+ * server (which would drop the port/auth token/connection mid-session).
299404
+ * The next `cascade:run` picks this up immediately since `this.workspacePath`
299405
+ * is read live per-run (see onCascadeRun below).
299406
+ */
299407
+ setWorkspacePath(workspacePath) {
299408
+ this.workspacePath = workspacePath;
299409
+ }
299362
299410
  /**
299363
299411
  * Write the in-memory config back to the workspace config file so mutations
299364
299412
  * made over the socket (Settings → Save) persist across restarts. Best-effort:
package/dist/index.cjs CHANGED
@@ -236,7 +236,7 @@ var init_audit_logger = __esm({
236
236
  });
237
237
 
238
238
  // src/constants.ts
239
- var CASCADE_VERSION = "0.15.2";
239
+ var CASCADE_VERSION = "0.16.0";
240
240
  var CASCADE_CONFIG_DIR = ".cascade";
241
241
  var CASCADE_MD_FILE = "CASCADE.md";
242
242
  var CASCADE_IGNORE_FILE = ".cascadeignore";
@@ -5552,6 +5552,10 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
5552
5552
  const worker = new T3Worker(this.router, this.toolRegistry, this.id);
5553
5553
  if (this.store) worker.setStore(this.store, taskId);
5554
5554
  worker.setPeerBus(this.t3PeerBus);
5555
+ if (this.permissionEscalator) worker.setPermissionEscalator(this.permissionEscalator);
5556
+ if (this.toolCreator) worker.setToolCreator(this.toolCreator);
5557
+ worker.on("log", (e) => this.emit("log", e));
5558
+ worker.on("tier:status", (e) => this.emit("tier:status", e));
5555
5559
  worker.on("stream:token", (e) => this.emit("stream:token", e));
5556
5560
  worker.on("tool:approval-request", (e) => this.emit("tool:approval-request", {
5557
5561
  ...e,
@@ -5900,12 +5904,14 @@ Board guidance (must be followed in the plan): ${decision.note}`,
5900
5904
  status: "IN_PROGRESS"
5901
5905
  });
5902
5906
  const okBefore = okCount(allT2Results);
5907
+ const completedSummary = this.summarizeCompletedSections(allT2Results);
5908
+ const correctionContext = [systemContext, completedSummary].filter(Boolean).join("\n\n") || void 0;
5903
5909
  const correctionPlan = await this.decomposeTask(`The previous execution plan failed to fully satisfy the original goal or encountered errors.
5904
5910
  Review reason: ${reviewResult.reason}
5905
5911
 
5906
5912
  Original goal: ${enrichedPrompt}
5907
5913
 
5908
- Create a CORRECTION PLAN that contains only the new sections needed to fix the issues. Do not repeat successful sections.`);
5914
+ Create a CORRECTION PLAN that contains only the new sections needed to fix the issues. Do not repeat successful sections.`, correctionContext);
5909
5915
  const correctionResults = await this.dispatchT2Managers(correctionPlan.sections);
5910
5916
  allT2Results = [...allT2Results, ...correctionResults];
5911
5917
  if (okCount(allT2Results) <= okBefore) {
@@ -6008,6 +6014,15 @@ In 3-5 terse bullets, flag the most important RISKS, GAPS, or over-/under-decomp
6008
6014
  return null;
6009
6015
  }
6010
6016
  }
6017
+ /** Structured, grounded summary of what's already done — used to keep
6018
+ * corrective replan passes from re-emitting completed sections. */
6019
+ summarizeCompletedSections(results) {
6020
+ const done = results.filter((r) => r.status === "COMPLETED" || r.status === "PARTIAL");
6021
+ if (done.length === 0) return "";
6022
+ const lines = done.map((r) => `- "${r.sectionTitle}" [${r.status}]: ${(r.sectionSummary || "(no summary)").slice(0, 300)}`);
6023
+ return `ALREADY COMPLETED SECTIONS (do not redo these \u2014 plan only what's still missing):
6024
+ ${lines.join("\n")}`;
6025
+ }
6011
6026
  async decomposeTask(prompt, systemContext) {
6012
6027
  const db = this.router.getWorldStateDB?.();
6013
6028
  let worldStateContext = "";
@@ -6275,7 +6290,7 @@ Leave dependsOn empty for sections that can run immediately in parallel.`;
6275
6290
  }
6276
6291
  }
6277
6292
  if (readyIds.length === 0) return;
6278
- await Promise.all(readyIds.map(async (id) => {
6293
+ const runOne = async (id) => {
6279
6294
  resultMap.set(id, null);
6280
6295
  const index = sections.findIndex((s) => s.sectionId === id);
6281
6296
  const section = sections[index];
@@ -6315,7 +6330,14 @@ Leave dependsOn empty for sections that can run immediately in parallel.`;
6315
6330
  for (const dependentId of adj.get(id) ?? /* @__PURE__ */ new Set()) {
6316
6331
  inDegree.set(dependentId, Math.max(0, (inDegree.get(dependentId) ?? 1) - 1));
6317
6332
  }
6318
- }));
6333
+ };
6334
+ if (this.router.getT3ExecutionMode?.() === "sequential") {
6335
+ for (const id of readyIds) {
6336
+ await runOne(id);
6337
+ }
6338
+ } else {
6339
+ await Promise.all(readyIds.map(runOne));
6340
+ }
6319
6341
  if (Array.from(inDegree.values()).some((deg) => deg === 0) && resultMap.size < totalSections) {
6320
6342
  await executeWave();
6321
6343
  }
@@ -8113,6 +8135,13 @@ var PermissionEscalator = class extends EventEmitter__default.default {
8113
8135
  * All T3 workers under the same T2 share cached decisions for the same tool.
8114
8136
  */
8115
8137
  sessionCache = /* @__PURE__ */ new Map();
8138
+ /**
8139
+ * Task-wide cache keyed by `toolName` alone, for USER- and T1-level
8140
+ * "always" decisions — these are meant to cover every sibling T2/T3 in the
8141
+ * run, not just the one that happened to ask first (see PermissionDecision
8142
+ * doc comment: "task-wide for T1").
8143
+ */
8144
+ taskWideCache = /* @__PURE__ */ new Map();
8116
8145
  t2Evaluator;
8117
8146
  t1Evaluator;
8118
8147
  /** Pending user-decision resolvers keyed by request ID */
@@ -8141,6 +8170,15 @@ var PermissionEscalator = class extends EventEmitter__default.default {
8141
8170
  * Returns a PermissionDecision from whichever tier was able to decide.
8142
8171
  */
8143
8172
  async requestPermission(req) {
8173
+ if (!req.forceReprompt && this.taskWideCache.has(req.toolName)) {
8174
+ return {
8175
+ requestId: req.id,
8176
+ approved: this.taskWideCache.get(req.toolName),
8177
+ always: true,
8178
+ decidedBy: "T1",
8179
+ reasoning: "Cached from a previous task-wide decision in this session"
8180
+ };
8181
+ }
8144
8182
  const cacheKey = `${req.parentT2Id}:${req.toolName}`;
8145
8183
  if (!req.forceReprompt && this.sessionCache.has(cacheKey)) {
8146
8184
  return {
@@ -8185,7 +8223,7 @@ var PermissionEscalator = class extends EventEmitter__default.default {
8185
8223
  try {
8186
8224
  const t1Decision = await this.t1Evaluator(req);
8187
8225
  if (t1Decision !== null) {
8188
- if (t1Decision.always) this.sessionCache.set(cacheKey, t1Decision.approved);
8226
+ if (t1Decision.always) this.taskWideCache.set(req.toolName, t1Decision.approved);
8189
8227
  return t1Decision;
8190
8228
  }
8191
8229
  } catch {
@@ -8215,7 +8253,7 @@ var PermissionEscalator = class extends EventEmitter__default.default {
8215
8253
  const wrappedResolver = (decision) => {
8216
8254
  if (timer) clearTimeout(timer);
8217
8255
  if (decision.always) {
8218
- this.sessionCache.set(`${req.parentT2Id}:${req.toolName}`, decision.approved);
8256
+ this.taskWideCache.set(req.toolName, decision.approved);
8219
8257
  }
8220
8258
  resolve(decision);
8221
8259
  };
@@ -12134,6 +12172,16 @@ var DashboardServer = class {
12134
12172
  getSocket() {
12135
12173
  return this.socket;
12136
12174
  }
12175
+ /**
12176
+ * Rebind the workspace tasks execute in — e.g. the desktop app's Code view
12177
+ * opening a different project folder — without tearing down the socket
12178
+ * server (which would drop the port/auth token/connection mid-session).
12179
+ * The next `cascade:run` picks this up immediately since `this.workspacePath`
12180
+ * is read live per-run (see onCascadeRun below).
12181
+ */
12182
+ setWorkspacePath(workspacePath) {
12183
+ this.workspacePath = workspacePath;
12184
+ }
12137
12185
  /**
12138
12186
  * Write the in-memory config back to the workspace config file so mutations
12139
12187
  * made over the socket (Settings → Save) persist across restarts. Best-effort: