cascade-ai 0.2.12 → 0.4.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
@@ -1,5 +1,5 @@
1
1
  import Anthropic from '@anthropic-ai/sdk';
2
- import OpenAI from 'openai';
2
+ import OpenAI, { AzureOpenAI } from 'openai';
3
3
  import { GoogleGenAI, HarmBlockThreshold, HarmCategory } from '@google/genai';
4
4
  import axios2 from 'axios';
5
5
  import { render, useApp, useStdout, useInput, Box, Text } from 'ink';
@@ -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.12";
86
+ CASCADE_VERSION = "0.4.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";
@@ -902,19 +902,21 @@ var init_azure = __esm({
902
902
  init_openai();
903
903
  AzureOpenAIProvider = class extends OpenAIProvider {
904
904
  constructor(config, model) {
905
- const baseUrl = config.baseUrl ?? AZURE_BASE_URL_TEMPLATE.replace("{resource}", "YOUR_RESOURCE");
905
+ const rawUrl = config.baseUrl ?? AZURE_BASE_URL_TEMPLATE.replace("{resource}", "YOUR_RESOURCE");
906
+ const endpoint = rawUrl.replace(/\/+$/, "");
906
907
  super(
907
908
  {
908
909
  ...config,
909
- baseUrl: `${baseUrl}/openai/deployments/${config.deploymentName ?? model.id}`
910
+ baseUrl: endpoint
911
+ // Kept for superclass compatibility if it reads it
910
912
  },
911
913
  model
912
914
  );
913
- this.client = new OpenAI({
915
+ this.client = new AzureOpenAI({
914
916
  apiKey: config.apiKey,
915
- baseURL: `${baseUrl}/openai/deployments/${config.deploymentName ?? model.id}`,
916
- defaultQuery: { "api-version": config.apiVersion ?? "2024-08-01-preview" },
917
- defaultHeaders: { "api-key": config.apiKey ?? "" }
917
+ endpoint,
918
+ deployment: config.deploymentName ?? model.id,
919
+ apiVersion: config.apiVersion ?? "2024-08-01-preview"
918
920
  });
919
921
  }
920
922
  async listModels() {
@@ -1875,8 +1877,8 @@ Original error: ${err.message}`
1875
1877
  upsertRuntimeNode(node) {
1876
1878
  this.enqueueWrite(() => {
1877
1879
  this.db.prepare(`
1878
- INSERT INTO runtime_nodes (tier_id, session_id, parent_id, role, label, status, current_action, progress_pct, updated_at, workspace_path, is_global)
1879
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1880
+ INSERT INTO runtime_nodes (tier_id, session_id, parent_id, role, label, status, current_action, progress_pct, updated_at, workspace_path, is_global, output)
1881
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1880
1882
  ON CONFLICT(tier_id) DO UPDATE SET
1881
1883
  session_id = excluded.session_id,
1882
1884
  parent_id = excluded.parent_id,
@@ -1887,7 +1889,8 @@ Original error: ${err.message}`
1887
1889
  progress_pct = excluded.progress_pct,
1888
1890
  updated_at = excluded.updated_at,
1889
1891
  workspace_path = excluded.workspace_path,
1890
- is_global = excluded.is_global
1892
+ is_global = excluded.is_global,
1893
+ output = excluded.output
1891
1894
  `).run(
1892
1895
  node.tierId,
1893
1896
  node.sessionId,
@@ -1899,7 +1902,8 @@ Original error: ${err.message}`
1899
1902
  node.progressPct ?? null,
1900
1903
  node.updatedAt,
1901
1904
  node.workspacePath ?? null,
1902
- node.isGlobal ? 1 : 0
1905
+ node.isGlobal ? 1 : 0,
1906
+ node.output ?? null
1903
1907
  );
1904
1908
  });
1905
1909
  }
@@ -1920,14 +1924,15 @@ Original error: ${err.message}`
1920
1924
  progressPct: row.progress_pct ?? void 0,
1921
1925
  updatedAt: row.updated_at,
1922
1926
  workspacePath: row.workspace_path ?? void 0,
1923
- isGlobal: row.is_global === 1
1927
+ isGlobal: row.is_global === 1,
1928
+ output: row.output ?? void 0
1924
1929
  }));
1925
1930
  }
1926
1931
  addRuntimeNodeLog(log) {
1927
1932
  this.enqueueWrite(() => {
1928
1933
  this.db.prepare(`
1929
- INSERT INTO runtime_node_logs (id, session_id, tier_id, role, label, status, current_action, progress_pct, timestamp, workspace_path, is_global)
1930
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1934
+ INSERT INTO runtime_node_logs (id, session_id, tier_id, role, label, status, current_action, progress_pct, timestamp, workspace_path, is_global, output)
1935
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1931
1936
  `).run(
1932
1937
  log.id,
1933
1938
  log.sessionId,
@@ -1939,7 +1944,8 @@ Original error: ${err.message}`
1939
1944
  log.progressPct ?? null,
1940
1945
  log.timestamp,
1941
1946
  log.workspacePath ?? null,
1942
- log.isGlobal ? 1 : 0
1947
+ log.isGlobal ? 1 : 0,
1948
+ log.output ?? null
1943
1949
  );
1944
1950
  this.db.prepare(`
1945
1951
  DELETE FROM runtime_node_logs
@@ -1982,7 +1988,8 @@ Original error: ${err.message}`
1982
1988
  progressPct: row.progress_pct ?? void 0,
1983
1989
  timestamp: row.timestamp,
1984
1990
  workspacePath: row.workspace_path ?? void 0,
1985
- isGlobal: row.is_global === 1
1991
+ isGlobal: row.is_global === 1,
1992
+ output: row.output ?? void 0
1986
1993
  }));
1987
1994
  }
1988
1995
  // ── Messages ──────────────────────────────────
@@ -2309,7 +2316,8 @@ Original error: ${err.message}`
2309
2316
  progress_pct INTEGER,
2310
2317
  updated_at TEXT NOT NULL,
2311
2318
  workspace_path TEXT,
2312
- is_global INTEGER NOT NULL DEFAULT 0
2319
+ is_global INTEGER NOT NULL DEFAULT 0,
2320
+ output TEXT
2313
2321
  );
2314
2322
 
2315
2323
  CREATE INDEX IF NOT EXISTS idx_runtime_nodes_session ON runtime_nodes(session_id);
@@ -2326,7 +2334,8 @@ Original error: ${err.message}`
2326
2334
  progress_pct INTEGER,
2327
2335
  timestamp TEXT NOT NULL,
2328
2336
  workspace_path TEXT,
2329
- is_global INTEGER NOT NULL DEFAULT 0
2337
+ is_global INTEGER NOT NULL DEFAULT 0,
2338
+ output TEXT
2330
2339
  );
2331
2340
 
2332
2341
  CREATE TABLE IF NOT EXISTS model_cache (
@@ -2355,6 +2364,14 @@ Original error: ${err.message}`
2355
2364
 
2356
2365
  CREATE INDEX IF NOT EXISTS idx_file_snapshots_session ON file_snapshots(session_id);
2357
2366
  `);
2367
+ try {
2368
+ this.db.exec("ALTER TABLE runtime_nodes ADD COLUMN output TEXT");
2369
+ } catch {
2370
+ }
2371
+ try {
2372
+ this.db.exec("ALTER TABLE runtime_node_logs ADD COLUMN output TEXT");
2373
+ } catch {
2374
+ }
2358
2375
  }
2359
2376
  // ── Deserializers ─────────────────────────────
2360
2377
  deserializeSession(row, messages) {
@@ -3233,7 +3250,7 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
3233
3250
  if (!model) {
3234
3251
  throw new Error(`Configured model "${override}" for ${tier} could not be loaded. Check provider availability and exact model name.`);
3235
3252
  }
3236
- if (model.id !== override) {
3253
+ if (model.id !== override && `${model.provider}:${model.id}` !== override) {
3237
3254
  throw new Error(`Configured model "${override}" for ${tier} resolved to "${model.id}". Use the exact provider model ID or prefix the provider (e.g. gemini:${override}).`);
3238
3255
  }
3239
3256
  this.tierModels.set(tier, model);
@@ -3583,7 +3600,7 @@ var BaseTier = class extends EventEmitter {
3583
3600
  getStatus() {
3584
3601
  return this.status;
3585
3602
  }
3586
- setStatus(status) {
3603
+ setStatus(status, output) {
3587
3604
  this.status = status;
3588
3605
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3589
3606
  const event = {
@@ -3592,7 +3609,8 @@ var BaseTier = class extends EventEmitter {
3592
3609
  parentId: this.parentId,
3593
3610
  label: this.label,
3594
3611
  status,
3595
- timestamp
3612
+ timestamp,
3613
+ output
3596
3614
  };
3597
3615
  this.emit("status", event);
3598
3616
  this.emit("tier:status", event);
@@ -3618,7 +3636,8 @@ var BaseTier = class extends EventEmitter {
3618
3636
  status: this.status,
3619
3637
  currentAction: update.currentAction,
3620
3638
  progressPct: update.progressPct,
3621
- timestamp
3639
+ timestamp,
3640
+ output: update.output
3622
3641
  });
3623
3642
  }
3624
3643
  buildMessage(type, to, payload) {
@@ -3999,16 +4018,17 @@ Now execute your subtask using this context where relevant.`
3999
4018
  return this.buildResult("ESCALATED", output, { checksRun, passed, failed }, issues, correctionAttempts);
4000
4019
  }
4001
4020
  }
4002
- this.setStatus("COMPLETED");
4003
- this.sendStatusUpdate({ progressPct: 100, currentAction: "Subtask complete", status: "IN_PROGRESS" });
4021
+ this.setStatus("COMPLETED", output);
4022
+ this.sendStatusUpdate({ progressPct: 100, currentAction: "Subtask complete", status: "IN_PROGRESS", output });
4004
4023
  this.peerBus?.publish(this.id, assignment.subtaskId, output, "COMPLETED");
4005
4024
  return this.buildResult("COMPLETED", output, { checksRun, passed, failed }, issues, correctionAttempts);
4006
4025
  } catch (err) {
4007
4026
  const errMsg = err instanceof Error ? err.message : String(err);
4008
4027
  issues.push(`Execution error: ${errMsg}`);
4009
- this.setStatus("FAILED");
4010
- this.peerBus?.publish(this.id, assignment.subtaskId, errMsg, "FAILED");
4011
- return this.buildResult("ESCALATED", output || errMsg, { checksRun, passed, failed }, issues, correctionAttempts);
4028
+ const finalOutput = output || errMsg;
4029
+ this.setStatus("FAILED", finalOutput);
4030
+ this.peerBus?.publish(this.id, assignment.subtaskId, finalOutput, "FAILED");
4031
+ return this.buildResult("ESCALATED", finalOutput, { checksRun, passed, failed }, issues, correctionAttempts);
4012
4032
  }
4013
4033
  }
4014
4034
  sendToPeer(toId, content) {
@@ -4062,6 +4082,10 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
4062
4082
  await this.context.addMessage({ role: "assistant", content: result.content, toolCalls: result.toolCalls });
4063
4083
  if (!result.toolCalls?.length) {
4064
4084
  if (requiresArtifact) {
4085
+ const artifactCheck = await this.verifyArtifacts(this.assignment);
4086
+ if (artifactCheck.ok) {
4087
+ return { output: result.content, toolCalls: allToolCalls };
4088
+ }
4065
4089
  stalledArtifactIterations += 1;
4066
4090
  if (stalledArtifactIterations >= 2) {
4067
4091
  if (stalledArtifactIterations === 2) {
@@ -4071,15 +4095,22 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
4071
4095
  }
4072
4096
  await this.context.addMessage({
4073
4097
  role: "user",
4074
- content: "You have not yet created and verified the required artifact. Use tools to create the file in the workspace, verify it exists, and inspect the result before concluding."
4098
+ content: `You have not yet created and verified the required artifact. Issues: ${artifactCheck.issues.join("; ")}. Use tools to create the file in the workspace, verify it exists, and inspect the result before concluding.`
4075
4099
  });
4076
4100
  continue;
4077
4101
  }
4078
4102
  return { output: result.content, toolCalls: allToolCalls };
4079
4103
  }
4080
4104
  stalledArtifactIterations = 0;
4081
- if (result.finishReason === "stop" && !requiresArtifact) {
4082
- return { output: result.content, toolCalls: allToolCalls };
4105
+ if (result.finishReason === "stop") {
4106
+ if (requiresArtifact) {
4107
+ const artifactCheck = await this.verifyArtifacts(this.assignment);
4108
+ if (artifactCheck.ok) {
4109
+ return { output: result.content, toolCalls: allToolCalls };
4110
+ }
4111
+ } else {
4112
+ return { output: result.content, toolCalls: allToolCalls };
4113
+ }
4083
4114
  }
4084
4115
  for (const tc of result.toolCalls) {
4085
4116
  allToolCalls.push(tc);
@@ -4691,8 +4722,9 @@ var T2Manager = class extends BaseTier {
4691
4722
  const summary = await this.aggregateResults(assignment, t3Results);
4692
4723
  const issues = t3Results.filter((r) => r.status !== "COMPLETED").flatMap((r) => r.issues);
4693
4724
  const overallStatus = this.determineStatus(t3Results);
4694
- this.setStatus(overallStatus === "COMPLETED" ? "COMPLETED" : "FAILED");
4695
- this.sendStatusUpdate({ progressPct: 100, currentAction: "Section complete", status: "IN_PROGRESS" });
4725
+ const isOk = overallStatus === "COMPLETED" || overallStatus === "PARTIAL";
4726
+ this.setStatus(isOk ? "COMPLETED" : "FAILED", summary);
4727
+ this.sendStatusUpdate({ progressPct: 100, currentAction: "Section complete", status: "IN_PROGRESS", output: summary });
4696
4728
  const result = {
4697
4729
  sectionId: assignment.sectionId,
4698
4730
  sectionTitle: assignment.sectionTitle,
@@ -4705,7 +4737,7 @@ var T2Manager = class extends BaseTier {
4705
4737
  return result;
4706
4738
  } catch (err) {
4707
4739
  const errMsg = err instanceof Error ? err.message : String(err);
4708
- this.setStatus("FAILED");
4740
+ this.setStatus("FAILED", errMsg);
4709
4741
  const failedResult = {
4710
4742
  sectionId: assignment.sectionId,
4711
4743
  sectionTitle: assignment.sectionTitle,
@@ -5270,8 +5302,8 @@ Create a CORRECTION PLAN that contains only the new sections needed to fix the i
5270
5302
  status: "IN_PROGRESS"
5271
5303
  });
5272
5304
  const output = await this.compileFinalOutput(userPrompt, plan, allT2Results);
5273
- this.setStatus("COMPLETED");
5274
- this.sendStatusUpdate({ progressPct: 100, currentAction: "Task complete", status: "IN_PROGRESS" });
5305
+ this.setStatus("COMPLETED", output);
5306
+ this.sendStatusUpdate({ progressPct: 100, currentAction: "Task complete", status: "IN_PROGRESS", output });
5275
5307
  return { output, t2Results: allT2Results, taskId: this.taskId, complexity: plan.complexity };
5276
5308
  }
5277
5309
  getEscalations() {
@@ -7549,10 +7581,17 @@ var Cascade = class extends EventEmitter {
7549
7581
  throw err;
7550
7582
  }
7551
7583
  }
7584
+ isCasualGreeting(prompt) {
7585
+ const casual = /^(hi|hello|hey|greetings|thanks|thank you|thx|bye|goodbye|cya)$/i.test(prompt.trim().replace(/[!?.]+$/, ""));
7586
+ return casual;
7587
+ }
7552
7588
  looksLikeSimpleArtifactTask(prompt) {
7553
7589
  return /create .*\.(txt|md|json|csv)\b/i.test(prompt) && !/(research|compare|thorough|pdf|report|analy[sz]e|architecture|multi-agent)/i.test(prompt);
7554
7590
  }
7555
7591
  async determineComplexity(prompt, workspacePath, conversationHistory = []) {
7592
+ if (this.isCasualGreeting(prompt)) {
7593
+ return "Simple";
7594
+ }
7556
7595
  if (this.looksLikeSimpleArtifactTask(prompt)) {
7557
7596
  return "Simple";
7558
7597
  }
@@ -8228,35 +8267,69 @@ function AgentTree({ root, theme }) {
8228
8267
  }
8229
8268
  function T2Row({ node, theme, isLast }) {
8230
8269
  const connector = isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
8231
- const t3Active = countByRoleAndStatus(node, "T3", "ACTIVE");
8232
- const t3Total = countByRole(node, "T3");
8233
- const workerSuffix = t3Total > 0 ? ` T3\xD7${t3Total}` : "";
8270
+ const t3Nodes = (node.children ?? []).filter((c) => c.role === "T3");
8271
+ const t3ActiveCount = t3Nodes.filter((c) => c.status === "ACTIVE").length;
8234
8272
  const label = stripRolePrefix(node.label, node.role);
8235
8273
  const action = node.currentAction ? ` ${node.currentAction.slice(0, 38)}` : "";
8274
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
8275
+ /* @__PURE__ */ jsxs(Box, { children: [
8276
+ /* @__PURE__ */ jsxs(Text, { color: theme.colors.muted, children: [
8277
+ " ",
8278
+ connector
8279
+ ] }),
8280
+ /* @__PURE__ */ jsx(Text, { color: theme.colors.t2Color, bold: true, children: "[T2]" }),
8281
+ /* @__PURE__ */ jsxs(Text, { color: theme.colors.foreground, children: [
8282
+ " ",
8283
+ label
8284
+ ] }),
8285
+ node.status === "ACTIVE" && /* @__PURE__ */ jsxs(Fragment, { children: [
8286
+ action ? /* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: action }) : null,
8287
+ /* @__PURE__ */ jsx(Text, { children: " " }),
8288
+ /* @__PURE__ */ jsx(Spinner, { type: "dots" }),
8289
+ t3ActiveCount > 0 ? /* @__PURE__ */ jsxs(Text, { color: theme.colors.muted, children: [
8290
+ " (",
8291
+ t3ActiveCount,
8292
+ " running)"
8293
+ ] }) : null
8294
+ ] }),
8295
+ node.status === "COMPLETED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.success, children: " \u2714" }),
8296
+ node.status === "FAILED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.error, children: " \u2718" }),
8297
+ node.status === "ESCALATED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.warning, children: " \u25B2" })
8298
+ ] }),
8299
+ t3Nodes.map((t3, idx) => /* @__PURE__ */ jsx(
8300
+ T3Row,
8301
+ {
8302
+ node: t3,
8303
+ theme,
8304
+ isLast: idx === t3Nodes.length - 1,
8305
+ parentIsLast: isLast
8306
+ },
8307
+ t3.id
8308
+ ))
8309
+ ] });
8310
+ }
8311
+ function T3Row({ node, theme, isLast, parentIsLast }) {
8312
+ const indent = parentIsLast ? " " : " \u2502 ";
8313
+ const connector = isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
8314
+ const label = stripRolePrefix(node.label, node.role);
8315
+ const action = node.currentAction ? ` ${node.currentAction.slice(0, 42)}` : "";
8236
8316
  return /* @__PURE__ */ jsxs(Box, { children: [
8237
8317
  /* @__PURE__ */ jsxs(Text, { color: theme.colors.muted, children: [
8238
- " ",
8318
+ indent,
8239
8319
  connector
8240
8320
  ] }),
8241
- /* @__PURE__ */ jsx(Text, { color: theme.colors.t2Color, bold: true, children: "[T2]" }),
8242
- /* @__PURE__ */ jsxs(Text, { color: theme.colors.foreground, children: [
8321
+ /* @__PURE__ */ jsx(Text, { color: theme.colors.t3Color, children: "[T3]" }),
8322
+ /* @__PURE__ */ jsxs(Text, { color: theme.colors.muted, children: [
8243
8323
  " ",
8244
8324
  label
8245
8325
  ] }),
8246
- workerSuffix ? /* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: workerSuffix }) : null,
8247
8326
  node.status === "ACTIVE" && /* @__PURE__ */ jsxs(Fragment, { children: [
8248
8327
  action ? /* @__PURE__ */ jsx(Text, { color: theme.colors.muted, children: action }) : null,
8249
8328
  /* @__PURE__ */ jsx(Text, { children: " " }),
8250
- /* @__PURE__ */ jsx(Spinner, { type: "dots" }),
8251
- t3Active > 0 ? /* @__PURE__ */ jsxs(Text, { color: theme.colors.muted, children: [
8252
- " (",
8253
- t3Active,
8254
- " running)"
8255
- ] }) : null
8329
+ /* @__PURE__ */ jsx(Spinner, { type: "dots" })
8256
8330
  ] }),
8257
8331
  node.status === "COMPLETED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.success, children: " \u2714" }),
8258
- node.status === "FAILED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.error, children: " \u2718" }),
8259
- node.status === "ESCALATED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.warning, children: " \u25B2" })
8332
+ node.status === "FAILED" && /* @__PURE__ */ jsx(Text, { color: theme.colors.error, children: " \u2718" })
8260
8333
  ] });
8261
8334
  }
8262
8335
  function hasActiveOrFailed(node) {
@@ -8825,7 +8898,7 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName })
8825
8898
  if (sel.kind === "auto") {
8826
8899
  delete config.models[tierKey];
8827
8900
  } else {
8828
- config.models[tierKey] = sel.modelId;
8901
+ config.models[tierKey] = `${sel.provider}:${sel.modelId}`;
8829
8902
  }
8830
8903
  try {
8831
8904
  const router = cascadeRef.current?.getRouter();
@@ -8868,7 +8941,7 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName })
8868
8941
  treeNodesRef.current.set(event.tierId, node);
8869
8942
  const store = storeRef.current;
8870
8943
  if (store) {
8871
- 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 };
8944
+ 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 };
8872
8945
  store.upsertRuntimeNode(runtimeNode);
8873
8946
  globalStoreRef.current?.upsertRuntimeNode({ ...runtimeNode, isGlobal: true });
8874
8947
  }
@@ -8879,7 +8952,6 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName })
8879
8952
  useEffect(() => {
8880
8953
  const originalWarn = console.warn;
8881
8954
  const originalLog = console.log;
8882
- process.stdout.write("\x1Bc");
8883
8955
  console.warn = (...args) => {
8884
8956
  const msg = args.join(" ");
8885
8957
  if (msg.includes("non-text parts") || msg.includes("functionCall")) return;
@@ -9384,9 +9456,15 @@ Use /identity <name|id> to switch.`;
9384
9456
  let agentTreeHeight = 0;
9385
9457
  if (state.agentTree && hasActiveOrFailed2(state.agentTree)) {
9386
9458
  agentTreeHeight = 1;
9387
- const childrenCount = state.agentTree.children?.length ?? 0;
9388
- agentTreeHeight += Math.min(childrenCount, 6);
9389
- if (childrenCount > 6) agentTreeHeight += 1;
9459
+ const visibleT2s = state.agentTree.children?.slice(0, 6) ?? [];
9460
+ agentTreeHeight += visibleT2s.length;
9461
+ for (const t2 of visibleT2s) {
9462
+ const t3Count = (t2.children ?? []).filter((c) => c.role === "T3").length;
9463
+ agentTreeHeight += t3Count;
9464
+ }
9465
+ if ((state.agentTree.children?.length ?? 0) > 6) {
9466
+ agentTreeHeight += 1;
9467
+ }
9390
9468
  }
9391
9469
  let timelineHeight = 0;
9392
9470
  if (state.showDetails && treeNodesRef.current.size > 0) {
@@ -9575,6 +9653,13 @@ async function validateConfiguredModels(config) {
9575
9653
  return problems.length ? `Model warnings: ${problems.join(", ")}` : null;
9576
9654
  }
9577
9655
  function inferProviderFromModelId(id, providers) {
9656
+ if (id.includes(":")) {
9657
+ const prefix = id.split(":")[0].toLowerCase();
9658
+ const validProviders = ["anthropic", "openai", "gemini", "azure", "openai-compatible", "ollama"];
9659
+ if (validProviders.includes(prefix)) {
9660
+ return prefix;
9661
+ }
9662
+ }
9578
9663
  const lower = id.toLowerCase();
9579
9664
  if (lower.includes("gpt")) return "openai";
9580
9665
  if (lower.includes("claude")) return "anthropic";
@@ -10989,8 +11074,8 @@ var DashboardServer = class {
10989
11074
  void (async () => {
10990
11075
  const cascade = new Cascade(this.config, this.workspacePath, this.store);
10991
11076
  cascade.on("stream:token", (e) => {
10992
- this.socket.broadcast("stream:token", { sessionId, token: e.text });
10993
- this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, token: e.text });
11077
+ this.socket.broadcast("stream:token", { sessionId, tierId: e.tierId, text: e.text });
11078
+ this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, tierId: e.tierId, text: e.text });
10994
11079
  });
10995
11080
  cascade.on("tier:status", (e) => {
10996
11081
  this.socket.broadcast("tier:status", { sessionId, ...e });
@@ -11426,6 +11511,9 @@ async function startRepl(options) {
11426
11511
  await cm.load();
11427
11512
  config = cm.getConfig();
11428
11513
  }
11514
+ if (process.stdout.isTTY) {
11515
+ process.stdout.write("\x1B[2J\x1B[H");
11516
+ }
11429
11517
  const { waitUntilExit } = render(
11430
11518
  React5.createElement(Repl, {
11431
11519
  config,