cascade-ai 0.2.11 → 0.3.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.js CHANGED
@@ -83,7 +83,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
83
83
  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;
84
84
  var init_constants = __esm({
85
85
  "src/constants.ts"() {
86
- CASCADE_VERSION = "0.2.11";
86
+ CASCADE_VERSION = "0.3.0";
87
87
  CASCADE_CONFIG_FILE = ".cascade/config.json";
88
88
  CASCADE_DB_FILE = ".cascade/memory.db";
89
89
  CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
@@ -522,17 +522,38 @@ var init_anthropic = __esm({
522
522
  messages,
523
523
  tools: tools?.length ? tools : void 0
524
524
  });
525
+ let isThinking = false;
525
526
  for await (const event of stream) {
526
- if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
527
- const text = event.delta.text;
528
- fullContent += text;
529
- onChunk({ text, finishReason: null });
527
+ if (event.type === "content_block_delta") {
528
+ if (event.delta.type === "thinking_delta") {
529
+ if (!isThinking) {
530
+ isThinking = true;
531
+ fullContent += "<think>\n";
532
+ onChunk({ text: "<think>\n", finishReason: null });
533
+ }
534
+ const text = event.delta.thinking;
535
+ fullContent += text;
536
+ onChunk({ text, finishReason: null });
537
+ } else if (event.delta.type === "text_delta") {
538
+ if (isThinking) {
539
+ isThinking = false;
540
+ fullContent += "\n</think>\n\n";
541
+ onChunk({ text: "\n</think>\n\n", finishReason: null });
542
+ }
543
+ const text = event.delta.text;
544
+ fullContent += text;
545
+ onChunk({ text, finishReason: null });
546
+ }
530
547
  } else if (event.type === "message_delta" && event.usage) {
531
548
  outputTokens = event.usage.output_tokens;
532
549
  } else if (event.type === "message_start" && event.message.usage) {
533
550
  inputTokens = event.message.usage.input_tokens;
534
551
  }
535
552
  }
553
+ if (isThinking) {
554
+ fullContent += "\n</think>\n\n";
555
+ onChunk({ text: "\n</think>\n\n", finishReason: null });
556
+ }
536
557
  const finalMessage = await stream.finalMessage();
537
558
  const toolCalls = finalMessage.content.filter((b) => b.type === "tool_use").map((b) => ({
538
559
  id: b.id,
@@ -716,9 +737,25 @@ var init_openai = __esm({
716
737
  }
717
738
  }
718
739
  const toolCallsMap = {};
740
+ let isThinking = false;
719
741
  for await (const chunk of stream) {
720
742
  const delta = chunk.choices[0]?.delta;
743
+ const reasoningContent = delta?.reasoning_content;
744
+ if (reasoningContent) {
745
+ if (!isThinking) {
746
+ isThinking = true;
747
+ fullContent += "<think>\n";
748
+ onChunk({ text: "<think>\n", finishReason: null });
749
+ }
750
+ fullContent += reasoningContent;
751
+ onChunk({ text: reasoningContent, finishReason: null });
752
+ }
721
753
  if (delta?.content) {
754
+ if (isThinking) {
755
+ isThinking = false;
756
+ fullContent += "\n</think>\n\n";
757
+ onChunk({ text: "\n</think>\n\n", finishReason: null });
758
+ }
722
759
  fullContent += delta.content;
723
760
  onChunk({ text: delta.content, finishReason: null });
724
761
  }
@@ -741,6 +778,10 @@ var init_openai = __esm({
741
778
  outputTokens = chunk.usage.completion_tokens;
742
779
  }
743
780
  }
781
+ if (isThinking) {
782
+ fullContent += "\n</think>\n\n";
783
+ onChunk({ text: "\n</think>\n\n", finishReason: null });
784
+ }
744
785
  const toolCalls = Object.values(toolCallsMap).map((tc) => {
745
786
  let input = {};
746
787
  try {
@@ -1834,8 +1875,8 @@ Original error: ${err.message}`
1834
1875
  upsertRuntimeNode(node) {
1835
1876
  this.enqueueWrite(() => {
1836
1877
  this.db.prepare(`
1837
- INSERT INTO runtime_nodes (tier_id, session_id, parent_id, role, label, status, current_action, progress_pct, updated_at, workspace_path, is_global)
1838
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1878
+ INSERT INTO runtime_nodes (tier_id, session_id, parent_id, role, label, status, current_action, progress_pct, updated_at, workspace_path, is_global, output)
1879
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1839
1880
  ON CONFLICT(tier_id) DO UPDATE SET
1840
1881
  session_id = excluded.session_id,
1841
1882
  parent_id = excluded.parent_id,
@@ -1846,7 +1887,8 @@ Original error: ${err.message}`
1846
1887
  progress_pct = excluded.progress_pct,
1847
1888
  updated_at = excluded.updated_at,
1848
1889
  workspace_path = excluded.workspace_path,
1849
- is_global = excluded.is_global
1890
+ is_global = excluded.is_global,
1891
+ output = excluded.output
1850
1892
  `).run(
1851
1893
  node.tierId,
1852
1894
  node.sessionId,
@@ -1858,7 +1900,8 @@ Original error: ${err.message}`
1858
1900
  node.progressPct ?? null,
1859
1901
  node.updatedAt,
1860
1902
  node.workspacePath ?? null,
1861
- node.isGlobal ? 1 : 0
1903
+ node.isGlobal ? 1 : 0,
1904
+ node.output ?? null
1862
1905
  );
1863
1906
  });
1864
1907
  }
@@ -1879,14 +1922,15 @@ Original error: ${err.message}`
1879
1922
  progressPct: row.progress_pct ?? void 0,
1880
1923
  updatedAt: row.updated_at,
1881
1924
  workspacePath: row.workspace_path ?? void 0,
1882
- isGlobal: row.is_global === 1
1925
+ isGlobal: row.is_global === 1,
1926
+ output: row.output ?? void 0
1883
1927
  }));
1884
1928
  }
1885
1929
  addRuntimeNodeLog(log) {
1886
1930
  this.enqueueWrite(() => {
1887
1931
  this.db.prepare(`
1888
- INSERT INTO runtime_node_logs (id, session_id, tier_id, role, label, status, current_action, progress_pct, timestamp, workspace_path, is_global)
1889
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1932
+ INSERT INTO runtime_node_logs (id, session_id, tier_id, role, label, status, current_action, progress_pct, timestamp, workspace_path, is_global, output)
1933
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1890
1934
  `).run(
1891
1935
  log.id,
1892
1936
  log.sessionId,
@@ -1898,7 +1942,8 @@ Original error: ${err.message}`
1898
1942
  log.progressPct ?? null,
1899
1943
  log.timestamp,
1900
1944
  log.workspacePath ?? null,
1901
- log.isGlobal ? 1 : 0
1945
+ log.isGlobal ? 1 : 0,
1946
+ log.output ?? null
1902
1947
  );
1903
1948
  this.db.prepare(`
1904
1949
  DELETE FROM runtime_node_logs
@@ -1941,7 +1986,8 @@ Original error: ${err.message}`
1941
1986
  progressPct: row.progress_pct ?? void 0,
1942
1987
  timestamp: row.timestamp,
1943
1988
  workspacePath: row.workspace_path ?? void 0,
1944
- isGlobal: row.is_global === 1
1989
+ isGlobal: row.is_global === 1,
1990
+ output: row.output ?? void 0
1945
1991
  }));
1946
1992
  }
1947
1993
  // ── Messages ──────────────────────────────────
@@ -2268,7 +2314,8 @@ Original error: ${err.message}`
2268
2314
  progress_pct INTEGER,
2269
2315
  updated_at TEXT NOT NULL,
2270
2316
  workspace_path TEXT,
2271
- is_global INTEGER NOT NULL DEFAULT 0
2317
+ is_global INTEGER NOT NULL DEFAULT 0,
2318
+ output TEXT
2272
2319
  );
2273
2320
 
2274
2321
  CREATE INDEX IF NOT EXISTS idx_runtime_nodes_session ON runtime_nodes(session_id);
@@ -2285,7 +2332,8 @@ Original error: ${err.message}`
2285
2332
  progress_pct INTEGER,
2286
2333
  timestamp TEXT NOT NULL,
2287
2334
  workspace_path TEXT,
2288
- is_global INTEGER NOT NULL DEFAULT 0
2335
+ is_global INTEGER NOT NULL DEFAULT 0,
2336
+ output TEXT
2289
2337
  );
2290
2338
 
2291
2339
  CREATE TABLE IF NOT EXISTS model_cache (
@@ -2314,6 +2362,14 @@ Original error: ${err.message}`
2314
2362
 
2315
2363
  CREATE INDEX IF NOT EXISTS idx_file_snapshots_session ON file_snapshots(session_id);
2316
2364
  `);
2365
+ try {
2366
+ this.db.exec("ALTER TABLE runtime_nodes ADD COLUMN output TEXT");
2367
+ } catch {
2368
+ }
2369
+ try {
2370
+ this.db.exec("ALTER TABLE runtime_node_logs ADD COLUMN output TEXT");
2371
+ } catch {
2372
+ }
2317
2373
  }
2318
2374
  // ── Deserializers ─────────────────────────────
2319
2375
  deserializeSession(row, messages) {
@@ -2576,6 +2632,7 @@ var ConfigManager = class {
2576
2632
  }
2577
2633
  }
2578
2634
  async injectEnvKeys() {
2635
+ const isFirstRun = this.config.providers.length === 0;
2579
2636
  const envProviders = [
2580
2637
  { env: "ANTHROPIC_API_KEY", type: "anthropic" },
2581
2638
  { env: "OPENAI_API_KEY", type: "openai" },
@@ -2586,10 +2643,13 @@ var ConfigManager = class {
2586
2643
  const key = process.env[env];
2587
2644
  if (!key) continue;
2588
2645
  const existing = this.config.providers.find((p) => p.type === type);
2589
- if (!existing) this.config.providers.push({ type, apiKey: key });
2590
- else if (!existing.apiKey) existing.apiKey = key;
2646
+ if (!existing && isFirstRun) {
2647
+ this.config.providers.push({ type, apiKey: key });
2648
+ } else if (existing && !existing.apiKey) {
2649
+ existing.apiKey = key;
2650
+ }
2591
2651
  }
2592
- if (!this.config.providers.find((p) => p.type === "ollama")) {
2652
+ if (isFirstRun && !this.config.providers.find((p) => p.type === "ollama")) {
2593
2653
  this.config.providers.push({ type: "ollama" });
2594
2654
  }
2595
2655
  }
@@ -3538,7 +3598,7 @@ var BaseTier = class extends EventEmitter {
3538
3598
  getStatus() {
3539
3599
  return this.status;
3540
3600
  }
3541
- setStatus(status) {
3601
+ setStatus(status, output) {
3542
3602
  this.status = status;
3543
3603
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3544
3604
  const event = {
@@ -3547,7 +3607,8 @@ var BaseTier = class extends EventEmitter {
3547
3607
  parentId: this.parentId,
3548
3608
  label: this.label,
3549
3609
  status,
3550
- timestamp
3610
+ timestamp,
3611
+ output
3551
3612
  };
3552
3613
  this.emit("status", event);
3553
3614
  this.emit("tier:status", event);
@@ -3573,7 +3634,8 @@ var BaseTier = class extends EventEmitter {
3573
3634
  status: this.status,
3574
3635
  currentAction: update.currentAction,
3575
3636
  progressPct: update.progressPct,
3576
- timestamp
3637
+ timestamp,
3638
+ output: update.output
3577
3639
  });
3578
3640
  }
3579
3641
  buildMessage(type, to, payload) {
@@ -3954,16 +4016,17 @@ Now execute your subtask using this context where relevant.`
3954
4016
  return this.buildResult("ESCALATED", output, { checksRun, passed, failed }, issues, correctionAttempts);
3955
4017
  }
3956
4018
  }
3957
- this.setStatus("COMPLETED");
3958
- this.sendStatusUpdate({ progressPct: 100, currentAction: "Subtask complete", status: "IN_PROGRESS" });
4019
+ this.setStatus("COMPLETED", output);
4020
+ this.sendStatusUpdate({ progressPct: 100, currentAction: "Subtask complete", status: "IN_PROGRESS", output });
3959
4021
  this.peerBus?.publish(this.id, assignment.subtaskId, output, "COMPLETED");
3960
4022
  return this.buildResult("COMPLETED", output, { checksRun, passed, failed }, issues, correctionAttempts);
3961
4023
  } catch (err) {
3962
4024
  const errMsg = err instanceof Error ? err.message : String(err);
3963
4025
  issues.push(`Execution error: ${errMsg}`);
3964
- this.setStatus("FAILED");
3965
- this.peerBus?.publish(this.id, assignment.subtaskId, errMsg, "FAILED");
3966
- return this.buildResult("ESCALATED", output || errMsg, { checksRun, passed, failed }, issues, correctionAttempts);
4026
+ const finalOutput = output || errMsg;
4027
+ this.setStatus("FAILED", finalOutput);
4028
+ this.peerBus?.publish(this.id, assignment.subtaskId, finalOutput, "FAILED");
4029
+ return this.buildResult("ESCALATED", finalOutput, { checksRun, passed, failed }, issues, correctionAttempts);
3967
4030
  }
3968
4031
  }
3969
4032
  sendToPeer(toId, content) {
@@ -4646,8 +4709,9 @@ var T2Manager = class extends BaseTier {
4646
4709
  const summary = await this.aggregateResults(assignment, t3Results);
4647
4710
  const issues = t3Results.filter((r) => r.status !== "COMPLETED").flatMap((r) => r.issues);
4648
4711
  const overallStatus = this.determineStatus(t3Results);
4649
- this.setStatus(overallStatus === "COMPLETED" ? "COMPLETED" : "FAILED");
4650
- this.sendStatusUpdate({ progressPct: 100, currentAction: "Section complete", status: "IN_PROGRESS" });
4712
+ const isOk = overallStatus === "COMPLETED" || overallStatus === "PARTIAL";
4713
+ this.setStatus(isOk ? "COMPLETED" : "FAILED", summary);
4714
+ this.sendStatusUpdate({ progressPct: 100, currentAction: "Section complete", status: "IN_PROGRESS", output: summary });
4651
4715
  const result = {
4652
4716
  sectionId: assignment.sectionId,
4653
4717
  sectionTitle: assignment.sectionTitle,
@@ -4660,7 +4724,7 @@ var T2Manager = class extends BaseTier {
4660
4724
  return result;
4661
4725
  } catch (err) {
4662
4726
  const errMsg = err instanceof Error ? err.message : String(err);
4663
- this.setStatus("FAILED");
4727
+ this.setStatus("FAILED", errMsg);
4664
4728
  const failedResult = {
4665
4729
  sectionId: assignment.sectionId,
4666
4730
  sectionTitle: assignment.sectionTitle,
@@ -5225,8 +5289,8 @@ Create a CORRECTION PLAN that contains only the new sections needed to fix the i
5225
5289
  status: "IN_PROGRESS"
5226
5290
  });
5227
5291
  const output = await this.compileFinalOutput(userPrompt, plan, allT2Results);
5228
- this.setStatus("COMPLETED");
5229
- this.sendStatusUpdate({ progressPct: 100, currentAction: "Task complete", status: "IN_PROGRESS" });
5292
+ this.setStatus("COMPLETED", output);
5293
+ this.sendStatusUpdate({ progressPct: 100, currentAction: "Task complete", status: "IN_PROGRESS", output });
5230
5294
  return { output, t2Results: allT2Results, taskId: this.taskId, complexity: plan.complexity };
5231
5295
  }
5232
5296
  getEscalations() {
@@ -8055,11 +8119,6 @@ var SlashCommandRegistry = class {
8055
8119
  description: "Show active models per tier",
8056
8120
  handler: (_args, ctx) => ({ output: ctx.onModelInfo(), handled: true })
8057
8121
  });
8058
- this.register({
8059
- command: "/models",
8060
- description: "Browse available models by provider",
8061
- handler: (_args, ctx) => ({ output: ctx.onModelsInfo(), handled: true })
8062
- });
8063
8122
  this.register({
8064
8123
  command: "/providers",
8065
8124
  description: "Show configured providers",
@@ -8188,35 +8247,69 @@ function AgentTree({ root, theme }) {
8188
8247
  }
8189
8248
  function T2Row({ node, theme, isLast }) {
8190
8249
  const connector = isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
8191
- const t3Active = countByRoleAndStatus(node, "T3", "ACTIVE");
8192
- const t3Total = countByRole(node, "T3");
8193
- const workerSuffix = t3Total > 0 ? ` T3\xD7${t3Total}` : "";
8250
+ const t3Nodes = (node.children ?? []).filter((c) => c.role === "T3");
8251
+ const t3ActiveCount = t3Nodes.filter((c) => c.status === "ACTIVE").length;
8194
8252
  const label = stripRolePrefix(node.label, node.role);
8195
8253
  const action = node.currentAction ? ` ${node.currentAction.slice(0, 38)}` : "";
8254
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
8255
+ /* @__PURE__ */ jsxs(Box, { children: [
8256
+ /* @__PURE__ */ jsxs(Text, { color: theme.colors.muted, children: [
8257
+ " ",
8258
+ connector
8259
+ ] }),
8260
+ /* @__PURE__ */ jsx(Text, { color: theme.colors.t2Color, bold: true, children: "[T2]" }),
8261
+ /* @__PURE__ */ jsxs(Text, { color: theme.colors.foreground, children: [
8262
+ " ",
8263
+ label
8264
+ ] }),
8265
+ node.status === "ACTIVE" && /* @__PURE__ */ jsxs(Fragment, { children: [
8266
+ action ? /* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: action }) : null,
8267
+ /* @__PURE__ */ jsx(Text, { children: " " }),
8268
+ /* @__PURE__ */ jsx(Spinner, { type: "dots" }),
8269
+ t3ActiveCount > 0 ? /* @__PURE__ */ jsxs(Text, { color: theme.colors.muted, children: [
8270
+ " (",
8271
+ t3ActiveCount,
8272
+ " running)"
8273
+ ] }) : null
8274
+ ] }),
8275
+ node.status === "COMPLETED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.success, children: " \u2714" }),
8276
+ node.status === "FAILED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.error, children: " \u2718" }),
8277
+ node.status === "ESCALATED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.warning, children: " \u25B2" })
8278
+ ] }),
8279
+ t3Nodes.map((t3, idx) => /* @__PURE__ */ jsx(
8280
+ T3Row,
8281
+ {
8282
+ node: t3,
8283
+ theme,
8284
+ isLast: idx === t3Nodes.length - 1,
8285
+ parentIsLast: isLast
8286
+ },
8287
+ t3.id
8288
+ ))
8289
+ ] });
8290
+ }
8291
+ function T3Row({ node, theme, isLast, parentIsLast }) {
8292
+ const indent = parentIsLast ? " " : " \u2502 ";
8293
+ const connector = isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
8294
+ const label = stripRolePrefix(node.label, node.role);
8295
+ const action = node.currentAction ? ` ${node.currentAction.slice(0, 42)}` : "";
8196
8296
  return /* @__PURE__ */ jsxs(Box, { children: [
8197
8297
  /* @__PURE__ */ jsxs(Text, { color: theme.colors.muted, children: [
8198
- " ",
8298
+ indent,
8199
8299
  connector
8200
8300
  ] }),
8201
- /* @__PURE__ */ jsx(Text, { color: theme.colors.t2Color, bold: true, children: "[T2]" }),
8202
- /* @__PURE__ */ jsxs(Text, { color: theme.colors.foreground, children: [
8301
+ /* @__PURE__ */ jsx(Text, { color: theme.colors.t3Color, children: "[T3]" }),
8302
+ /* @__PURE__ */ jsxs(Text, { color: theme.colors.muted, children: [
8203
8303
  " ",
8204
8304
  label
8205
8305
  ] }),
8206
- workerSuffix ? /* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: workerSuffix }) : null,
8207
8306
  node.status === "ACTIVE" && /* @__PURE__ */ jsxs(Fragment, { children: [
8208
8307
  action ? /* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: action }) : null,
8209
8308
  /* @__PURE__ */ jsx(Text, { children: " " }),
8210
- /* @__PURE__ */ jsx(Spinner, { type: "dots" }),
8211
- t3Active > 0 ? /* @__PURE__ */ jsxs(Text, { color: theme.colors.muted, children: [
8212
- " (",
8213
- t3Active,
8214
- " running)"
8215
- ] }) : null
8309
+ /* @__PURE__ */ jsx(Spinner, { type: "dots" })
8216
8310
  ] }),
8217
8311
  node.status === "COMPLETED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.success, children: " \u2714" }),
8218
- node.status === "FAILED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.error, children: " \u2718" }),
8219
- node.status === "ESCALATED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.warning, children: " \u25B2" })
8312
+ node.status === "FAILED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.error, children: " \u2718" })
8220
8313
  ] });
8221
8314
  }
8222
8315
  function hasActiveOrFailed(node) {
@@ -8828,7 +8921,7 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName })
8828
8921
  treeNodesRef.current.set(event.tierId, node);
8829
8922
  const store = storeRef.current;
8830
8923
  if (store) {
8831
- const runtimeNode = { tierId: node.id, sessionId: sessionIdRef.current, parentId: node.parentId, role: node.role, label: node.label, status: node.status, currentAction: node.currentAction, progressPct: node.progressPct, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), workspacePath, isGlobal: false };
8924
+ const runtimeNode = { tierId: node.id, sessionId: sessionIdRef.current, parentId: node.parentId, role: node.role, label: node.label, status: node.status, currentAction: node.currentAction, progressPct: node.progressPct, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), workspacePath, isGlobal: false, output: event.output };
8832
8925
  store.upsertRuntimeNode(runtimeNode);
8833
8926
  globalStoreRef.current?.upsertRuntimeNode({ ...runtimeNode, isGlobal: true });
8834
8927
  }
@@ -8839,7 +8932,6 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName })
8839
8932
  useEffect(() => {
8840
8933
  const originalWarn = console.warn;
8841
8934
  const originalLog = console.log;
8842
- process.stdout.write("\x1Bc");
8843
8935
  console.warn = (...args) => {
8844
8936
  const msg = args.join(" ");
8845
8937
  if (msg.includes("non-text parts") || msg.includes("functionCall")) return;
@@ -9356,8 +9448,8 @@ Use /identity <name|id> to switch.`;
9356
9448
  const statusHeight = agentTreeHeight + timelineHeight;
9357
9449
  const costHeight = state.showCost ? 6 : 0;
9358
9450
  const approvalHeight = state.approvalRequest ? 12 : 0;
9359
- const slashVisibleCount = Math.min(SLASH_PAGE_SIZE, slashEntries.length);
9360
- const slashHeight = slashVisibleCount > 0 ? slashVisibleCount + 2 : 0;
9451
+ Math.min(SLASH_PAGE_SIZE, slashEntries.length);
9452
+ const slashHeight = isTypingCommand ? SLASH_PAGE_SIZE + 2 : 0;
9361
9453
  const chromeHeight = statusHeight + costHeight + approvalHeight + slashHeight + 7;
9362
9454
  const availableHeight = Math.max(4, height - chromeHeight);
9363
9455
  const allLines = formatToLines(
@@ -9769,8 +9861,21 @@ function wizardReducer(state, action) {
9769
9861
  addingAnotherCompat: false
9770
9862
  };
9771
9863
  }
9864
+ case "ADD_OLLAMA": {
9865
+ const newEntry = {
9866
+ id: randomUUID(),
9867
+ type: "ollama",
9868
+ label: `Ollama endpoint ${state.entries.filter((e) => e.type === "ollama").length + 1}`
9869
+ };
9870
+ return {
9871
+ ...state,
9872
+ entries: [...state.entries, newEntry],
9873
+ currentEntryIdx: state.entries.length,
9874
+ addingAnotherOllama: false
9875
+ };
9876
+ }
9772
9877
  case "SKIP_MORE":
9773
- return { ...state, addingAnotherAzure: false, addingAnotherCompat: false, step: "FETCH_MODELS", currentEntryIdx: 0 };
9878
+ return { ...state, addingAnotherAzure: false, addingAnotherCompat: false, addingAnotherOllama: false, step: "FETCH_MODELS", currentEntryIdx: 0 };
9774
9879
  case "GO_FETCH":
9775
9880
  return { ...state, step: "FETCH_MODELS", fetchLog: [], fetchedModels: [] };
9776
9881
  case "SET_FETCH_LOG":
@@ -9803,6 +9908,7 @@ function SetupWizard({ workspacePath, onComplete }) {
9803
9908
  currentEntryIdx: 0,
9804
9909
  addingAnotherAzure: false,
9805
9910
  addingAnotherCompat: false,
9911
+ addingAnotherOllama: false,
9806
9912
  fetchedModels: [],
9807
9913
  fetchLog: [],
9808
9914
  tierT1: "auto",
@@ -9902,13 +10008,16 @@ function SetupWizard({ workspacePath, onComplete }) {
9902
10008
  }, [state.step, state.entries, state.tierT1, state.tierT2, state.tierT3, workspacePath, onComplete, exit]);
9903
10009
  useInput((_input, key) => {
9904
10010
  if (state.step === "PROVIDER_SELECT") {
9905
- if (key.upArrow) setProviderCursor((p) => Math.max(0, p - 1));
9906
- if (key.downArrow) setProviderCursor((p) => Math.min(providerOrder.length - 1, p + 1));
10011
+ if (key.upArrow) setProviderCursor((p) => p <= 0 ? providerOrder.length - 1 : p - 1);
10012
+ if (key.downArrow) setProviderCursor((p) => p >= providerOrder.length - 1 ? 0 : p + 1);
9907
10013
  if (_input === " ") dispatch({ type: "TOGGLE_PROVIDER", provider: providerOrder[providerCursor] });
9908
10014
  if (_input === "a") dispatch({ type: "TOGGLE_ALL" });
9909
10015
  if (_input === "i") dispatch({ type: "INVERT_SELECTION" });
9910
10016
  if (key.return) {
9911
- if (state.selectedTypes.size === 0) return;
10017
+ if (state.selectedTypes.size === 0) {
10018
+ dispatch({ type: "SET_ERROR", error: "Please select at least one provider using <space> before pressing <enter>." });
10019
+ return;
10020
+ }
9912
10021
  dispatch({ type: "CONFIRM_PROVIDERS" });
9913
10022
  const firstType = [...state.selectedTypes][0];
9914
10023
  setFieldStage(firstType === "azure" ? "deploymentName" : firstType === "openai-compatible" ? "label" : firstType === "ollama" ? "baseUrl" : "apiKey");
@@ -9965,11 +10074,7 @@ function SetupWizard({ workspacePath, onComplete }) {
9965
10074
  } else if (currentEntry.type === "ollama") {
9966
10075
  dispatch({ type: "SET_ENTRY_FIELD", field: "baseUrl", value: val || "http://localhost:11434" });
9967
10076
  setFieldBuffer("");
9968
- const nextEntry = state.entries[state.currentEntryIdx + 1];
9969
- if (nextEntry) {
9970
- setFieldStage(nextEntry.type === "azure" ? "deploymentName" : nextEntry.type === "openai-compatible" ? "label" : nextEntry.type === "ollama" ? "baseUrl" : "apiKey");
9971
- }
9972
- dispatch({ type: "NEXT_ENTRY" });
10077
+ setFieldStage("askMore");
9973
10078
  } else {
9974
10079
  dispatch({ type: "SET_ENTRY_FIELD", field: "apiKey", value: val });
9975
10080
  setFieldBuffer("");
@@ -10010,7 +10115,7 @@ function SetupWizard({ workspacePath, onComplete }) {
10010
10115
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
10011
10116
  /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
10012
10117
  /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "? " }),
10013
- /* @__PURE__ */ jsx(Text, { bold: true, children: isAzure ? "Add another Azure deployment? (y/n)" : "Add another custom endpoint? (y/n)" })
10118
+ /* @__PURE__ */ jsx(Text, { bold: true, children: isAzure ? "Add another Azure deployment? (y/n)" : isOllama ? "Add another Ollama endpoint? (y/n)" : "Add another custom endpoint? (y/n)" })
10014
10119
  ] }),
10015
10120
  /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
10016
10121
  SelectInput,
@@ -10024,8 +10129,9 @@ function SetupWizard({ workspacePath, onComplete }) {
10024
10129
  onSelect: (item) => {
10025
10130
  if (item.value === "yes") {
10026
10131
  if (isAzure) dispatch({ type: "ADD_AZURE" });
10132
+ else if (isOllama) dispatch({ type: "ADD_OLLAMA" });
10027
10133
  else dispatch({ type: "ADD_COMPAT" });
10028
- setFieldStage(isAzure ? "deploymentName" : "label");
10134
+ setFieldStage(isAzure ? "deploymentName" : isOllama ? "baseUrl" : "label");
10029
10135
  setFieldBuffer("");
10030
10136
  } else {
10031
10137
  const nextEntry = state.entries[state.currentEntryIdx + 1];
@@ -10041,7 +10147,7 @@ function SetupWizard({ workspacePath, onComplete }) {
10041
10147
  ] });
10042
10148
  }
10043
10149
  const prompt = isAzure && fieldStage === "deploymentName" ? `Azure deployment name (${currentEntry.label})` : isAzure && fieldStage === "baseUrl" ? `Azure endpoint URL` : isCompat && fieldStage === "label" ? `Name for this endpoint (e.g. Groq)` : isCompat && fieldStage === "baseUrl" ? `Base URL (e.g. https://api.groq.com/openai/v1)` : isOllama ? `Ollama URL (Enter for http://localhost:11434)` : `${currentEntry.label} API Key`;
10044
- const isMasked = fieldStage === "apiKey";
10150
+ const isMasked = fieldStage === "apiKey" && !isOllama;
10045
10151
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
10046
10152
  /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
10047
10153
  /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "? " }),
@@ -10935,8 +11041,8 @@ var DashboardServer = class {
10935
11041
  void (async () => {
10936
11042
  const cascade = new Cascade(this.config, this.workspacePath, this.store);
10937
11043
  cascade.on("stream:token", (e) => {
10938
- this.socket.broadcast("stream:token", { sessionId, token: e.text });
10939
- this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, token: e.text });
11044
+ this.socket.broadcast("stream:token", { sessionId, tierId: e.tierId, text: e.text });
11045
+ this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, tierId: e.tierId, text: e.text });
10940
11046
  });
10941
11047
  cascade.on("tier:status", (e) => {
10942
11048
  this.socket.broadcast("tier:status", { sessionId, ...e });
@@ -11372,6 +11478,9 @@ async function startRepl(options) {
11372
11478
  await cm.load();
11373
11479
  config = cm.getConfig();
11374
11480
  }
11481
+ if (process.stdout.isTTY) {
11482
+ process.stdout.write("\x1B[2J\x1B[H");
11483
+ }
11375
11484
  const { waitUntilExit } = render(
11376
11485
  React5.createElement(Repl, {
11377
11486
  config,