koneck 2.25.65 → 2.25.66
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/config-store.d.ts +8 -0
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +25 -4
- package/dist/config-store.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/endpoints.d.ts +29 -1
- package/dist/endpoints.d.ts.map +1 -1
- package/dist/endpoints.js +69 -1
- package/dist/endpoints.js.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/ink-chat.d.ts.map +1 -1
- package/dist/ink-chat.js +79 -23
- package/dist/ink-chat.js.map +1 -1
- package/dist/providers.d.ts +2 -0
- package/dist/providers.d.ts.map +1 -1
- package/dist/providers.js +62 -10
- package/dist/providers.js.map +1 -1
- package/package.json +1 -1
package/dist/ink-chat.js
CHANGED
|
@@ -7,8 +7,8 @@ import { estimateCost, formatCost } from './pricing.js';
|
|
|
7
7
|
import { generateSessionId, saveSession, listSessions, loadSession, relativeAge, renameSession, forkSession, archiveSession, findSession, } from './session.js';
|
|
8
8
|
import { loadMemory } from './memory.js';
|
|
9
9
|
import { loadKoneckConfig, saveKoneckConfig, setConfigKey, configKeyDescriptions, CONFIG_FILE, CONFIG_SCHEMA, stepValue, displayValue } from './config-store.js';
|
|
10
|
-
import { PROVIDERS, resolveProvider, getApiKey, addUserProvider, userProvidersPath } from './providers.js';
|
|
11
|
-
import { resolveEndpoint, normaliseEndpoint, withEndpoint, savedEndpoints, shortSource } from './endpoints.js';
|
|
10
|
+
import { PROVIDERS, resolveProvider, getApiKey, addUserProvider, userProvidersPath, needsNoCredential } from './providers.js';
|
|
11
|
+
import { resolveEndpoint, normaliseEndpoint, withEndpoint, savedEndpoints, shortSource, resolveModelChoice, withModel } from './endpoints.js';
|
|
12
12
|
import { loadProjectConfig, loadGlobalConfig } from './config.js';
|
|
13
13
|
import { listCheckpoints, revertCheckpoint, snapshotTree, filesTouchedBy } from './checkpoint.js';
|
|
14
14
|
import { fileDiff, diffSummary } from './diff-view.js';
|
|
@@ -1250,6 +1250,23 @@ function App({ config: initialConfig, clearFrame }) {
|
|
|
1250
1250
|
}, [cfg.cwd]);
|
|
1251
1251
|
/** Nearest first, which is the order resolveEndpoint reads them in. */
|
|
1252
1252
|
const endpointLayers = (store = stored) => [outerLayers.project, store, outerLayers.global];
|
|
1253
|
+
/**
|
|
1254
|
+
* The model in force for a provider, with where the choice came from.
|
|
1255
|
+
*
|
|
1256
|
+
* A model belongs to a provider: qwen3-coder:30b means nothing to Anthropic. Reported as
|
|
1257
|
+
* "koneck keeps reverting the model to codellama" — which is the Ollama definition's built-in
|
|
1258
|
+
* default, reached because nothing else was ever saved against ollama.
|
|
1259
|
+
*/
|
|
1260
|
+
function modelFor(provider, store) {
|
|
1261
|
+
const def = PROVIDERS[provider.trim().toLowerCase()];
|
|
1262
|
+
return resolveModelChoice(provider, endpointLayers(store), def?.defaultModel ?? 'auto');
|
|
1263
|
+
}
|
|
1264
|
+
/** Remembers a model against the provider it belongs to. */
|
|
1265
|
+
async function rememberModel(provider, model) {
|
|
1266
|
+
const saved = withModel(await loadKoneckConfig(), provider, model);
|
|
1267
|
+
await saveKoneckConfig(saved);
|
|
1268
|
+
setStored(saved);
|
|
1269
|
+
}
|
|
1253
1270
|
/** The endpoint in force for a provider, with where it came from. */
|
|
1254
1271
|
function endpointFor(provider, store) {
|
|
1255
1272
|
const def = PROVIDERS[provider.trim().toLowerCase()];
|
|
@@ -1654,10 +1671,25 @@ function App({ config: initialConfig, clearFrame }) {
|
|
|
1654
1671
|
/** The text an edit box opens on: what the row is showing. */
|
|
1655
1672
|
function settingText(field) {
|
|
1656
1673
|
const key = String(field.key);
|
|
1657
|
-
const value = key === 'baseURL' ||
|
|
1674
|
+
const value = key === 'baseURL' || key === 'model' || stored[key] === undefined
|
|
1675
|
+
? liveSetting(key) : stored[key];
|
|
1658
1676
|
return value === undefined || value === null ? '' : String(value);
|
|
1659
1677
|
}
|
|
1660
1678
|
async function applySetting(field, value) {
|
|
1679
|
+
// The model row is one provider's model, so it is saved against the provider in force. In the
|
|
1680
|
+
// single shared slot, choosing a model for Ollama also chose it for Anthropic.
|
|
1681
|
+
if (field.key === 'model') {
|
|
1682
|
+
const name = typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined;
|
|
1683
|
+
const saved = withModel(await loadKoneckConfig(), cfg.provider, name);
|
|
1684
|
+
await saveKoneckConfig(saved);
|
|
1685
|
+
setStored(saved);
|
|
1686
|
+
const now = modelFor(cfg.provider, saved);
|
|
1687
|
+
const merged = { ...cfg, model: now.model };
|
|
1688
|
+
setCfg(merged);
|
|
1689
|
+
await resetSession(merged);
|
|
1690
|
+
addSystem(`**${cfg.provider}** → ${now.model} (${now.source})`);
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1661
1693
|
// The endpoint row is not a plain setting: it is one provider's address, so it is saved
|
|
1662
1694
|
// against the provider in force rather than into a single slot every provider shares.
|
|
1663
1695
|
if (field.key === 'baseURL') {
|
|
@@ -1695,20 +1727,19 @@ function App({ config: initialConfig, clearFrame }) {
|
|
|
1695
1727
|
setConfigMaxWidth(typeof value === 'number' && value >= 40 ? value : undefined);
|
|
1696
1728
|
break;
|
|
1697
1729
|
case 'provider': {
|
|
1698
|
-
// Changing the provider here has to move the endpoint with it, or the new
|
|
1699
|
-
// addressed at the old one's URL
|
|
1730
|
+
// Changing the provider here has to move the endpoint and the model with it, or the new
|
|
1731
|
+
// provider is addressed at the old one's URL and asked for the old one's model — the same
|
|
1732
|
+
// fault /provider had, in a second place.
|
|
1700
1733
|
const name = typeof value === 'string' && value.trim() !== '' ? value.trim() : cfg.provider;
|
|
1701
|
-
const def = PROVIDERS[name.toLowerCase()];
|
|
1702
1734
|
const merged = {
|
|
1703
1735
|
...cfg, provider: name,
|
|
1704
|
-
model:
|
|
1736
|
+
model: resolveModelChoice(name, endpointLayers(next), PROVIDERS[name.toLowerCase()]?.defaultModel ?? 'auto').model,
|
|
1705
1737
|
baseURL: endpointFor(name, next).url,
|
|
1706
1738
|
};
|
|
1707
1739
|
setCfg(merged);
|
|
1708
1740
|
await resetSession(merged);
|
|
1709
1741
|
break;
|
|
1710
1742
|
}
|
|
1711
|
-
case 'model':
|
|
1712
1743
|
case 'maxTurns':
|
|
1713
1744
|
case 'requireApproval':
|
|
1714
1745
|
case 'agentConcurrency':
|
|
@@ -1799,12 +1830,16 @@ function App({ config: initialConfig, clearFrame }) {
|
|
|
1799
1830
|
async function fetchModels() {
|
|
1800
1831
|
const infos = await loadModelCatalog();
|
|
1801
1832
|
const statuses = readModelStatus();
|
|
1833
|
+
const selfHosted = needsNoCredential(resolveProvider(cfg.provider, endpointFor(cfg.provider).url));
|
|
1802
1834
|
return [...infos]
|
|
1803
1835
|
.sort((a, b) => a.id.localeCompare(b.id))
|
|
1804
1836
|
.map(m => {
|
|
1805
1837
|
const st = statuses[`${cfg.provider}:${m.id}`];
|
|
1806
1838
|
const parts = [
|
|
1807
|
-
|
|
1839
|
+
// "paid" is a claim about somebody's billing, and a model on your own hardware has no
|
|
1840
|
+
// billing. The catalogue's free flag describes a gateway's tiers; it means nothing for
|
|
1841
|
+
// a runtime you are running, and calling qwen3-coder on a LAN box "paid" is just wrong.
|
|
1842
|
+
selfHosted ? 'local' : m.free ? 'free' : 'paid',
|
|
1808
1843
|
...(m.contextLength ? [`${humanTokens(m.contextLength)} ctx`] : []),
|
|
1809
1844
|
...(m.toolCalling === false ? ['no tools'] : []),
|
|
1810
1845
|
...(st && st.state !== 'ok' ? [stateLabel(st.state)] : st ? ['ready'] : []),
|
|
@@ -1904,7 +1939,10 @@ function App({ config: initialConfig, clearFrame }) {
|
|
|
1904
1939
|
if (kind === 'model') {
|
|
1905
1940
|
const next = { ...cfg, model: item.value };
|
|
1906
1941
|
await resetSession(next);
|
|
1907
|
-
|
|
1942
|
+
// Saved against the provider. Choosing a model used to change the running session and
|
|
1943
|
+
// nothing else, so it lasted until the next provider switch or the next start.
|
|
1944
|
+
await rememberModel(cfg.provider, item.value);
|
|
1945
|
+
addSystem(`Model → ${item.value} · remembered for ${cfg.provider} (session reset)`);
|
|
1908
1946
|
return;
|
|
1909
1947
|
}
|
|
1910
1948
|
if (kind === 'provider') {
|
|
@@ -1914,9 +1952,12 @@ function App({ config: initialConfig, clearFrame }) {
|
|
|
1914
1952
|
// not loopback and does need a key, whatever the provider is called.
|
|
1915
1953
|
const endpoint = endpointFor(item.value);
|
|
1916
1954
|
const resolved = resolveProvider(item.value, endpoint.url);
|
|
1955
|
+
// Whichever model was last used with this provider, not its built-in default. The old line
|
|
1956
|
+
// reached for PROVIDERS[name].defaultModel — `codellama` for Ollama — and wrote it to disk.
|
|
1957
|
+
const chosen = modelFor(item.value);
|
|
1917
1958
|
const next = {
|
|
1918
1959
|
...cfg, provider: item.value,
|
|
1919
|
-
model:
|
|
1960
|
+
model: chosen.model,
|
|
1920
1961
|
baseURL: resolved.baseURL,
|
|
1921
1962
|
apiKey: undefined, // a key for the previous provider must not carry over
|
|
1922
1963
|
};
|
|
@@ -1931,10 +1972,13 @@ function App({ config: initialConfig, clearFrame }) {
|
|
|
1931
1972
|
}
|
|
1932
1973
|
if (usable) {
|
|
1933
1974
|
await resetSession(next);
|
|
1934
|
-
|
|
1975
|
+
// The provider is persisted; the model is not, because it is already stored against the
|
|
1976
|
+
// provider it belongs to. Writing it into the shared `model` key is what made a switch to
|
|
1977
|
+
// ollama leave `codellama` on disk for everything else to inherit.
|
|
1978
|
+
const savedCfg = { ...(await loadKoneckConfig()), provider: item.value };
|
|
1935
1979
|
await saveKoneckConfig(savedCfg);
|
|
1936
1980
|
setStored(savedCfg);
|
|
1937
|
-
addSystem(`Provider → ${item.value} · model → ${next.model}\n` +
|
|
1981
|
+
addSystem(`Provider → ${item.value} · model → ${next.model} (${chosen.source})\n` +
|
|
1938
1982
|
`Endpoint ${resolved.baseURL} (${endpoint.source}) · session reset`);
|
|
1939
1983
|
return;
|
|
1940
1984
|
}
|
|
@@ -2012,12 +2056,17 @@ function App({ config: initialConfig, clearFrame }) {
|
|
|
2012
2056
|
}
|
|
2013
2057
|
case '/model': {
|
|
2014
2058
|
if (!arg) {
|
|
2015
|
-
|
|
2059
|
+
const now = modelFor(cfg.provider);
|
|
2060
|
+
addSystem(`Current model: ${cfg.model} (${now.source})\n` +
|
|
2061
|
+
`Usage: /model <name> — remembered for ${cfg.provider}`);
|
|
2016
2062
|
return;
|
|
2017
2063
|
}
|
|
2018
2064
|
const newCfg = { ...cfg, model: arg };
|
|
2019
2065
|
await resetSession(newCfg);
|
|
2020
|
-
|
|
2066
|
+
// Persisted against the provider. It used to change the running session only, so the
|
|
2067
|
+
// next start or the next provider switch put the old model back.
|
|
2068
|
+
await rememberModel(cfg.provider, arg);
|
|
2069
|
+
addSystem(`Model → ${arg} · remembered for ${cfg.provider} (session reset)`);
|
|
2021
2070
|
return;
|
|
2022
2071
|
}
|
|
2023
2072
|
case '/provider': {
|
|
@@ -2039,15 +2088,19 @@ function App({ config: initialConfig, clearFrame }) {
|
|
|
2039
2088
|
// address with PROVIDERS[name].baseURL, which is how a configured Ollama kept coming back
|
|
2040
2089
|
// as localhost.
|
|
2041
2090
|
const endpoint = endpointFor(name);
|
|
2042
|
-
|
|
2091
|
+
// Whichever model was last used with this provider. Forcing def.defaultModel here — and
|
|
2092
|
+
// then writing it to disk — is what "keeps reverting the model to codellama" was.
|
|
2093
|
+
const chosen = modelFor(name);
|
|
2094
|
+
const newCfg = { ...cfg, provider: name, model: chosen.model, baseURL: endpoint.url };
|
|
2043
2095
|
await resetSession(newCfg);
|
|
2044
|
-
//
|
|
2045
|
-
|
|
2096
|
+
// The provider is persisted, or the switch is undone by the next start. The model is not:
|
|
2097
|
+
// it already lives against the provider it belongs to.
|
|
2098
|
+
const savedCfg = { ...(await loadKoneckConfig()), provider: name };
|
|
2046
2099
|
await saveKoneckConfig(savedCfg);
|
|
2047
2100
|
setStored(savedCfg);
|
|
2048
|
-
addSystem(`Provider → ${name} model → ${
|
|
2101
|
+
addSystem(`Provider → ${name} model → ${chosen.model} (${chosen.source})\n` +
|
|
2049
2102
|
`Endpoint ${endpoint.url} (${endpoint.source})\n` +
|
|
2050
|
-
`Session reset. /endpoint <url> changes the address.`);
|
|
2103
|
+
`Session reset. /endpoint <url> changes the address, /model <name> the model.`);
|
|
2051
2104
|
return;
|
|
2052
2105
|
}
|
|
2053
2106
|
case '/redraw': {
|
|
@@ -3550,7 +3603,8 @@ function App({ config: initialConfig, clearFrame }) {
|
|
|
3550
3603
|
// An endpoint always has a source worth naming, so it never reads as unset or as
|
|
3551
3604
|
// merely inherited: the question it answers is which file won.
|
|
3552
3605
|
const isEndpoint = f.key === 'baseURL';
|
|
3553
|
-
const
|
|
3606
|
+
const isModel = f.key === 'model';
|
|
3607
|
+
const here = !isEndpoint && !isModel && stored[f.key] !== undefined;
|
|
3554
3608
|
const value = here ? stored[f.key] : liveSetting(String(f.key));
|
|
3555
3609
|
const raw = f.key === 'maxTurns' && (value === 0 || value === undefined)
|
|
3556
3610
|
? 'no limit'
|
|
@@ -3558,7 +3612,8 @@ function App({ config: initialConfig, clearFrame }) {
|
|
|
3558
3612
|
const shown = beingTyped
|
|
3559
3613
|
? `${editing.value}_`
|
|
3560
3614
|
: isEndpoint ? `${raw} (${shortSource(endpoint.source)})`
|
|
3561
|
-
:
|
|
3615
|
+
: isModel ? `${raw} (${shortSource(modelFor(cfg.provider).source)})`
|
|
3616
|
+
: raw + (here || value === undefined ? '' : ' (inherited)');
|
|
3562
3617
|
// The marker is what makes the selection legible without colour, which a panel line
|
|
3563
3618
|
// drawn as one string cannot carry per-segment.
|
|
3564
3619
|
const mark = selected ? '>' : ' ';
|
|
@@ -3643,7 +3698,8 @@ function App({ config: initialConfig, clearFrame }) {
|
|
|
3643
3698
|
const st = statuses[`${cfg.provider}:${cfg.model}`];
|
|
3644
3699
|
lines.push(`**In use** ${cfg.model}`);
|
|
3645
3700
|
if (current) {
|
|
3646
|
-
|
|
3701
|
+
const hosted = needsNoCredential(resolveProvider(cfg.provider, endpointFor(cfg.provider).url));
|
|
3702
|
+
lines.push(` ${hosted ? 'local' : current.free ? 'free' : 'paid'}` +
|
|
3647
3703
|
(current.contextLength ? ` · ${humanTokens(current.contextLength)} context` : '') +
|
|
3648
3704
|
(current.maxOutput ? ` · ${humanTokens(current.maxOutput)} max out` : '') +
|
|
3649
3705
|
(current.toolCalling === false ? ' · no tool calling' : ''));
|