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.
- package/dist/cli.cjs +81 -9
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +81 -9
- package/dist/cli.js.map +1 -1
- package/dist/desktop-core.cjs +54 -6
- package/dist/index.cjs +54 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -4
- package/dist/index.d.ts +27 -4
- package/dist/index.js +54 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1574,10 +1574,15 @@ type T1Evaluator = (req: PermissionRequest) => Promise<PermissionDecision | null
|
|
|
1574
1574
|
* PermissionEscalator manages the hierarchical permission flow for a single task run.
|
|
1575
1575
|
*
|
|
1576
1576
|
* Decision cascade:
|
|
1577
|
-
* 1. Check
|
|
1578
|
-
*
|
|
1579
|
-
*
|
|
1580
|
-
*
|
|
1577
|
+
* 1. Check the task-wide cache (USER/T1 "always" decisions, keyed by `toolName`
|
|
1578
|
+
* alone) → return if hit. These are deliberately NOT scoped to one T2, so a
|
|
1579
|
+
* user's or T1's "Always" covers every sibling worker anywhere in the run.
|
|
1580
|
+
* 2. Check the per-T2 session cache (section-wide key `${t2Id}:${toolName}`)
|
|
1581
|
+
* → return if hit
|
|
1582
|
+
* 3. Ask T2 evaluator → if decision returned, cache (per-T2) + return
|
|
1583
|
+
* 4. Ask T1 evaluator → if decision returned, cache (task-wide) + return
|
|
1584
|
+
* 5. Emit `permission:user-required` → wait for external decision via
|
|
1585
|
+
* `resolveUserDecision()`; an "always" answer caches task-wide.
|
|
1581
1586
|
*/
|
|
1582
1587
|
declare class PermissionEscalator extends EventEmitter {
|
|
1583
1588
|
/**
|
|
@@ -1585,6 +1590,13 @@ declare class PermissionEscalator extends EventEmitter {
|
|
|
1585
1590
|
* All T3 workers under the same T2 share cached decisions for the same tool.
|
|
1586
1591
|
*/
|
|
1587
1592
|
private sessionCache;
|
|
1593
|
+
/**
|
|
1594
|
+
* Task-wide cache keyed by `toolName` alone, for USER- and T1-level
|
|
1595
|
+
* "always" decisions — these are meant to cover every sibling T2/T3 in the
|
|
1596
|
+
* run, not just the one that happened to ask first (see PermissionDecision
|
|
1597
|
+
* doc comment: "task-wide for T1").
|
|
1598
|
+
*/
|
|
1599
|
+
private taskWideCache;
|
|
1588
1600
|
private t2Evaluator?;
|
|
1589
1601
|
private t1Evaluator?;
|
|
1590
1602
|
/** Pending user-decision resolvers keyed by request ID */
|
|
@@ -1742,6 +1754,9 @@ declare class T1Administrator extends BaseTier {
|
|
|
1742
1754
|
* so it never blocks the approval gate.
|
|
1743
1755
|
*/
|
|
1744
1756
|
private reviewPlan;
|
|
1757
|
+
/** Structured, grounded summary of what's already done — used to keep
|
|
1758
|
+
* corrective replan passes from re-emitting completed sections. */
|
|
1759
|
+
private summarizeCompletedSections;
|
|
1745
1760
|
private decomposeTask;
|
|
1746
1761
|
private validatePlan;
|
|
1747
1762
|
private dispatchT2Managers;
|
|
@@ -2392,6 +2407,14 @@ declare class DashboardServer {
|
|
|
2392
2407
|
start(): Promise<void>;
|
|
2393
2408
|
stop(): Promise<void>;
|
|
2394
2409
|
getSocket(): DashboardSocket;
|
|
2410
|
+
/**
|
|
2411
|
+
* Rebind the workspace tasks execute in — e.g. the desktop app's Code view
|
|
2412
|
+
* opening a different project folder — without tearing down the socket
|
|
2413
|
+
* server (which would drop the port/auth token/connection mid-session).
|
|
2414
|
+
* The next `cascade:run` picks this up immediately since `this.workspacePath`
|
|
2415
|
+
* is read live per-run (see onCascadeRun below).
|
|
2416
|
+
*/
|
|
2417
|
+
setWorkspacePath(workspacePath: string): void;
|
|
2395
2418
|
/**
|
|
2396
2419
|
* Write the in-memory config back to the workspace config file so mutations
|
|
2397
2420
|
* made over the socket (Settings → Save) persist across restarts. Best-effort:
|
package/dist/index.d.ts
CHANGED
|
@@ -1574,10 +1574,15 @@ type T1Evaluator = (req: PermissionRequest) => Promise<PermissionDecision | null
|
|
|
1574
1574
|
* PermissionEscalator manages the hierarchical permission flow for a single task run.
|
|
1575
1575
|
*
|
|
1576
1576
|
* Decision cascade:
|
|
1577
|
-
* 1. Check
|
|
1578
|
-
*
|
|
1579
|
-
*
|
|
1580
|
-
*
|
|
1577
|
+
* 1. Check the task-wide cache (USER/T1 "always" decisions, keyed by `toolName`
|
|
1578
|
+
* alone) → return if hit. These are deliberately NOT scoped to one T2, so a
|
|
1579
|
+
* user's or T1's "Always" covers every sibling worker anywhere in the run.
|
|
1580
|
+
* 2. Check the per-T2 session cache (section-wide key `${t2Id}:${toolName}`)
|
|
1581
|
+
* → return if hit
|
|
1582
|
+
* 3. Ask T2 evaluator → if decision returned, cache (per-T2) + return
|
|
1583
|
+
* 4. Ask T1 evaluator → if decision returned, cache (task-wide) + return
|
|
1584
|
+
* 5. Emit `permission:user-required` → wait for external decision via
|
|
1585
|
+
* `resolveUserDecision()`; an "always" answer caches task-wide.
|
|
1581
1586
|
*/
|
|
1582
1587
|
declare class PermissionEscalator extends EventEmitter {
|
|
1583
1588
|
/**
|
|
@@ -1585,6 +1590,13 @@ declare class PermissionEscalator extends EventEmitter {
|
|
|
1585
1590
|
* All T3 workers under the same T2 share cached decisions for the same tool.
|
|
1586
1591
|
*/
|
|
1587
1592
|
private sessionCache;
|
|
1593
|
+
/**
|
|
1594
|
+
* Task-wide cache keyed by `toolName` alone, for USER- and T1-level
|
|
1595
|
+
* "always" decisions — these are meant to cover every sibling T2/T3 in the
|
|
1596
|
+
* run, not just the one that happened to ask first (see PermissionDecision
|
|
1597
|
+
* doc comment: "task-wide for T1").
|
|
1598
|
+
*/
|
|
1599
|
+
private taskWideCache;
|
|
1588
1600
|
private t2Evaluator?;
|
|
1589
1601
|
private t1Evaluator?;
|
|
1590
1602
|
/** Pending user-decision resolvers keyed by request ID */
|
|
@@ -1742,6 +1754,9 @@ declare class T1Administrator extends BaseTier {
|
|
|
1742
1754
|
* so it never blocks the approval gate.
|
|
1743
1755
|
*/
|
|
1744
1756
|
private reviewPlan;
|
|
1757
|
+
/** Structured, grounded summary of what's already done — used to keep
|
|
1758
|
+
* corrective replan passes from re-emitting completed sections. */
|
|
1759
|
+
private summarizeCompletedSections;
|
|
1745
1760
|
private decomposeTask;
|
|
1746
1761
|
private validatePlan;
|
|
1747
1762
|
private dispatchT2Managers;
|
|
@@ -2392,6 +2407,14 @@ declare class DashboardServer {
|
|
|
2392
2407
|
start(): Promise<void>;
|
|
2393
2408
|
stop(): Promise<void>;
|
|
2394
2409
|
getSocket(): DashboardSocket;
|
|
2410
|
+
/**
|
|
2411
|
+
* Rebind the workspace tasks execute in — e.g. the desktop app's Code view
|
|
2412
|
+
* opening a different project folder — without tearing down the socket
|
|
2413
|
+
* server (which would drop the port/auth token/connection mid-session).
|
|
2414
|
+
* The next `cascade:run` picks this up immediately since `this.workspacePath`
|
|
2415
|
+
* is read live per-run (see onCascadeRun below).
|
|
2416
|
+
*/
|
|
2417
|
+
setWorkspacePath(workspacePath: string): void;
|
|
2395
2418
|
/**
|
|
2396
2419
|
* Write the in-memory config back to the workspace config file so mutations
|
|
2397
2420
|
* made over the socket (Settings → Save) persist across restarts. Best-effort:
|
package/dist/index.js
CHANGED
|
@@ -190,7 +190,7 @@ var init_audit_logger = __esm({
|
|
|
190
190
|
});
|
|
191
191
|
|
|
192
192
|
// src/constants.ts
|
|
193
|
-
var CASCADE_VERSION = "0.
|
|
193
|
+
var CASCADE_VERSION = "0.16.0";
|
|
194
194
|
var CASCADE_CONFIG_DIR = ".cascade";
|
|
195
195
|
var CASCADE_MD_FILE = "CASCADE.md";
|
|
196
196
|
var CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
@@ -5506,6 +5506,10 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
5506
5506
|
const worker = new T3Worker(this.router, this.toolRegistry, this.id);
|
|
5507
5507
|
if (this.store) worker.setStore(this.store, taskId);
|
|
5508
5508
|
worker.setPeerBus(this.t3PeerBus);
|
|
5509
|
+
if (this.permissionEscalator) worker.setPermissionEscalator(this.permissionEscalator);
|
|
5510
|
+
if (this.toolCreator) worker.setToolCreator(this.toolCreator);
|
|
5511
|
+
worker.on("log", (e) => this.emit("log", e));
|
|
5512
|
+
worker.on("tier:status", (e) => this.emit("tier:status", e));
|
|
5509
5513
|
worker.on("stream:token", (e) => this.emit("stream:token", e));
|
|
5510
5514
|
worker.on("tool:approval-request", (e) => this.emit("tool:approval-request", {
|
|
5511
5515
|
...e,
|
|
@@ -5854,12 +5858,14 @@ Board guidance (must be followed in the plan): ${decision.note}`,
|
|
|
5854
5858
|
status: "IN_PROGRESS"
|
|
5855
5859
|
});
|
|
5856
5860
|
const okBefore = okCount(allT2Results);
|
|
5861
|
+
const completedSummary = this.summarizeCompletedSections(allT2Results);
|
|
5862
|
+
const correctionContext = [systemContext, completedSummary].filter(Boolean).join("\n\n") || void 0;
|
|
5857
5863
|
const correctionPlan = await this.decomposeTask(`The previous execution plan failed to fully satisfy the original goal or encountered errors.
|
|
5858
5864
|
Review reason: ${reviewResult.reason}
|
|
5859
5865
|
|
|
5860
5866
|
Original goal: ${enrichedPrompt}
|
|
5861
5867
|
|
|
5862
|
-
Create a CORRECTION PLAN that contains only the new sections needed to fix the issues. Do not repeat successful sections
|
|
5868
|
+
Create a CORRECTION PLAN that contains only the new sections needed to fix the issues. Do not repeat successful sections.`, correctionContext);
|
|
5863
5869
|
const correctionResults = await this.dispatchT2Managers(correctionPlan.sections);
|
|
5864
5870
|
allT2Results = [...allT2Results, ...correctionResults];
|
|
5865
5871
|
if (okCount(allT2Results) <= okBefore) {
|
|
@@ -5962,6 +5968,15 @@ In 3-5 terse bullets, flag the most important RISKS, GAPS, or over-/under-decomp
|
|
|
5962
5968
|
return null;
|
|
5963
5969
|
}
|
|
5964
5970
|
}
|
|
5971
|
+
/** Structured, grounded summary of what's already done — used to keep
|
|
5972
|
+
* corrective replan passes from re-emitting completed sections. */
|
|
5973
|
+
summarizeCompletedSections(results) {
|
|
5974
|
+
const done = results.filter((r) => r.status === "COMPLETED" || r.status === "PARTIAL");
|
|
5975
|
+
if (done.length === 0) return "";
|
|
5976
|
+
const lines = done.map((r) => `- "${r.sectionTitle}" [${r.status}]: ${(r.sectionSummary || "(no summary)").slice(0, 300)}`);
|
|
5977
|
+
return `ALREADY COMPLETED SECTIONS (do not redo these \u2014 plan only what's still missing):
|
|
5978
|
+
${lines.join("\n")}`;
|
|
5979
|
+
}
|
|
5965
5980
|
async decomposeTask(prompt, systemContext) {
|
|
5966
5981
|
const db = this.router.getWorldStateDB?.();
|
|
5967
5982
|
let worldStateContext = "";
|
|
@@ -6229,7 +6244,7 @@ Leave dependsOn empty for sections that can run immediately in parallel.`;
|
|
|
6229
6244
|
}
|
|
6230
6245
|
}
|
|
6231
6246
|
if (readyIds.length === 0) return;
|
|
6232
|
-
|
|
6247
|
+
const runOne = async (id) => {
|
|
6233
6248
|
resultMap.set(id, null);
|
|
6234
6249
|
const index = sections.findIndex((s) => s.sectionId === id);
|
|
6235
6250
|
const section = sections[index];
|
|
@@ -6269,7 +6284,14 @@ Leave dependsOn empty for sections that can run immediately in parallel.`;
|
|
|
6269
6284
|
for (const dependentId of adj.get(id) ?? /* @__PURE__ */ new Set()) {
|
|
6270
6285
|
inDegree.set(dependentId, Math.max(0, (inDegree.get(dependentId) ?? 1) - 1));
|
|
6271
6286
|
}
|
|
6272
|
-
}
|
|
6287
|
+
};
|
|
6288
|
+
if (this.router.getT3ExecutionMode?.() === "sequential") {
|
|
6289
|
+
for (const id of readyIds) {
|
|
6290
|
+
await runOne(id);
|
|
6291
|
+
}
|
|
6292
|
+
} else {
|
|
6293
|
+
await Promise.all(readyIds.map(runOne));
|
|
6294
|
+
}
|
|
6273
6295
|
if (Array.from(inDegree.values()).some((deg) => deg === 0) && resultMap.size < totalSections) {
|
|
6274
6296
|
await executeWave();
|
|
6275
6297
|
}
|
|
@@ -8067,6 +8089,13 @@ var PermissionEscalator = class extends EventEmitter {
|
|
|
8067
8089
|
* All T3 workers under the same T2 share cached decisions for the same tool.
|
|
8068
8090
|
*/
|
|
8069
8091
|
sessionCache = /* @__PURE__ */ new Map();
|
|
8092
|
+
/**
|
|
8093
|
+
* Task-wide cache keyed by `toolName` alone, for USER- and T1-level
|
|
8094
|
+
* "always" decisions — these are meant to cover every sibling T2/T3 in the
|
|
8095
|
+
* run, not just the one that happened to ask first (see PermissionDecision
|
|
8096
|
+
* doc comment: "task-wide for T1").
|
|
8097
|
+
*/
|
|
8098
|
+
taskWideCache = /* @__PURE__ */ new Map();
|
|
8070
8099
|
t2Evaluator;
|
|
8071
8100
|
t1Evaluator;
|
|
8072
8101
|
/** Pending user-decision resolvers keyed by request ID */
|
|
@@ -8095,6 +8124,15 @@ var PermissionEscalator = class extends EventEmitter {
|
|
|
8095
8124
|
* Returns a PermissionDecision from whichever tier was able to decide.
|
|
8096
8125
|
*/
|
|
8097
8126
|
async requestPermission(req) {
|
|
8127
|
+
if (!req.forceReprompt && this.taskWideCache.has(req.toolName)) {
|
|
8128
|
+
return {
|
|
8129
|
+
requestId: req.id,
|
|
8130
|
+
approved: this.taskWideCache.get(req.toolName),
|
|
8131
|
+
always: true,
|
|
8132
|
+
decidedBy: "T1",
|
|
8133
|
+
reasoning: "Cached from a previous task-wide decision in this session"
|
|
8134
|
+
};
|
|
8135
|
+
}
|
|
8098
8136
|
const cacheKey = `${req.parentT2Id}:${req.toolName}`;
|
|
8099
8137
|
if (!req.forceReprompt && this.sessionCache.has(cacheKey)) {
|
|
8100
8138
|
return {
|
|
@@ -8139,7 +8177,7 @@ var PermissionEscalator = class extends EventEmitter {
|
|
|
8139
8177
|
try {
|
|
8140
8178
|
const t1Decision = await this.t1Evaluator(req);
|
|
8141
8179
|
if (t1Decision !== null) {
|
|
8142
|
-
if (t1Decision.always) this.
|
|
8180
|
+
if (t1Decision.always) this.taskWideCache.set(req.toolName, t1Decision.approved);
|
|
8143
8181
|
return t1Decision;
|
|
8144
8182
|
}
|
|
8145
8183
|
} catch {
|
|
@@ -8169,7 +8207,7 @@ var PermissionEscalator = class extends EventEmitter {
|
|
|
8169
8207
|
const wrappedResolver = (decision) => {
|
|
8170
8208
|
if (timer) clearTimeout(timer);
|
|
8171
8209
|
if (decision.always) {
|
|
8172
|
-
this.
|
|
8210
|
+
this.taskWideCache.set(req.toolName, decision.approved);
|
|
8173
8211
|
}
|
|
8174
8212
|
resolve(decision);
|
|
8175
8213
|
};
|
|
@@ -12088,6 +12126,16 @@ var DashboardServer = class {
|
|
|
12088
12126
|
getSocket() {
|
|
12089
12127
|
return this.socket;
|
|
12090
12128
|
}
|
|
12129
|
+
/**
|
|
12130
|
+
* Rebind the workspace tasks execute in — e.g. the desktop app's Code view
|
|
12131
|
+
* opening a different project folder — without tearing down the socket
|
|
12132
|
+
* server (which would drop the port/auth token/connection mid-session).
|
|
12133
|
+
* The next `cascade:run` picks this up immediately since `this.workspacePath`
|
|
12134
|
+
* is read live per-run (see onCascadeRun below).
|
|
12135
|
+
*/
|
|
12136
|
+
setWorkspacePath(workspacePath) {
|
|
12137
|
+
this.workspacePath = workspacePath;
|
|
12138
|
+
}
|
|
12091
12139
|
/**
|
|
12092
12140
|
* Write the in-memory config back to the workspace config file so mutations
|
|
12093
12141
|
* made over the socket (Settings → Save) persist across restarts. Best-effort:
|