cascade-ai 0.15.1 → 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/cli.cjs
CHANGED
|
@@ -109,7 +109,7 @@ var __export = (target, all) => {
|
|
|
109
109
|
var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SECRET_FILE, GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_RUNTIME_DB_FILE, DEFAULT_DASHBOARD_PORT, DEFAULT_CONTEXT_LIMIT, DEFAULT_AUTO_SUMMARIZE_AT, MODELS, T1_MODEL_PRIORITY, T2_MODEL_PRIORITY, T3_MODEL_PRIORITY, VISION_MODEL_PRIORITY, COMPLEXITY_T2_COUNT, THEME_NAMES, DEFAULT_THEME, OLLAMA_BASE_URL, LM_STUDIO_BASE_URL, AZURE_BASE_URL_TEMPLATE, TOOL_NAMES, DEFAULT_APPROVAL_REQUIRED;
|
|
110
110
|
var init_constants = __esm({
|
|
111
111
|
"src/constants.ts"() {
|
|
112
|
-
CASCADE_VERSION = "0.
|
|
112
|
+
CASCADE_VERSION = "0.16.0";
|
|
113
113
|
CASCADE_CONFIG_FILE = ".cascade/config.json";
|
|
114
114
|
CASCADE_DB_FILE = ".cascade/memory.db";
|
|
115
115
|
CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
|
|
@@ -3230,6 +3230,11 @@ function validateConfig(raw) {
|
|
|
3230
3230
|
|
|
3231
3231
|
// src/config/index.ts
|
|
3232
3232
|
init_constants();
|
|
3233
|
+
var KEY_OPTIONAL_PROVIDER_TYPES = /* @__PURE__ */ new Set(["ollama", "openai-compatible"]);
|
|
3234
|
+
function hasUsableProvider(providers) {
|
|
3235
|
+
if (!providers?.length) return false;
|
|
3236
|
+
return providers.some((p) => KEY_OPTIONAL_PROVIDER_TYPES.has(p.type) || !!p.apiKey);
|
|
3237
|
+
}
|
|
3233
3238
|
var ConfigManager = class {
|
|
3234
3239
|
config;
|
|
3235
3240
|
keystore;
|
|
@@ -7518,6 +7523,10 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
7518
7523
|
const worker = new T3Worker(this.router, this.toolRegistry, this.id);
|
|
7519
7524
|
if (this.store) worker.setStore(this.store, taskId);
|
|
7520
7525
|
worker.setPeerBus(this.t3PeerBus);
|
|
7526
|
+
if (this.permissionEscalator) worker.setPermissionEscalator(this.permissionEscalator);
|
|
7527
|
+
if (this.toolCreator) worker.setToolCreator(this.toolCreator);
|
|
7528
|
+
worker.on("log", (e) => this.emit("log", e));
|
|
7529
|
+
worker.on("tier:status", (e) => this.emit("tier:status", e));
|
|
7521
7530
|
worker.on("stream:token", (e) => this.emit("stream:token", e));
|
|
7522
7531
|
worker.on("tool:approval-request", (e) => this.emit("tool:approval-request", {
|
|
7523
7532
|
...e,
|
|
@@ -7869,12 +7878,14 @@ Board guidance (must be followed in the plan): ${decision.note}`,
|
|
|
7869
7878
|
status: "IN_PROGRESS"
|
|
7870
7879
|
});
|
|
7871
7880
|
const okBefore = okCount(allT2Results);
|
|
7881
|
+
const completedSummary = this.summarizeCompletedSections(allT2Results);
|
|
7882
|
+
const correctionContext = [systemContext, completedSummary].filter(Boolean).join("\n\n") || void 0;
|
|
7872
7883
|
const correctionPlan = await this.decomposeTask(`The previous execution plan failed to fully satisfy the original goal or encountered errors.
|
|
7873
7884
|
Review reason: ${reviewResult.reason}
|
|
7874
7885
|
|
|
7875
7886
|
Original goal: ${enrichedPrompt}
|
|
7876
7887
|
|
|
7877
|
-
Create a CORRECTION PLAN that contains only the new sections needed to fix the issues. Do not repeat successful sections
|
|
7888
|
+
Create a CORRECTION PLAN that contains only the new sections needed to fix the issues. Do not repeat successful sections.`, correctionContext);
|
|
7878
7889
|
const correctionResults = await this.dispatchT2Managers(correctionPlan.sections);
|
|
7879
7890
|
allT2Results = [...allT2Results, ...correctionResults];
|
|
7880
7891
|
if (okCount(allT2Results) <= okBefore) {
|
|
@@ -7977,6 +7988,15 @@ In 3-5 terse bullets, flag the most important RISKS, GAPS, or over-/under-decomp
|
|
|
7977
7988
|
return null;
|
|
7978
7989
|
}
|
|
7979
7990
|
}
|
|
7991
|
+
/** Structured, grounded summary of what's already done — used to keep
|
|
7992
|
+
* corrective replan passes from re-emitting completed sections. */
|
|
7993
|
+
summarizeCompletedSections(results) {
|
|
7994
|
+
const done = results.filter((r) => r.status === "COMPLETED" || r.status === "PARTIAL");
|
|
7995
|
+
if (done.length === 0) return "";
|
|
7996
|
+
const lines = done.map((r) => `- "${r.sectionTitle}" [${r.status}]: ${(r.sectionSummary || "(no summary)").slice(0, 300)}`);
|
|
7997
|
+
return `ALREADY COMPLETED SECTIONS (do not redo these \u2014 plan only what's still missing):
|
|
7998
|
+
${lines.join("\n")}`;
|
|
7999
|
+
}
|
|
7980
8000
|
async decomposeTask(prompt, systemContext) {
|
|
7981
8001
|
const db = this.router.getWorldStateDB?.();
|
|
7982
8002
|
let worldStateContext = "";
|
|
@@ -8244,7 +8264,7 @@ Leave dependsOn empty for sections that can run immediately in parallel.`;
|
|
|
8244
8264
|
}
|
|
8245
8265
|
}
|
|
8246
8266
|
if (readyIds.length === 0) return;
|
|
8247
|
-
|
|
8267
|
+
const runOne = async (id) => {
|
|
8248
8268
|
resultMap.set(id, null);
|
|
8249
8269
|
const index = sections.findIndex((s) => s.sectionId === id);
|
|
8250
8270
|
const section = sections[index];
|
|
@@ -8284,7 +8304,14 @@ Leave dependsOn empty for sections that can run immediately in parallel.`;
|
|
|
8284
8304
|
for (const dependentId of adj.get(id) ?? /* @__PURE__ */ new Set()) {
|
|
8285
8305
|
inDegree.set(dependentId, Math.max(0, (inDegree.get(dependentId) ?? 1) - 1));
|
|
8286
8306
|
}
|
|
8287
|
-
}
|
|
8307
|
+
};
|
|
8308
|
+
if (this.router.getT3ExecutionMode?.() === "sequential") {
|
|
8309
|
+
for (const id of readyIds) {
|
|
8310
|
+
await runOne(id);
|
|
8311
|
+
}
|
|
8312
|
+
} else {
|
|
8313
|
+
await Promise.all(readyIds.map(runOne));
|
|
8314
|
+
}
|
|
8288
8315
|
if (Array.from(inDegree.values()).some((deg) => deg === 0) && resultMap.size < totalSections) {
|
|
8289
8316
|
await executeWave();
|
|
8290
8317
|
}
|
|
@@ -10088,6 +10115,13 @@ var PermissionEscalator = class extends EventEmitter__default.default {
|
|
|
10088
10115
|
* All T3 workers under the same T2 share cached decisions for the same tool.
|
|
10089
10116
|
*/
|
|
10090
10117
|
sessionCache = /* @__PURE__ */ new Map();
|
|
10118
|
+
/**
|
|
10119
|
+
* Task-wide cache keyed by `toolName` alone, for USER- and T1-level
|
|
10120
|
+
* "always" decisions — these are meant to cover every sibling T2/T3 in the
|
|
10121
|
+
* run, not just the one that happened to ask first (see PermissionDecision
|
|
10122
|
+
* doc comment: "task-wide for T1").
|
|
10123
|
+
*/
|
|
10124
|
+
taskWideCache = /* @__PURE__ */ new Map();
|
|
10091
10125
|
t2Evaluator;
|
|
10092
10126
|
t1Evaluator;
|
|
10093
10127
|
/** Pending user-decision resolvers keyed by request ID */
|
|
@@ -10116,6 +10150,15 @@ var PermissionEscalator = class extends EventEmitter__default.default {
|
|
|
10116
10150
|
* Returns a PermissionDecision from whichever tier was able to decide.
|
|
10117
10151
|
*/
|
|
10118
10152
|
async requestPermission(req) {
|
|
10153
|
+
if (!req.forceReprompt && this.taskWideCache.has(req.toolName)) {
|
|
10154
|
+
return {
|
|
10155
|
+
requestId: req.id,
|
|
10156
|
+
approved: this.taskWideCache.get(req.toolName),
|
|
10157
|
+
always: true,
|
|
10158
|
+
decidedBy: "T1",
|
|
10159
|
+
reasoning: "Cached from a previous task-wide decision in this session"
|
|
10160
|
+
};
|
|
10161
|
+
}
|
|
10119
10162
|
const cacheKey = `${req.parentT2Id}:${req.toolName}`;
|
|
10120
10163
|
if (!req.forceReprompt && this.sessionCache.has(cacheKey)) {
|
|
10121
10164
|
return {
|
|
@@ -10160,7 +10203,7 @@ var PermissionEscalator = class extends EventEmitter__default.default {
|
|
|
10160
10203
|
try {
|
|
10161
10204
|
const t1Decision = await this.t1Evaluator(req);
|
|
10162
10205
|
if (t1Decision !== null) {
|
|
10163
|
-
if (t1Decision.always) this.
|
|
10206
|
+
if (t1Decision.always) this.taskWideCache.set(req.toolName, t1Decision.approved);
|
|
10164
10207
|
return t1Decision;
|
|
10165
10208
|
}
|
|
10166
10209
|
} catch {
|
|
@@ -10190,7 +10233,7 @@ var PermissionEscalator = class extends EventEmitter__default.default {
|
|
|
10190
10233
|
const wrappedResolver = (decision) => {
|
|
10191
10234
|
if (timer) clearTimeout(timer);
|
|
10192
10235
|
if (decision.always) {
|
|
10193
|
-
this.
|
|
10236
|
+
this.taskWideCache.set(req.toolName, decision.approved);
|
|
10194
10237
|
}
|
|
10195
10238
|
resolve(decision);
|
|
10196
10239
|
};
|
|
@@ -15269,11 +15312,18 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
15269
15312
|
}
|
|
15270
15313
|
});
|
|
15271
15314
|
const currentEntry = state.entries[state.currentEntryIdx];
|
|
15315
|
+
const rejectIfBlank = React2.useCallback((val, message) => {
|
|
15316
|
+
if (val.trim()) return false;
|
|
15317
|
+
dispatch({ type: "SET_ERROR", error: message });
|
|
15318
|
+
return true;
|
|
15319
|
+
}, []);
|
|
15272
15320
|
const handleFieldSubmit = React2.useCallback((val) => {
|
|
15273
15321
|
if (!currentEntry) return;
|
|
15274
15322
|
if (currentEntry.type === "azure") {
|
|
15275
15323
|
if (fieldStage === "deploymentName") {
|
|
15324
|
+
if (rejectIfBlank(val, "Deployment name is required.")) return;
|
|
15276
15325
|
dispatch({ type: "SET_ENTRY_FIELD", field: "deploymentName", value: val });
|
|
15326
|
+
dispatch({ type: "SET_ERROR", error: "" });
|
|
15277
15327
|
setFieldBuffer("");
|
|
15278
15328
|
if (currentEntry.baseUrl && currentEntry.apiKey && currentEntry.apiVersion) {
|
|
15279
15329
|
setFieldStage("askMore");
|
|
@@ -15281,38 +15331,50 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
15281
15331
|
setFieldStage("baseUrl");
|
|
15282
15332
|
}
|
|
15283
15333
|
} else if (fieldStage === "baseUrl") {
|
|
15334
|
+
if (rejectIfBlank(val, "Azure endpoint URL is required.")) return;
|
|
15284
15335
|
dispatch({ type: "SET_ENTRY_FIELD", field: "baseUrl", value: val });
|
|
15336
|
+
dispatch({ type: "SET_ERROR", error: "" });
|
|
15285
15337
|
setFieldBuffer("");
|
|
15286
15338
|
setFieldStage("apiKey");
|
|
15287
15339
|
} else if (fieldStage === "apiKey") {
|
|
15340
|
+
if (rejectIfBlank(val, "API key is required.")) return;
|
|
15288
15341
|
dispatch({ type: "SET_ENTRY_FIELD", field: "apiKey", value: val });
|
|
15342
|
+
dispatch({ type: "SET_ERROR", error: "" });
|
|
15289
15343
|
setFieldBuffer("");
|
|
15290
15344
|
setFieldStage("apiVersion");
|
|
15291
15345
|
} else if (fieldStage === "apiVersion") {
|
|
15292
15346
|
dispatch({ type: "SET_ENTRY_FIELD", field: "apiVersion", value: val || "2024-08-01-preview" });
|
|
15347
|
+
dispatch({ type: "SET_ERROR", error: "" });
|
|
15293
15348
|
setFieldBuffer("");
|
|
15294
15349
|
setFieldStage("askMore");
|
|
15295
15350
|
}
|
|
15296
15351
|
} else if (currentEntry.type === "openai-compatible") {
|
|
15297
15352
|
if (fieldStage === "label") {
|
|
15298
15353
|
dispatch({ type: "SET_ENTRY_FIELD", field: "label", value: val || currentEntry.label });
|
|
15354
|
+
dispatch({ type: "SET_ERROR", error: "" });
|
|
15299
15355
|
setFieldBuffer("");
|
|
15300
15356
|
setFieldStage("baseUrl");
|
|
15301
15357
|
} else if (fieldStage === "baseUrl") {
|
|
15358
|
+
if (rejectIfBlank(val, "Base URL is required.")) return;
|
|
15302
15359
|
dispatch({ type: "SET_ENTRY_FIELD", field: "baseUrl", value: val });
|
|
15360
|
+
dispatch({ type: "SET_ERROR", error: "" });
|
|
15303
15361
|
setFieldBuffer("");
|
|
15304
15362
|
setFieldStage("apiKey");
|
|
15305
15363
|
} else if (fieldStage === "apiKey") {
|
|
15306
15364
|
dispatch({ type: "SET_ENTRY_FIELD", field: "apiKey", value: val });
|
|
15365
|
+
dispatch({ type: "SET_ERROR", error: "" });
|
|
15307
15366
|
setFieldBuffer("");
|
|
15308
15367
|
setFieldStage("askMore");
|
|
15309
15368
|
}
|
|
15310
15369
|
} else if (currentEntry.type === "ollama") {
|
|
15311
15370
|
dispatch({ type: "SET_ENTRY_FIELD", field: "baseUrl", value: val || "http://localhost:11434" });
|
|
15371
|
+
dispatch({ type: "SET_ERROR", error: "" });
|
|
15312
15372
|
setFieldBuffer("");
|
|
15313
15373
|
setFieldStage("askMore");
|
|
15314
15374
|
} else {
|
|
15375
|
+
if (rejectIfBlank(val, "API key is required.")) return;
|
|
15315
15376
|
dispatch({ type: "SET_ENTRY_FIELD", field: "apiKey", value: val });
|
|
15377
|
+
dispatch({ type: "SET_ERROR", error: "" });
|
|
15316
15378
|
setFieldBuffer("");
|
|
15317
15379
|
const nextEntry = state.entries[state.currentEntryIdx + 1];
|
|
15318
15380
|
if (nextEntry) {
|
|
@@ -15320,7 +15382,7 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
15320
15382
|
}
|
|
15321
15383
|
dispatch({ type: "NEXT_ENTRY" });
|
|
15322
15384
|
}
|
|
15323
|
-
}, [currentEntry, fieldStage]);
|
|
15385
|
+
}, [currentEntry, fieldStage, rejectIfBlank, state.entries, state.currentEntryIdx]);
|
|
15324
15386
|
if (state.step === "PROVIDER_SELECT") {
|
|
15325
15387
|
return /* @__PURE__ */ jsxRuntime.jsxs(Frame, { theme, phase: "keys", children: [
|
|
15326
15388
|
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginBottom: 1, children: [
|
|
@@ -16255,6 +16317,16 @@ var DashboardServer = class {
|
|
|
16255
16317
|
getSocket() {
|
|
16256
16318
|
return this.socket;
|
|
16257
16319
|
}
|
|
16320
|
+
/**
|
|
16321
|
+
* Rebind the workspace tasks execute in — e.g. the desktop app's Code view
|
|
16322
|
+
* opening a different project folder — without tearing down the socket
|
|
16323
|
+
* server (which would drop the port/auth token/connection mid-session).
|
|
16324
|
+
* The next `cascade:run` picks this up immediately since `this.workspacePath`
|
|
16325
|
+
* is read live per-run (see onCascadeRun below).
|
|
16326
|
+
*/
|
|
16327
|
+
setWorkspacePath(workspacePath) {
|
|
16328
|
+
this.workspacePath = workspacePath;
|
|
16329
|
+
}
|
|
16258
16330
|
/**
|
|
16259
16331
|
* Write the in-memory config back to the workspace config file so mutations
|
|
16260
16332
|
* made over the socket (Settings → Save) persist across restarts. Best-effort:
|
|
@@ -17558,7 +17630,7 @@ async function startRepl(options) {
|
|
|
17558
17630
|
process.exit(1);
|
|
17559
17631
|
}
|
|
17560
17632
|
let config = cm.getConfig();
|
|
17561
|
-
const needsSetup = !config.providers
|
|
17633
|
+
const needsSetup = !hasUsableProvider(config.providers);
|
|
17562
17634
|
if (needsSetup) {
|
|
17563
17635
|
console.log(chalk9__default.default.magenta(" \u25C8 No providers configured \u2014 launching setup wizard\u2026"));
|
|
17564
17636
|
console.log();
|
|
@@ -17599,7 +17671,7 @@ async function runHeadless(prompt, options) {
|
|
|
17599
17671
|
process.exit(1);
|
|
17600
17672
|
}
|
|
17601
17673
|
const config = cm.getConfig();
|
|
17602
|
-
const needsSetup = !config.providers
|
|
17674
|
+
const needsSetup = !hasUsableProvider(config.providers);
|
|
17603
17675
|
if (needsSetup) {
|
|
17604
17676
|
console.error(chalk9__default.default.red("No providers configured. Run `cascade init` first."));
|
|
17605
17677
|
process.exit(1);
|