mindweave 2.4.8 → 2.4.9
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 +3 -2
- package/dist/cli/App.js +57 -22
- package/dist/cli/App.js.map +1 -1
- package/dist/cli/commandArgs.js +60 -16
- package/dist/cli/commandArgs.js.map +1 -1
- package/dist/cli/components/Picker.js +61 -19
- package/dist/cli/components/Picker.js.map +1 -1
- package/dist/drivers/clientId.js +2 -3
- package/dist/drivers/clientId.js.map +1 -1
- package/dist/drivers/openaiCompat/wire.js +55 -3
- package/dist/drivers/openaiCompat/wire.js.map +1 -1
- package/dist/drivers/openrouter/catalog.js +136 -0
- package/dist/drivers/openrouter/catalog.js.map +1 -0
- package/dist/drivers/openrouter/client.js +102 -0
- package/dist/drivers/openrouter/client.js.map +1 -0
- package/dist/drivers/openrouter/endpoint.js +20 -0
- package/dist/drivers/openrouter/endpoint.js.map +1 -0
- package/dist/drivers/openrouter/index.js +5 -0
- package/dist/drivers/openrouter/index.js.map +1 -0
- package/dist/drivers/openrouter/manifest.js +83 -0
- package/dist/drivers/openrouter/manifest.js.map +1 -0
- package/dist/drivers/providerError.js +51 -0
- package/dist/drivers/providerError.js.map +1 -1
- package/dist/drivers/registry.js +111 -3
- package/dist/drivers/registry.js.map +1 -1
- package/dist/dynamo/model.js +63 -4
- package/dist/dynamo/model.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -70,8 +70,9 @@ else is configured inside a session.
|
|
|
70
70
|
|
|
71
71
|
Short version, one line each. The depth is in the linked pages.
|
|
72
72
|
|
|
73
|
-
- **
|
|
74
|
-
Mistral, Groq, Cerebras, Qwen, Kimi, GLM, Meta, MiniMax, Tencent
|
|
73
|
+
- **15 providers, 53 models, one key** — DeepSeek, Anthropic, OpenAI, Gemini, xAI,
|
|
74
|
+
Mistral, Groq, Cerebras, Qwen, Kimi, GLM, Meta, MiniMax, Tencent, plus every
|
|
75
|
+
tool-capable model on OpenRouter. Only the driver you
|
|
75
76
|
use is loaded. Switch with `/provider` and `/model`; remembered per project.
|
|
76
77
|
[PROVIDERS.md](src/drivers/PROVIDERS.md)
|
|
77
78
|
- **Real tools** — read and edit files, multi-file edits, ripgrep search, a shell with
|
package/dist/cli/App.js
CHANGED
|
@@ -46,10 +46,10 @@ import { TrustGate } from "./components/TrustGate.js";
|
|
|
46
46
|
import { rootBreadth, breadthWarning, trustPersists, isTrusted, rememberTrust } from "./trust.js";
|
|
47
47
|
import { projectDir } from "../memory/store.js";
|
|
48
48
|
import { parseUndoArg, undoNotice } from "../tools/checkpoints.js";
|
|
49
|
-
import { DEFAULT_MODEL_CONFIG, thinkLevels, thinkLabel, modelLabel, modelsOfProvider, providerOf, usableFallback, needsKeySetup, withModel, saveModelConfig, refreshModels } from "../dynamo/model.js";
|
|
49
|
+
import { DEFAULT_MODEL_CONFIG, thinkLevels, thinkLabel, modelLabel, modelsOfProvider, providerOf, usableFallback, needsKeySetup, withModel, saveModelConfig, refreshModels, DISCOVERY_TTL_MS } from "../dynamo/model.js";
|
|
50
50
|
import { allProviders, manifestForModel, modelsOf } from "../drivers/registry.js";
|
|
51
51
|
import { orderProviders, orderModels } from "./pickerOrder.js";
|
|
52
|
-
import { accessRefusal } from "../drivers/providerError.js";
|
|
52
|
+
import { accessRefusal, providerOutage } from "../drivers/providerError.js";
|
|
53
53
|
import { resolveAttachments, stripAttachments } from "./attachments.js";
|
|
54
54
|
import { collapsePastes, wrapPastedText } from "../memory/pastedText.js";
|
|
55
55
|
import { createDropHandles, expandHandles } from "./dropHandles.js";
|
|
@@ -89,7 +89,7 @@ import { perf, perfEnabled } from "./perfLog.js";
|
|
|
89
89
|
import { isGroupMember, groupSettled, planGroupReveal, planStandaloneReveal, resultQueued, STANDALONE_HOLD_MS } from "./groupReveal.js";
|
|
90
90
|
import { drain as drainQueue, popAll as popAllQueued, queueMessage, takeSteerable, visibleQueue } from "./messageQueue.js";
|
|
91
91
|
import { routeCommand, parseCommandLine, unknownCommandMessage } from "./commandRoute.js";
|
|
92
|
-
import { resolveChoice } from "./commandArgs.js";
|
|
92
|
+
import { resolveChoice, splitModelArg } from "./commandArgs.js";
|
|
93
93
|
import { carryAcrossFreshSession } from "./sessionCarry.js";
|
|
94
94
|
import { toolDisplay, isGroupable, KIND_COLOR } from "./toolDisplay.js";
|
|
95
95
|
import { workingVerb } from "./workingVerb.js";
|
|
@@ -141,6 +141,11 @@ function orderedProviderList() {
|
|
|
141
141
|
function orderedModelList(model) {
|
|
142
142
|
return orderModels(modelsOfProvider(model));
|
|
143
143
|
}
|
|
144
|
+
/** The same order, for a provider named by id rather than by one of its models. */
|
|
145
|
+
function orderedModelsOf(providerId) {
|
|
146
|
+
const provider = allProviders().find((p) => p.id === providerId);
|
|
147
|
+
return provider ? orderModels(modelsOf(provider)) : [];
|
|
148
|
+
}
|
|
144
149
|
// After you pick a session in /continue, the three ways to resume it.
|
|
145
150
|
const RESUME_MODES = [
|
|
146
151
|
{ label: "Compact & continue", description: "summarize the old chat first so it won't eat your context, then pick up where you left off" },
|
|
@@ -2013,7 +2018,10 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2013
2018
|
// the provider's own sentence quoted inside it. Anything else — a malformed
|
|
2014
2019
|
// request, a bug of ours — stays loud, which is the point of classifying
|
|
2015
2020
|
// narrowly. See drivers/providerError.ts.
|
|
2016
|
-
|
|
2021
|
+
// A provider falling over after the retries ran out gets the same calm treatment:
|
|
2022
|
+
// it is not our crash, and the useful news is "wait, or switch model".
|
|
2023
|
+
const refusal = accessRefusal(error, providerOf(s.modelConfig.model).label, otherProviderHasKey(s.modelConfig.model)) ??
|
|
2024
|
+
providerOutage(error, providerOf(s.modelConfig.model).label, modelLabel(s.modelConfig.model));
|
|
2017
2025
|
if (refusal)
|
|
2018
2026
|
enqueueReveal({ type: "notice", title: refusal.title, body: refusal.body });
|
|
2019
2027
|
else
|
|
@@ -2148,15 +2156,28 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2148
2156
|
}
|
|
2149
2157
|
// Apply a /model pick: switch model (clamping reasoning to a valid level), persist
|
|
2150
2158
|
// the choice for this project, and confirm.
|
|
2151
|
-
async function applyModel(index) {
|
|
2159
|
+
async function applyModel(index, providerId) {
|
|
2152
2160
|
const s = session.current;
|
|
2153
|
-
// Index into the SAME list the picker rendered —
|
|
2154
|
-
//
|
|
2155
|
-
//
|
|
2156
|
-
//
|
|
2157
|
-
const
|
|
2161
|
+
// Index into the SAME list the picker rendered — that provider's models in display
|
|
2162
|
+
// order, not every model everywhere. Indexing a differently-ordered list here would
|
|
2163
|
+
// silently select a different model than the one on screen, and would type-check
|
|
2164
|
+
// perfectly.
|
|
2165
|
+
const list = !s ? [] : providerId ? orderedModelsOf(providerId) : orderedModelList(s.modelConfig.model);
|
|
2166
|
+
const choice = list[index];
|
|
2158
2167
|
if (!s || !choice)
|
|
2159
2168
|
return;
|
|
2169
|
+
// A model on another provider is a provider switch, and gets the same key check.
|
|
2170
|
+
const target = manifestForModel(choice.id);
|
|
2171
|
+
if (target.id !== providerOf(s.modelConfig.model).id) {
|
|
2172
|
+
if (missingKeyFor(choice.id)) {
|
|
2173
|
+
pendingSwitch.current = { model: choice.id, apiKeyEnv: target.apiKeyEnv, label: target.label };
|
|
2174
|
+
note(`${target.label} has no key yet — add one and the switch will finish.`);
|
|
2175
|
+
setKeysOpen(true);
|
|
2176
|
+
return;
|
|
2177
|
+
}
|
|
2178
|
+
await switchTo(choice.id, target.label);
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2160
2181
|
s.modelConfig = withModel(s.modelConfig, choice.id);
|
|
2161
2182
|
await saveModelConfig(s.cwd, s.modelConfig);
|
|
2162
2183
|
note(`model → ${modelLabel(s.modelConfig.model)} · ${thinkLabel(s.modelConfig)}`);
|
|
@@ -2202,6 +2223,10 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2202
2223
|
s.modelConfig = withModel(s.modelConfig, model);
|
|
2203
2224
|
await saveModelConfig(s.cwd, s.modelConfig);
|
|
2204
2225
|
note(`provider → ${providerLabel} · model → ${modelLabel(s.modelConfig.model)} · ${thinkLabel(s.modelConfig)}`);
|
|
2226
|
+
// Where prompts go is worth a sentence at the moment someone moves their work there.
|
|
2227
|
+
const notice = manifestForModel(model).notice;
|
|
2228
|
+
if (notice)
|
|
2229
|
+
note(notice);
|
|
2205
2230
|
}
|
|
2206
2231
|
// Apply a /think pick for the current model: set thinking + effort, persist, confirm.
|
|
2207
2232
|
async function applyThink(index) {
|
|
@@ -2256,7 +2281,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2256
2281
|
else if (o.kind === "provider")
|
|
2257
2282
|
void applyProvider(index);
|
|
2258
2283
|
else if (o.kind === "model")
|
|
2259
|
-
void applyModel(index);
|
|
2284
|
+
void applyModel(index, o.providerId);
|
|
2260
2285
|
else if (o.kind === "think")
|
|
2261
2286
|
void applyThink(index);
|
|
2262
2287
|
else if (o.kind === "screen") {
|
|
@@ -2620,12 +2645,12 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2620
2645
|
setOverlay({ kind: "sessions", items: sessions });
|
|
2621
2646
|
return;
|
|
2622
2647
|
}
|
|
2623
|
-
// Both pickers refresh discovered providers
|
|
2624
|
-
//
|
|
2648
|
+
// Both pickers refresh discovered providers whose list has gone stale, so a newly
|
|
2649
|
+
// served model appears without a restart. Awaited rather than fired off: the
|
|
2625
2650
|
// picker renders from the list, and opening on a stale one that then changes
|
|
2626
2651
|
// under the cursor is the exact "two-stage reveal" the UI work removed.
|
|
2627
2652
|
if (name === "/provider") {
|
|
2628
|
-
await refreshModels();
|
|
2653
|
+
await refreshModels({ maxAgeMs: DISCOVERY_TTL_MS });
|
|
2629
2654
|
setOverlay({ kind: "provider" });
|
|
2630
2655
|
return;
|
|
2631
2656
|
}
|
|
@@ -2641,12 +2666,21 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2641
2666
|
// getting the picker anyway — which is what happened before, because the argument
|
|
2642
2667
|
// was dropped without a word — reads as the app not having heard you.
|
|
2643
2668
|
if (name === "/model") {
|
|
2644
|
-
await refreshModels();
|
|
2669
|
+
await refreshModels({ maxAgeMs: DISCOVERY_TTL_MS });
|
|
2645
2670
|
if (arg) {
|
|
2646
|
-
const
|
|
2671
|
+
const current = providerOf(s.modelConfig.model).id;
|
|
2672
|
+
const here = resolveChoice(arg, orderedModelList(s.modelConfig.model), "model");
|
|
2673
|
+
const { providerId, words } = splitModelArg(arg, allProviders(), current, here.kind !== "error");
|
|
2674
|
+
// `/model openrouter` alone: that provider's picker.
|
|
2675
|
+
if (!words)
|
|
2676
|
+
return setOverlay({ kind: "model", providerId });
|
|
2677
|
+
const picked = providerId === current ? here : resolveChoice(words, orderedModelsOf(providerId), "model");
|
|
2647
2678
|
if (picked.kind === "error")
|
|
2648
2679
|
return say(picked.message);
|
|
2649
|
-
|
|
2680
|
+
// Several fit: show exactly those, in the picker, rather than a list to retype from.
|
|
2681
|
+
if (picked.kind === "several")
|
|
2682
|
+
return setOverlay({ kind: "model", providerId, filter: words });
|
|
2683
|
+
await applyModel(picked.index, providerId);
|
|
2650
2684
|
return;
|
|
2651
2685
|
}
|
|
2652
2686
|
setOverlay({ kind: "model" });
|
|
@@ -2675,7 +2709,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2675
2709
|
if (name === "/think") {
|
|
2676
2710
|
if (arg) {
|
|
2677
2711
|
const picked = resolveChoice(arg, thinkLevels(s.modelConfig.model), "reasoning level");
|
|
2678
|
-
if (picked.kind
|
|
2712
|
+
if (picked.kind !== "match")
|
|
2679
2713
|
return say(picked.message);
|
|
2680
2714
|
await applyThink(picked.index);
|
|
2681
2715
|
return;
|
|
@@ -3102,9 +3136,10 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
3102
3136
|
}
|
|
3103
3137
|
if (overlay.kind === "model") {
|
|
3104
3138
|
const id = cur?.modelConfig.model ?? DEFAULT_MODEL_CONFIG.model;
|
|
3105
|
-
//
|
|
3106
|
-
//
|
|
3107
|
-
const models = orderedModelList(id);
|
|
3139
|
+
// One provider's models, in display order (default first, rest A→Z): the one in use,
|
|
3140
|
+
// unless `/model <provider> …` named another.
|
|
3141
|
+
const models = overlay.providerId ? orderedModelsOf(overlay.providerId) : orderedModelList(id);
|
|
3142
|
+
const pickerProvider = overlay.providerId ? allProviders().find((p) => p.id === overlay.providerId)?.label ?? "" : providerOf(id).label;
|
|
3108
3143
|
// The two facts that decide the choice and are nowhere else on the screen: how much
|
|
3109
3144
|
// it can hold, and whether it can see an image you attach. They lead the description
|
|
3110
3145
|
// because the row truncates from the RIGHT — put behind the prose they would be the
|
|
@@ -3118,7 +3153,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
3118
3153
|
description: m.description ? `${facts} · ${m.description}` : facts,
|
|
3119
3154
|
};
|
|
3120
3155
|
});
|
|
3121
|
-
return (_jsx(Picker, { title: `Choose a ${
|
|
3156
|
+
return (_jsx(Picker, { title: `Choose a ${pickerProvider} model`, items: items, width: width, maxRows: maxRows, initialIndex: Math.max(0, models.findIndex((m) => m.id === id)), initialFilter: overlay.filter, onSelect: onOverlaySelect, onCancel: onOverlayCancel }));
|
|
3122
3157
|
}
|
|
3123
3158
|
if (overlay.kind === "think") {
|
|
3124
3159
|
const model = cur?.modelConfig.model ?? DEFAULT_MODEL_CONFIG.model;
|