cascade-ai 0.12.21 → 0.12.23

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.js CHANGED
@@ -58,7 +58,7 @@ var __export = (target, all) => {
58
58
  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;
59
59
  var init_constants = __esm({
60
60
  "src/constants.ts"() {
61
- CASCADE_VERSION = "0.12.21";
61
+ CASCADE_VERSION = "0.12.23";
62
62
  CASCADE_CONFIG_FILE = ".cascade/config.json";
63
63
  CASCADE_DB_FILE = ".cascade/memory.db";
64
64
  CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
@@ -14780,7 +14780,8 @@ var DashboardSocket = class {
14780
14780
  this.io.on("connection", (socket) => {
14781
14781
  socket.on("cascade:run", (payload) => {
14782
14782
  if (typeof payload?.prompt === "string" && payload.prompt.trim()) {
14783
- callback(payload.prompt.trim(), payload.model ?? "auto", socket.id);
14783
+ const sessionId = typeof payload.sessionId === "string" && payload.sessionId.trim() ? payload.sessionId.trim() : void 0;
14784
+ callback(payload.prompt.trim(), payload.model ?? "auto", socket.id, sessionId);
14784
14785
  }
14785
14786
  });
14786
14787
  });
@@ -14861,18 +14862,18 @@ var DashboardServer = class {
14861
14862
  }
14862
14863
  this.persistConfig();
14863
14864
  });
14864
- this.socket.onCascadeRun(async (prompt, model, socketId) => {
14865
- const sessionId = randomUUID();
14865
+ this.socket.onCascadeRun(async (prompt, model, socketId, requestedSessionId) => {
14866
+ const sessionId = requestedSessionId ?? randomUUID();
14867
+ const runPrompt = requestedSessionId ? this.buildContinuationPrompt(sessionId, prompt) : prompt;
14868
+ const title = this.persistRunStart(sessionId, prompt);
14866
14869
  const cfg = model !== "auto" ? { ...this.config, models: { ...this.config.models, t1: model } } : this.config;
14867
14870
  const cascade = new Cascade(cfg, this.workspacePath, this.store);
14868
14871
  this.activeSessions.set(sessionId, cascade);
14869
14872
  cascade.on("stream:token", (e) => {
14870
14873
  this.socket.emitToSocket(socketId, "stream:token", { sessionId, tierId: e.tierId, text: e.text });
14871
- this.socket.broadcast("stream:token", { sessionId, tierId: e.tierId, text: e.text });
14872
14874
  });
14873
14875
  cascade.on("tier:status", (e) => {
14874
14876
  this.socket.emitToSocket(socketId, "tier:status", { sessionId, ...e });
14875
- this.socket.broadcast("tier:status", { sessionId, ...e });
14876
14877
  });
14877
14878
  cascade.on("permission:user-required", (e) => {
14878
14879
  this.socket.emitToSocket(socketId, "permission:user-required", { sessionId, ...e });
@@ -14881,7 +14882,8 @@ var DashboardServer = class {
14881
14882
  this.socket.emitPeerMessage(e);
14882
14883
  });
14883
14884
  try {
14884
- const result = await cascade.run({ prompt });
14885
+ const result = await cascade.run({ prompt: runPrompt });
14886
+ this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED");
14885
14887
  this.socket.emitToSocket(socketId, "session:complete", { sessionId, result });
14886
14888
  this.socket.broadcast("cost:update", {
14887
14889
  sessionId,
@@ -14890,6 +14892,7 @@ var DashboardServer = class {
14890
14892
  });
14891
14893
  this.throttledBroadcast("workspace");
14892
14894
  } catch (err) {
14895
+ this.persistRunEnd(sessionId, title, prompt, void 0, "FAILED");
14893
14896
  this.socket.emitToSocket(socketId, "session:error", {
14894
14897
  sessionId,
14895
14898
  error: err instanceof Error ? err.message : String(err)
@@ -15000,6 +15003,86 @@ var DashboardServer = class {
15000
15003
  }
15001
15004
  return this.globalStore;
15002
15005
  }
15006
+ // ── Desktop-run session persistence ─────────
15007
+ // Only the CLI REPL used to persist sessions; runs started from the desktop
15008
+ // (socket `cascade:run` and REST /api/run) wrote nothing to the store, so
15009
+ // they never appeared in the session list and couldn't be resumed. These
15010
+ // helpers give both run paths the same persistence the CLI has.
15011
+ /** Record run start: session row (if new), the user message, and an ACTIVE runtime row. Returns the session title. */
15012
+ persistRunStart(sessionId, prompt) {
15013
+ const now = (/* @__PURE__ */ new Date()).toISOString();
15014
+ let title = prompt.replace(/\s+/g, " ").trim().slice(0, 40);
15015
+ try {
15016
+ const existing = this.store.getSession(sessionId);
15017
+ if (existing) {
15018
+ title = existing.title;
15019
+ } else {
15020
+ this.store.createSession({
15021
+ id: sessionId,
15022
+ title,
15023
+ createdAt: now,
15024
+ updatedAt: now,
15025
+ identityId: this.config.defaultIdentityId ?? "default",
15026
+ workspacePath: this.workspacePath,
15027
+ messages: [],
15028
+ metadata: { totalTokens: 0, totalCostUsd: 0, modelsUsed: [], toolsUsed: [], taskCount: 0 }
15029
+ });
15030
+ }
15031
+ this.store.addMessage({ id: randomUUID(), sessionId, role: "user", content: prompt, timestamp: now });
15032
+ } catch (err) {
15033
+ console.warn("[dashboard] failed to persist run start:", err);
15034
+ }
15035
+ this.persistRuntimeRow(sessionId, title, "ACTIVE", prompt);
15036
+ return title;
15037
+ }
15038
+ /** Record run end: the assistant reply (when there is one) and the runtime row's final status. */
15039
+ persistRunEnd(sessionId, title, latestPrompt, reply, status) {
15040
+ try {
15041
+ if (reply && reply.trim()) {
15042
+ this.store.addMessage({ id: randomUUID(), sessionId, role: "assistant", content: reply, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
15043
+ }
15044
+ } catch (err) {
15045
+ console.warn("[dashboard] failed to persist run end:", err);
15046
+ }
15047
+ this.persistRuntimeRow(sessionId, title, status, latestPrompt);
15048
+ }
15049
+ persistRuntimeRow(sessionId, title, status, latestPrompt) {
15050
+ const now = (/* @__PURE__ */ new Date()).toISOString();
15051
+ const row = { sessionId, title, workspacePath: this.workspacePath, status, startedAt: now, updatedAt: now, latestPrompt, isGlobal: false };
15052
+ try {
15053
+ this.store.upsertRuntimeSession(row);
15054
+ } catch (err) {
15055
+ console.warn("[dashboard] runtime upsert failed:", err);
15056
+ }
15057
+ try {
15058
+ this.getGlobalStore().upsertRuntimeSession({ ...row, isGlobal: true });
15059
+ } catch {
15060
+ }
15061
+ this.throttledBroadcast("workspace");
15062
+ }
15063
+ /**
15064
+ * Continuing an existing session: the Cascade orchestrator is per-run and
15065
+ * stateless across runs, so prepend a compact transcript of the stored
15066
+ * conversation to the new prompt. Called BEFORE persistRunStart so the new
15067
+ * prompt isn't duplicated into its own context.
15068
+ */
15069
+ buildContinuationPrompt(sessionId, prompt) {
15070
+ try {
15071
+ const history = this.store.getSessionMessages(sessionId);
15072
+ if (history.length === 0) return prompt;
15073
+ const lines = history.slice(-10).map((m) => {
15074
+ const text = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
15075
+ return `${m.role === "user" ? "User" : "Assistant"}: ${text.slice(0, 2e3)}`;
15076
+ });
15077
+ return `Conversation so far (for context):
15078
+ ${lines.join("\n")}
15079
+
15080
+ User's new message:
15081
+ ${prompt}`;
15082
+ } catch {
15083
+ return prompt;
15084
+ }
15085
+ }
15003
15086
  throttledBroadcast(scope) {
15004
15087
  if (this.broadcastTimer) return;
15005
15088
  this.broadcastTimer = setTimeout(() => {
@@ -15361,17 +15444,19 @@ var DashboardServer = class {
15361
15444
  res.status(400).json({ error: "prompt is required" });
15362
15445
  return;
15363
15446
  }
15364
- const sessionId = randomUUID();
15447
+ const requestedSessionId = typeof body.sessionId === "string" && body.sessionId.trim() ? body.sessionId.trim() : void 0;
15448
+ const sessionId = requestedSessionId ?? randomUUID();
15365
15449
  res.json({ sessionId, status: "ACTIVE" });
15450
+ const prompt = body.prompt;
15451
+ const runPrompt = requestedSessionId ? this.buildContinuationPrompt(sessionId, prompt) : prompt;
15452
+ const title = this.persistRunStart(sessionId, prompt);
15366
15453
  void (async () => {
15367
15454
  const cascade = new Cascade(this.config, this.workspacePath, this.store);
15368
15455
  this.activeSessions.set(sessionId, cascade);
15369
15456
  cascade.on("stream:token", (e) => {
15370
- this.socket.broadcast("stream:token", { sessionId, tierId: e.tierId, text: e.text });
15371
15457
  this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, tierId: e.tierId, text: e.text });
15372
15458
  });
15373
15459
  cascade.on("tier:status", (e) => {
15374
- this.socket.broadcast("tier:status", { sessionId, ...e });
15375
15460
  this.socket.broadcastToRoom(`session:${sessionId}`, "tier:status", { sessionId, ...e });
15376
15461
  });
15377
15462
  cascade.on("permission:user-required", (e) => {
@@ -15381,7 +15466,8 @@ var DashboardServer = class {
15381
15466
  this.socket.emitPeerMessage(e);
15382
15467
  });
15383
15468
  try {
15384
- const result = await cascade.run({ prompt: body.prompt, identityId: body.identityId });
15469
+ const result = await cascade.run({ prompt: runPrompt, identityId: body.identityId });
15470
+ this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED");
15385
15471
  this.socket.broadcast("cost:update", {
15386
15472
  sessionId,
15387
15473
  totalTokens: result.usage.totalTokens,
@@ -15390,6 +15476,7 @@ var DashboardServer = class {
15390
15476
  this.socket.broadcastToRoom(`session:${sessionId}`, "session:complete", { sessionId, result });
15391
15477
  this.throttledBroadcast("workspace");
15392
15478
  } catch (err) {
15479
+ this.persistRunEnd(sessionId, title, prompt, void 0, "FAILED");
15393
15480
  this.socket.broadcastToRoom(`session:${sessionId}`, "session:error", {
15394
15481
  sessionId,
15395
15482
  error: err instanceof Error ? err.message : String(err)