cascade-ai 0.12.22 → 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.22";
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,8 +14862,10 @@ 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);
@@ -14879,7 +14882,8 @@ var DashboardServer = class {
14879
14882
  this.socket.emitPeerMessage(e);
14880
14883
  });
14881
14884
  try {
14882
- const result = await cascade.run({ prompt });
14885
+ const result = await cascade.run({ prompt: runPrompt });
14886
+ this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED");
14883
14887
  this.socket.emitToSocket(socketId, "session:complete", { sessionId, result });
14884
14888
  this.socket.broadcast("cost:update", {
14885
14889
  sessionId,
@@ -14888,6 +14892,7 @@ var DashboardServer = class {
14888
14892
  });
14889
14893
  this.throttledBroadcast("workspace");
14890
14894
  } catch (err) {
14895
+ this.persistRunEnd(sessionId, title, prompt, void 0, "FAILED");
14891
14896
  this.socket.emitToSocket(socketId, "session:error", {
14892
14897
  sessionId,
14893
14898
  error: err instanceof Error ? err.message : String(err)
@@ -14998,6 +15003,86 @@ var DashboardServer = class {
14998
15003
  }
14999
15004
  return this.globalStore;
15000
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
+ }
15001
15086
  throttledBroadcast(scope) {
15002
15087
  if (this.broadcastTimer) return;
15003
15088
  this.broadcastTimer = setTimeout(() => {
@@ -15359,8 +15444,12 @@ var DashboardServer = class {
15359
15444
  res.status(400).json({ error: "prompt is required" });
15360
15445
  return;
15361
15446
  }
15362
- const sessionId = randomUUID();
15447
+ const requestedSessionId = typeof body.sessionId === "string" && body.sessionId.trim() ? body.sessionId.trim() : void 0;
15448
+ const sessionId = requestedSessionId ?? randomUUID();
15363
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);
15364
15453
  void (async () => {
15365
15454
  const cascade = new Cascade(this.config, this.workspacePath, this.store);
15366
15455
  this.activeSessions.set(sessionId, cascade);
@@ -15377,7 +15466,8 @@ var DashboardServer = class {
15377
15466
  this.socket.emitPeerMessage(e);
15378
15467
  });
15379
15468
  try {
15380
- 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");
15381
15471
  this.socket.broadcast("cost:update", {
15382
15472
  sessionId,
15383
15473
  totalTokens: result.usage.totalTokens,
@@ -15386,6 +15476,7 @@ var DashboardServer = class {
15386
15476
  this.socket.broadcastToRoom(`session:${sessionId}`, "session:complete", { sessionId, result });
15387
15477
  this.throttledBroadcast("workspace");
15388
15478
  } catch (err) {
15479
+ this.persistRunEnd(sessionId, title, prompt, void 0, "FAILED");
15389
15480
  this.socket.broadcastToRoom(`session:${sessionId}`, "session:error", {
15390
15481
  sessionId,
15391
15482
  error: err instanceof Error ? err.message : String(err)