cascade-ai 0.2.0 → 0.2.1
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/README.md +10 -3
- package/bin/cascade.js +3 -3
- package/completions/cascade.bash +23 -23
- package/completions/cascade.fish +20 -20
- package/completions/cascade.zsh +32 -32
- package/dist/cli.cjs +295 -96
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +295 -96
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +25 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +25 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/web/dist/assets/{index-qNI_hqt1.js → index-BdaS_Mbj.js} +69 -69
- package/web/dist/index.html +13 -13
package/dist/cli.cjs
CHANGED
|
@@ -27,9 +27,9 @@ var index_js = require('@modelcontextprotocol/sdk/client/index.js');
|
|
|
27
27
|
var stdio_js = require('@modelcontextprotocol/sdk/client/stdio.js');
|
|
28
28
|
var vm = require('vm');
|
|
29
29
|
var Spinner = require('ink-spinner');
|
|
30
|
-
var SelectInput = require('ink-select-input');
|
|
31
30
|
var wrapAnsi = require('wrap-ansi');
|
|
32
31
|
var ora = require('ora');
|
|
32
|
+
var SelectInput = require('ink-select-input');
|
|
33
33
|
var http = require('http');
|
|
34
34
|
var url = require('url');
|
|
35
35
|
var express = require('express');
|
|
@@ -76,9 +76,9 @@ var Database__default = /*#__PURE__*/_interopDefault(Database);
|
|
|
76
76
|
var EventEmitter__default = /*#__PURE__*/_interopDefault(EventEmitter);
|
|
77
77
|
var PDFDocument__default = /*#__PURE__*/_interopDefault(PDFDocument);
|
|
78
78
|
var Spinner__default = /*#__PURE__*/_interopDefault(Spinner);
|
|
79
|
-
var SelectInput__default = /*#__PURE__*/_interopDefault(SelectInput);
|
|
80
79
|
var wrapAnsi__default = /*#__PURE__*/_interopDefault(wrapAnsi);
|
|
81
80
|
var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
81
|
+
var SelectInput__default = /*#__PURE__*/_interopDefault(SelectInput);
|
|
82
82
|
var express__default = /*#__PURE__*/_interopDefault(express);
|
|
83
83
|
var rateLimit__default = /*#__PURE__*/_interopDefault(rateLimit);
|
|
84
84
|
var bcrypt__default = /*#__PURE__*/_interopDefault(bcrypt);
|
|
@@ -129,7 +129,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
129
129
|
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;
|
|
130
130
|
var init_constants = __esm({
|
|
131
131
|
"src/constants.ts"() {
|
|
132
|
-
CASCADE_VERSION = "0.
|
|
132
|
+
CASCADE_VERSION = "0.2.0";
|
|
133
133
|
CASCADE_CONFIG_FILE = ".cascade/config.json";
|
|
134
134
|
CASCADE_DB_FILE = ".cascade/memory.db";
|
|
135
135
|
CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
|
|
@@ -2827,6 +2827,15 @@ var ModelSelector = class {
|
|
|
2827
2827
|
markProviderUnavailable(provider) {
|
|
2828
2828
|
this.availableProviders.delete(provider);
|
|
2829
2829
|
}
|
|
2830
|
+
/**
|
|
2831
|
+
* Re-add a provider to the available set after it has recovered (e.g. after
|
|
2832
|
+
* a failover timeout expires or a successful call confirms recovery). Only
|
|
2833
|
+
* re-enables providers that were originally configured — callers should
|
|
2834
|
+
* guard against enabling providers that were never configured.
|
|
2835
|
+
*/
|
|
2836
|
+
markProviderAvailable(provider) {
|
|
2837
|
+
this.availableProviders.add(provider);
|
|
2838
|
+
}
|
|
2830
2839
|
resolveDynamicModel(overrideModelId) {
|
|
2831
2840
|
let providerStr = null;
|
|
2832
2841
|
let actualId = overrideModelId;
|
|
@@ -2896,10 +2905,23 @@ var FailoverManager = class {
|
|
|
2896
2905
|
if (!failure) return true;
|
|
2897
2906
|
if (Date.now() - failure.failedAt >= failure.retryAfterMs) {
|
|
2898
2907
|
this.failures.delete(provider);
|
|
2908
|
+
this.selector.markProviderAvailable(provider);
|
|
2899
2909
|
return true;
|
|
2900
2910
|
}
|
|
2901
2911
|
return false;
|
|
2902
2912
|
}
|
|
2913
|
+
/**
|
|
2914
|
+
* Call after a successful generation to immediately re-enable a provider
|
|
2915
|
+
* that had previously been marked unavailable. This allows fast recovery
|
|
2916
|
+
* when a transient rate-limit clears before the backoff window expires,
|
|
2917
|
+
* preventing unnecessary routing to more expensive fallback models.
|
|
2918
|
+
*/
|
|
2919
|
+
recordSuccess(provider) {
|
|
2920
|
+
if (this.failures.has(provider)) {
|
|
2921
|
+
this.failures.delete(provider);
|
|
2922
|
+
this.selector.markProviderAvailable(provider);
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2903
2925
|
getFallbackModel(currentModel, tier) {
|
|
2904
2926
|
return this.selector.getNextFallback(currentModel.id, tier);
|
|
2905
2927
|
}
|
|
@@ -2916,6 +2938,7 @@ var FailoverManager = class {
|
|
|
2916
2938
|
}
|
|
2917
2939
|
clearFailure(provider) {
|
|
2918
2940
|
this.failures.delete(provider);
|
|
2941
|
+
this.selector.markProviderAvailable(provider);
|
|
2919
2942
|
}
|
|
2920
2943
|
};
|
|
2921
2944
|
|
|
@@ -3131,6 +3154,7 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
3131
3154
|
throw new Error(`Provider ${model.provider}:${model.id} returned an invalid generation result.`);
|
|
3132
3155
|
}
|
|
3133
3156
|
this.recordStats(tier, model, result.usage);
|
|
3157
|
+
this.failover.recordSuccess(model.provider);
|
|
3134
3158
|
return result;
|
|
3135
3159
|
} catch (err) {
|
|
3136
3160
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -7350,12 +7374,17 @@ var SlashCommandRegistry = class {
|
|
|
7350
7374
|
});
|
|
7351
7375
|
this.register({
|
|
7352
7376
|
command: "/model",
|
|
7377
|
+
description: "Pick a provider and model for a tier (interactive)",
|
|
7378
|
+
handler: (_args, ctx) => ({ output: ctx.onModelPicker(), handled: true })
|
|
7379
|
+
});
|
|
7380
|
+
this.register({
|
|
7381
|
+
command: "/model-info",
|
|
7353
7382
|
description: "Show active models per tier",
|
|
7354
7383
|
handler: (_args, ctx) => ({ output: ctx.onModelInfo(), handled: true })
|
|
7355
7384
|
});
|
|
7356
7385
|
this.register({
|
|
7357
7386
|
command: "/models",
|
|
7358
|
-
description: "
|
|
7387
|
+
description: "Browse available models by provider",
|
|
7359
7388
|
handler: (_args, ctx) => ({ output: ctx.onModelsInfo(), handled: true })
|
|
7360
7389
|
});
|
|
7361
7390
|
this.register({
|
|
@@ -7658,55 +7687,140 @@ function ApprovalPrompt({ request, theme, onDecision }) {
|
|
|
7658
7687
|
}
|
|
7659
7688
|
);
|
|
7660
7689
|
}
|
|
7661
|
-
var
|
|
7662
|
-
|
|
7663
|
-
|
|
7690
|
+
var TIERS = [
|
|
7691
|
+
{ id: "T1", label: "T1 \u2014 Administrator", hint: "complex reasoning \xB7 runs once per task" },
|
|
7692
|
+
{ id: "T2", label: "T2 \u2014 Manager", hint: "per-section planning \xB7 a few calls per task" },
|
|
7693
|
+
{ id: "T3", label: "T3 \u2014 Worker", hint: "high volume \xB7 many parallel runs" }
|
|
7694
|
+
];
|
|
7695
|
+
var ModelsDisplay = ({
|
|
7696
|
+
providers,
|
|
7697
|
+
modelsByProvider,
|
|
7698
|
+
onSelect,
|
|
7699
|
+
onClose
|
|
7700
|
+
}) => {
|
|
7701
|
+
const [step, setStep] = React5.useState("PROVIDER");
|
|
7702
|
+
const [cursor, setCursor] = React5.useState(0);
|
|
7703
|
+
const [picked, setPicked] = React5.useState({});
|
|
7704
|
+
const providerItems = React5.useMemo(() => {
|
|
7705
|
+
const base = [
|
|
7706
|
+
{ label: "\u25C7 Auto", sublabel: "let Cascade pick per tier \u2014 recommended", value: "auto" }
|
|
7707
|
+
];
|
|
7708
|
+
for (const p of providers) {
|
|
7709
|
+
const count = modelsByProvider.get(p)?.length ?? 0;
|
|
7710
|
+
base.push({ label: p, sublabel: `${count} model${count === 1 ? "" : "s"} discovered`, value: p });
|
|
7711
|
+
}
|
|
7712
|
+
return base;
|
|
7713
|
+
}, [providers, modelsByProvider]);
|
|
7714
|
+
const tierItems = React5.useMemo(
|
|
7715
|
+
() => TIERS.map((t) => ({ label: t.label, sublabel: t.hint, value: t.id })),
|
|
7716
|
+
[]
|
|
7717
|
+
);
|
|
7718
|
+
const modelItems = React5.useMemo(() => {
|
|
7719
|
+
if (!picked.provider || picked.provider === "auto") return [];
|
|
7720
|
+
const list = (modelsByProvider.get(picked.provider) ?? []).slice().sort((a, b) => a.id.localeCompare(b.id));
|
|
7721
|
+
const items = [
|
|
7722
|
+
{ label: "\u25C7 Auto", sublabel: "best available from this provider", value: "__auto__" }
|
|
7723
|
+
];
|
|
7724
|
+
for (const m of list) {
|
|
7725
|
+
const ctx = m.contextWindow >= 1e6 ? `${(m.contextWindow / 1e6).toFixed(1)}M ctx` : m.contextWindow >= 1e3 ? `${Math.round(m.contextWindow / 1e3)}K ctx` : `${m.contextWindow} ctx`;
|
|
7726
|
+
items.push({ label: m.name, sublabel: `${m.id} \xB7 ${ctx}`, value: m.id });
|
|
7727
|
+
}
|
|
7728
|
+
return items;
|
|
7729
|
+
}, [picked.provider, modelsByProvider]);
|
|
7730
|
+
const currentItems = step === "PROVIDER" ? providerItems : step === "TIER" ? tierItems : modelItems;
|
|
7731
|
+
ink.useInput((input, key) => {
|
|
7664
7732
|
if (key.escape) {
|
|
7665
|
-
if (
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7733
|
+
if (step === "MODEL") {
|
|
7734
|
+
setStep("TIER");
|
|
7735
|
+
setCursor(0);
|
|
7736
|
+
return;
|
|
7669
7737
|
}
|
|
7738
|
+
if (step === "TIER") {
|
|
7739
|
+
setStep("PROVIDER");
|
|
7740
|
+
setCursor(0);
|
|
7741
|
+
return;
|
|
7742
|
+
}
|
|
7743
|
+
onClose();
|
|
7744
|
+
return;
|
|
7670
7745
|
}
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7746
|
+
if (key.upArrow || input === "k") {
|
|
7747
|
+
setCursor((c) => currentItems.length === 0 ? 0 : (c - 1 + currentItems.length) % currentItems.length);
|
|
7748
|
+
return;
|
|
7749
|
+
}
|
|
7750
|
+
if (key.downArrow || key.tab || input === "j") {
|
|
7751
|
+
setCursor((c) => currentItems.length === 0 ? 0 : (c + 1) % currentItems.length);
|
|
7752
|
+
return;
|
|
7753
|
+
}
|
|
7754
|
+
if (/^[1-9]$/.test(input)) {
|
|
7755
|
+
const idx = parseInt(input, 10) - 1;
|
|
7756
|
+
if (idx < currentItems.length) setCursor(idx);
|
|
7757
|
+
return;
|
|
7758
|
+
}
|
|
7759
|
+
if (key.return) {
|
|
7760
|
+
const selected = currentItems[cursor];
|
|
7761
|
+
if (!selected) return;
|
|
7762
|
+
if (step === "PROVIDER") {
|
|
7763
|
+
if (selected.value === "auto") {
|
|
7764
|
+
setPicked({ provider: "auto" });
|
|
7765
|
+
setStep("TIER");
|
|
7766
|
+
setCursor(0);
|
|
7767
|
+
return;
|
|
7688
7768
|
}
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
] }),
|
|
7701
|
-
modelItems.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { italic: true, color: "yellow", children: "No supported models discovered for this provider." }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
7702
|
-
SelectInput__default.default,
|
|
7703
|
-
{
|
|
7704
|
-
items: modelItems,
|
|
7705
|
-
limit: 10,
|
|
7706
|
-
onSelect: () => {
|
|
7769
|
+
setPicked({ provider: selected.value });
|
|
7770
|
+
setStep("TIER");
|
|
7771
|
+
setCursor(0);
|
|
7772
|
+
return;
|
|
7773
|
+
}
|
|
7774
|
+
if (step === "TIER") {
|
|
7775
|
+
const tier = selected.value;
|
|
7776
|
+
if (picked.provider === "auto") {
|
|
7777
|
+
onSelect?.({ kind: "auto", tier });
|
|
7778
|
+
onClose();
|
|
7779
|
+
return;
|
|
7707
7780
|
}
|
|
7781
|
+
setPicked((p) => ({ ...p, tier }));
|
|
7782
|
+
setStep("MODEL");
|
|
7783
|
+
setCursor(0);
|
|
7784
|
+
return;
|
|
7708
7785
|
}
|
|
7709
|
-
|
|
7786
|
+
if (!picked.tier || !picked.provider || picked.provider === "auto") {
|
|
7787
|
+
onClose();
|
|
7788
|
+
return;
|
|
7789
|
+
}
|
|
7790
|
+
if (selected.value === "__auto__") {
|
|
7791
|
+
onSelect?.({ kind: "auto", tier: picked.tier });
|
|
7792
|
+
} else {
|
|
7793
|
+
onSelect?.({
|
|
7794
|
+
kind: "pick",
|
|
7795
|
+
tier: picked.tier,
|
|
7796
|
+
provider: picked.provider,
|
|
7797
|
+
modelId: selected.value
|
|
7798
|
+
});
|
|
7799
|
+
}
|
|
7800
|
+
onClose();
|
|
7801
|
+
}
|
|
7802
|
+
});
|
|
7803
|
+
const title = step === "PROVIDER" ? "\u25C8 SELECT PROVIDER" : step === "TIER" ? `\u25C8 APPLY ${picked.provider === "auto" ? "AUTO" : String(picked.provider).toUpperCase()} TO WHICH TIER?` : `\u25C8 ${String(picked.provider).toUpperCase()} \u2192 SELECT MODEL FOR ${picked.tier}`;
|
|
7804
|
+
const breadcrumb = step === "PROVIDER" ? "Step 1 / 3" : step === "TIER" ? `Step 2 / 3 \xB7 provider: ${picked.provider}` : `Step 3 / 3 \xB7 ${picked.provider} \u2192 ${picked.tier}`;
|
|
7805
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
|
|
7806
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { justifyContent: "space-between", children: [
|
|
7807
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, color: "cyan", children: title }),
|
|
7808
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "gray", children: "[Esc back \xB7 Enter select]" })
|
|
7809
|
+
] }),
|
|
7810
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginBottom: 1, children: /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: "gray", children: [
|
|
7811
|
+
breadcrumb,
|
|
7812
|
+
" \xB7 \u2191/\u2193 navigate \xB7 1\u20139 jump"
|
|
7813
|
+
] }) }),
|
|
7814
|
+
currentItems.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { italic: true, color: "yellow", children: "No items to show." }) : /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { flexDirection: "column", children: currentItems.map((item, i) => {
|
|
7815
|
+
const focused = i === cursor;
|
|
7816
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", children: [
|
|
7817
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: focused ? "green" : "gray", children: focused ? "\u276F " : " " }),
|
|
7818
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
7819
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: focused ? "white" : "gray", bold: focused, children: item.label }),
|
|
7820
|
+
item.sublabel && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "gray", dimColor: true, children: ` ${item.sublabel}` })
|
|
7821
|
+
] })
|
|
7822
|
+
] }, `${step}-${item.value}-${i}`);
|
|
7823
|
+
}) })
|
|
7710
7824
|
] });
|
|
7711
7825
|
};
|
|
7712
7826
|
function CostTracker({
|
|
@@ -7971,6 +8085,40 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName })
|
|
|
7971
8085
|
const persistMessage = React5.useCallback((role, content, timestamp) => {
|
|
7972
8086
|
storeRef.current?.addMessage({ id: crypto.randomUUID(), sessionId: sessionIdRef.current, role, content, timestamp });
|
|
7973
8087
|
}, []);
|
|
8088
|
+
const applyModelPick = React5.useCallback(async (sel) => {
|
|
8089
|
+
const tierKey = sel.tier.toLowerCase();
|
|
8090
|
+
const tierLabel = sel.tier;
|
|
8091
|
+
if (sel.kind === "auto") {
|
|
8092
|
+
delete config.models[tierKey];
|
|
8093
|
+
} else {
|
|
8094
|
+
config.models[tierKey] = sel.modelId;
|
|
8095
|
+
}
|
|
8096
|
+
try {
|
|
8097
|
+
const router = cascadeRef.current?.getRouter();
|
|
8098
|
+
if (router && sel.kind === "pick") {
|
|
8099
|
+
const candidate = (cachedModels.get(sel.provider) ?? []).find((m) => m.id === sel.modelId);
|
|
8100
|
+
if (candidate) router.overrideTierModel(tierLabel, candidate);
|
|
8101
|
+
}
|
|
8102
|
+
} catch {
|
|
8103
|
+
}
|
|
8104
|
+
const configPath = path17__default.default.join(workspacePath, ".cascade", "config.json");
|
|
8105
|
+
try {
|
|
8106
|
+
await fs7__default.default.mkdir(path17__default.default.dirname(configPath), { recursive: true });
|
|
8107
|
+
await fs7__default.default.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
8108
|
+
} catch (err) {
|
|
8109
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
8110
|
+
dispatch({
|
|
8111
|
+
type: "ADD_MESSAGE",
|
|
8112
|
+
message: { id: crypto.randomUUID(), role: "error", content: `Failed to persist model selection: ${msg}`, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
8113
|
+
});
|
|
8114
|
+
return;
|
|
8115
|
+
}
|
|
8116
|
+
const summary = sel.kind === "auto" ? `${tierLabel} \u2192 Auto (Cascade will pick best available).` : `${tierLabel} \u2192 ${sel.modelId} (provider: ${sel.provider}).`;
|
|
8117
|
+
dispatch({
|
|
8118
|
+
type: "ADD_MESSAGE",
|
|
8119
|
+
message: { id: crypto.randomUUID(), role: "system", content: `\u2714 Model updated \u2014 ${summary}`, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
8120
|
+
});
|
|
8121
|
+
}, [config, workspacePath, cachedModels]);
|
|
7974
8122
|
const globalStoreRef = React5.useRef(null);
|
|
7975
8123
|
const persistRuntimeSession = React5.useCallback((status, latestPrompt) => {
|
|
7976
8124
|
const runtimeSession = { sessionId: sessionIdRef.current, title: sessionTitle, workspacePath, status, startedAt: startedAtRef.current, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), latestPrompt, isGlobal: false };
|
|
@@ -8117,6 +8265,10 @@ ${msg.content}`).join("\n\n");
|
|
|
8117
8265
|
return `${t}: ${m?.name ?? "none"} (${m?.provider ?? "\u2014"})${configured ? ` | configured: ${configured}` : ""}`;
|
|
8118
8266
|
}).join("\n");
|
|
8119
8267
|
},
|
|
8268
|
+
onModelPicker: async () => {
|
|
8269
|
+
setIsShowingModels(true);
|
|
8270
|
+
return "Opening model picker \u2014 choose provider \u2192 tier \u2192 model (ESC to exit).";
|
|
8271
|
+
},
|
|
8120
8272
|
onModelsInfo: async () => {
|
|
8121
8273
|
setIsShowingModels(true);
|
|
8122
8274
|
return "Opening interactive models explorer... (ESC to exit)";
|
|
@@ -8399,6 +8551,16 @@ Use /identity <name|id> to switch.`;
|
|
|
8399
8551
|
}
|
|
8400
8552
|
}, [handleSlashCommand, persistMessage, state.messages, workspacePath, rebuildTree, recordNodeEvent, slashCompletions, slashIndex, state.isExecuting]);
|
|
8401
8553
|
ink.useInput((_input, key) => {
|
|
8554
|
+
if (isShowingModels) {
|
|
8555
|
+
if (key.ctrl && _input === "c") {
|
|
8556
|
+
if (quitAttempted) {
|
|
8557
|
+
exit();
|
|
8558
|
+
return;
|
|
8559
|
+
}
|
|
8560
|
+
setQuitAttempted(true);
|
|
8561
|
+
}
|
|
8562
|
+
return;
|
|
8563
|
+
}
|
|
8402
8564
|
if (key.ctrl && _input === "c") {
|
|
8403
8565
|
if (quitAttempted) {
|
|
8404
8566
|
exit();
|
|
@@ -8567,7 +8729,17 @@ Use /identity <name|id> to switch.`;
|
|
|
8567
8729
|
] }) }),
|
|
8568
8730
|
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 1, children: [
|
|
8569
8731
|
state.messages.length === 0 && !state.isStreaming && /* @__PURE__ */ jsxRuntime.jsx(WelcomeBanner, { theme, config, workspacePath, sessionId: sessionIdRef.current }),
|
|
8570
|
-
isShowingModels && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8732
|
+
isShowingModels && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8733
|
+
ModelsDisplay,
|
|
8734
|
+
{
|
|
8735
|
+
providers: config.providers.map((p) => p.type),
|
|
8736
|
+
modelsByProvider: cachedModels,
|
|
8737
|
+
onSelect: (sel) => {
|
|
8738
|
+
void applyModelPick(sel);
|
|
8739
|
+
},
|
|
8740
|
+
onClose: () => setIsShowingModels(false)
|
|
8741
|
+
}
|
|
8742
|
+
)
|
|
8571
8743
|
] }),
|
|
8572
8744
|
/* @__PURE__ */ jsxRuntime.jsx(AgentTree, { root: state.agentTree, theme }),
|
|
8573
8745
|
state.showDetails && /* @__PURE__ */ jsxRuntime.jsx(TimelinePanel, { nodes: [...treeNodesRef.current.values()], theme, currentIndex: timelineIndex, onChangeIndex: setTimelineIndex }),
|
|
@@ -8619,7 +8791,7 @@ Use /identity <name|id> to switch.`;
|
|
|
8619
8791
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8620
8792
|
SafeTextInput,
|
|
8621
8793
|
{
|
|
8622
|
-
focus: !state.approvalRequest,
|
|
8794
|
+
focus: !state.approvalRequest && !isShowingModels,
|
|
8623
8795
|
value: input,
|
|
8624
8796
|
manageMouseReporting: false,
|
|
8625
8797
|
onChange: (val) => {
|
|
@@ -8812,6 +8984,7 @@ var PROVIDER_LABELS = {
|
|
|
8812
8984
|
ollama: "Ollama (local)",
|
|
8813
8985
|
"openai-compatible": "OpenAI-Compatible"
|
|
8814
8986
|
};
|
|
8987
|
+
var providerOrder = ["anthropic", "openai", "gemini", "azure", "ollama", "openai-compatible"];
|
|
8815
8988
|
function buildInitialEntries(types) {
|
|
8816
8989
|
return [...types].map((type) => ({
|
|
8817
8990
|
id: crypto.randomUUID(),
|
|
@@ -8827,6 +9000,17 @@ function wizardReducer(state, action) {
|
|
|
8827
9000
|
else next.add(action.provider);
|
|
8828
9001
|
return { ...state, selectedTypes: next };
|
|
8829
9002
|
}
|
|
9003
|
+
case "TOGGLE_ALL": {
|
|
9004
|
+
const allSelected = state.selectedTypes.size === providerOrder.length;
|
|
9005
|
+
return { ...state, selectedTypes: allSelected ? /* @__PURE__ */ new Set() : new Set(providerOrder) };
|
|
9006
|
+
}
|
|
9007
|
+
case "INVERT_SELECTION": {
|
|
9008
|
+
const next = /* @__PURE__ */ new Set();
|
|
9009
|
+
providerOrder.forEach((p) => {
|
|
9010
|
+
if (!state.selectedTypes.has(p)) next.add(p);
|
|
9011
|
+
});
|
|
9012
|
+
return { ...state, selectedTypes: next };
|
|
9013
|
+
}
|
|
8830
9014
|
case "CONFIRM_PROVIDERS": {
|
|
8831
9015
|
const entries = buildInitialEntries(state.selectedTypes);
|
|
8832
9016
|
return { ...state, entries, currentEntryIdx: 0, step: "API_KEYS" };
|
|
@@ -8914,7 +9098,6 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
8914
9098
|
tierSelectFocus: "T1",
|
|
8915
9099
|
error: null
|
|
8916
9100
|
});
|
|
8917
|
-
const providerOrder = ["anthropic", "openai", "gemini", "azure", "ollama", "openai-compatible"];
|
|
8918
9101
|
const [providerCursor, setProviderCursor] = React5.useState(0);
|
|
8919
9102
|
const [fieldBuffer, setFieldBuffer] = React5.useState("");
|
|
8920
9103
|
const [fieldStage, setFieldStage] = React5.useState("apiKey");
|
|
@@ -9007,6 +9190,8 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9007
9190
|
if (key.upArrow) setProviderCursor((p) => Math.max(0, p - 1));
|
|
9008
9191
|
if (key.downArrow) setProviderCursor((p) => Math.min(providerOrder.length - 1, p + 1));
|
|
9009
9192
|
if (_input === " ") dispatch({ type: "TOGGLE_PROVIDER", provider: providerOrder[providerCursor] });
|
|
9193
|
+
if (_input === "a") dispatch({ type: "TOGGLE_ALL" });
|
|
9194
|
+
if (_input === "i") dispatch({ type: "INVERT_SELECTION" });
|
|
9010
9195
|
if (key.return) {
|
|
9011
9196
|
if (state.selectedTypes.size === 0) return;
|
|
9012
9197
|
dispatch({ type: "CONFIRM_PROVIDERS" });
|
|
@@ -9068,21 +9253,23 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9068
9253
|
}, [currentEntry, fieldStage]);
|
|
9069
9254
|
if (state.step === "PROVIDER_SELECT") {
|
|
9070
9255
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9071
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9072
|
-
|
|
9073
|
-
|
|
9074
|
-
|
|
9256
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginBottom: 1, children: [
|
|
9257
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", bold: true, children: "? " }),
|
|
9258
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Which providers do you want to configure?" })
|
|
9259
|
+
] }),
|
|
9260
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { flexDirection: "column", children: providerOrder.map((p, i) => {
|
|
9075
9261
|
const selected = state.selectedTypes.has(p);
|
|
9076
9262
|
const focused = i === providerCursor;
|
|
9077
9263
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { children: [
|
|
9078
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: focused ? "
|
|
9079
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: selected ? "green" : "white", children: selected ? "\u25C9 " : "\
|
|
9080
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: focused ? "white" : "gray", children: PROVIDER_LABELS[p] }),
|
|
9264
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: focused ? "magenta" : "white", children: focused ? "\u276F " : " " }),
|
|
9265
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: selected ? "green" : "white", children: selected ? "\u25C9 " : "\u25EF " }),
|
|
9266
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: focused ? "magenta" : selected ? "white" : "gray", children: PROVIDER_LABELS[p] }),
|
|
9081
9267
|
p === "azure" && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: " \u2014 multiple deployments supported" }),
|
|
9082
9268
|
p === "openai-compatible" && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: " \u2014 Groq, Together, custom" }),
|
|
9083
9269
|
p === "ollama" && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: " \u2014 no API key needed" })
|
|
9084
9270
|
] }, p);
|
|
9085
9271
|
}) }),
|
|
9272
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "(Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)" }) }),
|
|
9086
9273
|
state.error && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "red", children: state.error })
|
|
9087
9274
|
] });
|
|
9088
9275
|
}
|
|
@@ -9092,15 +9279,19 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9092
9279
|
const isOllama = currentEntry.type === "ollama";
|
|
9093
9280
|
if (fieldStage === "askMore") {
|
|
9094
9281
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9095
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9096
|
-
|
|
9097
|
-
|
|
9282
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginBottom: 1, children: [
|
|
9283
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", bold: true, children: "? " }),
|
|
9284
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: isAzure ? "Add another Azure deployment? (y/n)" : "Add another custom endpoint? (y/n)" })
|
|
9285
|
+
] }),
|
|
9286
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9098
9287
|
SelectInput__default.default,
|
|
9099
9288
|
{
|
|
9100
9289
|
items: [
|
|
9101
9290
|
{ label: "Yes \u2014 add another", value: "yes" },
|
|
9102
9291
|
{ label: "No \u2014 continue", value: "no" }
|
|
9103
9292
|
],
|
|
9293
|
+
indicatorComponent: ({ isSelected }) => /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", children: isSelected ? "\u276F " : " " }),
|
|
9294
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: isSelected ? "magenta" : "white", children: label }),
|
|
9104
9295
|
onSelect: (item) => {
|
|
9105
9296
|
if (item.value === "yes") {
|
|
9106
9297
|
if (isAzure) dispatch({ type: "ADD_AZURE" });
|
|
@@ -9117,21 +9308,15 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9117
9308
|
) })
|
|
9118
9309
|
] });
|
|
9119
9310
|
}
|
|
9120
|
-
const prompt = isAzure && fieldStage === "deploymentName" ? `Azure deployment name (${currentEntry.label})
|
|
9311
|
+
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`;
|
|
9121
9312
|
const isMasked = fieldStage === "apiKey";
|
|
9122
9313
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9123
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
|
|
9127
|
-
|
|
9128
|
-
|
|
9129
|
-
": ",
|
|
9130
|
-
currentEntry.label
|
|
9131
|
-
] }) }),
|
|
9132
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: prompt }),
|
|
9133
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, children: [
|
|
9134
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "green", children: "> " }),
|
|
9314
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginBottom: 1, children: [
|
|
9315
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", bold: true, children: "? " }),
|
|
9316
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: prompt })
|
|
9317
|
+
] }),
|
|
9318
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { children: [
|
|
9319
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", children: "\u276F " }),
|
|
9135
9320
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9136
9321
|
SafeTextInput,
|
|
9137
9322
|
{
|
|
@@ -9149,13 +9334,13 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9149
9334
|
}
|
|
9150
9335
|
if (state.step === "FETCH_MODELS") {
|
|
9151
9336
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9152
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9153
|
-
|
|
9337
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginBottom: 1, children: [
|
|
9338
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", bold: true, children: "? " }),
|
|
9339
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Connecting to providers and fetching models..." })
|
|
9340
|
+
] }),
|
|
9341
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
9154
9342
|
state.fetchLog.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: line }, i)),
|
|
9155
|
-
state.fetchedModels.length === 0 && /* @__PURE__ */ jsxRuntime.
|
|
9156
|
-
/* @__PURE__ */ jsxRuntime.jsx(Spinner__default.default, { type: "dots" }),
|
|
9157
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: " Connecting to providers..." })
|
|
9158
|
-
] })
|
|
9343
|
+
state.fetchedModels.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(Spinner__default.default, { type: "dots" }) })
|
|
9159
9344
|
] })
|
|
9160
9345
|
] });
|
|
9161
9346
|
}
|
|
@@ -9170,41 +9355,55 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9170
9355
|
const tierLabel = (tier, hint) => {
|
|
9171
9356
|
const isFocused = state.tierSelectFocus === tier;
|
|
9172
9357
|
const current = tier === "T1" ? state.tierT1 : tier === "T2" ? state.tierT2 : state.tierT3;
|
|
9358
|
+
if (!isFocused) {
|
|
9359
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { children: [
|
|
9360
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "green", bold: true, children: "\u2714 " }),
|
|
9361
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { bold: true, children: [
|
|
9362
|
+
tier,
|
|
9363
|
+
" ",
|
|
9364
|
+
hint,
|
|
9365
|
+
": "
|
|
9366
|
+
] }),
|
|
9367
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", children: current === "auto" ? "Auto \u2014 let Cascade choose best available" : state.fetchedModels.find((m) => m.id === current)?.name || current })
|
|
9368
|
+
] }, tier);
|
|
9369
|
+
}
|
|
9173
9370
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
9174
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
current === "auto" ? "Auto" : current
|
|
9371
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { children: [
|
|
9372
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", bold: true, children: "? " }),
|
|
9373
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { bold: true, children: [
|
|
9374
|
+
tier,
|
|
9375
|
+
" ",
|
|
9376
|
+
hint,
|
|
9377
|
+
": "
|
|
9378
|
+
] })
|
|
9183
9379
|
] }),
|
|
9184
|
-
|
|
9380
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9185
9381
|
SelectInput__default.default,
|
|
9186
9382
|
{
|
|
9187
9383
|
items: modelOptions,
|
|
9188
|
-
onSelect: (item) => dispatch({ type: "SET_TIER", tier, value: item.value })
|
|
9384
|
+
onSelect: (item) => dispatch({ type: "SET_TIER", tier, value: item.value }),
|
|
9385
|
+
indicatorComponent: ({ isSelected }) => /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", children: isSelected ? "\u276F " : " " }),
|
|
9386
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: isSelected ? "magenta" : "white", children: label })
|
|
9189
9387
|
}
|
|
9190
|
-
)
|
|
9388
|
+
) })
|
|
9191
9389
|
] }, tier);
|
|
9192
9390
|
};
|
|
9193
9391
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9194
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9195
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Tab to switch tier \xB7 Enter to confirm selection" }),
|
|
9196
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, flexDirection: "column", children: [
|
|
9392
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
9197
9393
|
tierLabel("T1", "(Administrator \u2014 complex reasoning, runs once per task)"),
|
|
9198
9394
|
tierLabel("T2", "(Manager \u2014 runs per section)"),
|
|
9199
9395
|
tierLabel("T3", "(Worker \u2014 high volume, many parallel runs)")
|
|
9200
9396
|
] }),
|
|
9201
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "
|
|
9397
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "(Tab/Arrow Down to skip tier, Enter to select or save)" }) }),
|
|
9202
9398
|
state.error && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "red", children: state.error })
|
|
9203
9399
|
] });
|
|
9204
9400
|
}
|
|
9205
9401
|
if (state.step === "SAVE") {
|
|
9206
9402
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9207
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9403
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { children: [
|
|
9404
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "green", bold: true, children: "\u2714 " }),
|
|
9405
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Setup complete!" })
|
|
9406
|
+
] }),
|
|
9208
9407
|
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, children: [
|
|
9209
9408
|
/* @__PURE__ */ jsxRuntime.jsx(Spinner__default.default, { type: "dots" }),
|
|
9210
9409
|
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: " Writing .cascade/config.json..." })
|